1
0
Fork 0

Initial commit

master
Ambrose Chua 2019-08-04 03:10:19 +08:00
commit 657143849b
Signed by: ambrose
GPG Key ID: B34FBE029276BA5D
4 changed files with 2159 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target
**/*.rs.bk

1989
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

16
Cargo.toml Normal file
View File

@ -0,0 +1,16 @@
[package]
name = "paste"
version = "0.1.0"
authors = ["Ambrose Chua <ambrose@chua.family>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
futures = "0.1"
rocket = "0.4"
cuid = "0.1"
rusoto_s3 = "0.40"
rusoto_core = "0.40"
rusoto_credential = "0.40"

152
src/main.rs Normal file
View File

@ -0,0 +1,152 @@
#![feature(proc_macro_hygiene, decl_macro)]
#[macro_use] extern crate rocket;
use rocket::State;
use rocket::response::content;
use core::time::Duration;
use futures::future::Future;
use cuid::slug;
use rusoto_core::region::Region;
use rusoto_credential::AwsCredentials;
use rusoto_credential::EnvironmentProvider;
use rusoto_credential::ProvideAwsCredentials;
use rusoto_s3::PutObjectRequest;
use rusoto_s3::util::PreSignedRequestOption;
use rusoto_s3::util::PreSignedRequest;
#[get("/")]
fn index() -> content::Html<&'static str> {
content::Html("
<!DOCTYPE html>
<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Cousine&display=swap\">
<style>
body {
font-family: -apple-system, BlinkMacSystemFont,\"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";
}
.toolbar {
padding: 0.25em;
text-align: right;
}
input[type=submit], textarea {
border: none;
font: inherit;
resize: none;
}
input[type=submit] {
background: #8f8;
padding: 0.25em 0.5em;
}
textarea {
display: block;
width: 100%;
font-family: \"Cousine\", monospace;
}
</style>
<form method=\"get\">
<div class=\"toolbar\">
<input type=\"submit\" value=\"Create new paste\">
</div>
<textarea placeholder=\"Paste content here\" rows=\"10\" required></textarea>
</form>
<script src=\"https://cdnjs.cloudflare.com/ajax/libs/autosize.js/4.0.2/autosize.min.js\"></script>
<script>
autosize(document.querySelectorAll('textarea'));
const form = document.querySelector('form');
const textarea = form.querySelector('textarea');
const submit = form.querySelector('input[type=submit]');
textarea.focus();
form.addEventListener('submit', e => {
e.preventDefault();
submit.value = 'Creating paste...';
submit.disabled = true;
textarea.disabled = true;
fetch('/upload', {
method: 'POST',
})
.then(response => response.text())
.then(url => Promise.all([
url,
fetch(url, {
method: 'PUT',
body: textarea.value,
}),
]))
.then(res => {
const url = new URL(res[0]);
textarea.value = '';
window.location = url.origin + url.pathname;
});
});
</script>
")
}
#[post("/upload")]
fn upload(api: State<Api>) -> String {
api.presigned_put_object()
}
fn main() {
rocket::ignite().manage(Api::new()).mount("/", routes![index, upload]).launch();
}
struct Api {
region: Region,
credentials: AwsCredentials,
bucket: String,
expires_in: Duration,
}
impl Api {
pub fn new() -> Self {
let credentials = EnvironmentProvider::default().credentials().wait().unwrap();
let region = Region::Custom {
name: Self::read_region(),
endpoint: Self::read_region_endpoint(),
};
let bucket = "paste".to_string();
let expires_in = Duration::new(1 * 60 * 60, 0);
Self {
region,
credentials,
bucket,
expires_in,
}
}
pub fn presigned_put_object(&self) -> String {
let mut put_object_request = PutObjectRequest::default();
put_object_request.bucket = self.bucket.clone();
put_object_request.key = self.generate_id();
let option = PreSignedRequestOption {
expires_in: self.expires_in,
};
put_object_request.get_presigned_url(&self.region, &self.credentials, &option)
}
fn generate_id(&self) -> String {
slug().unwrap()
}
fn read_region() -> String {
std::env::var("AWS_DEFAULT_REGION").or_else(|_| std::env::var("AWS_REGION")).map_err(|_| RegionError::new("No AWS_DEFAULT_REGION in environment")).unwrap()
}
fn read_region_endpoint() -> String {
std::env::var("AWS_BASE_URL").map_err(|_| RegionError::new("No AWS_BASE_URL in environment")).unwrap()
}
}
#[derive(Debug)]
struct RegionError {
pub message: String,
}
impl RegionError {
pub fn new<S>(message: S) -> Self where S: ToString {
RegionError {
message: message.to_string(),
}
}
}