mirror of
https://codeberg.org/icewind/shelve.git
synced 2026-06-03 20:14:08 +02:00
frontend
This commit is contained in:
parent
317d2c2020
commit
91a9a6b486
7 changed files with 1118 additions and 28 deletions
|
|
@ -42,7 +42,7 @@ impl UploadId {
|
|||
// have 7 consecutive bytes of "free" space, so we discard 1 bytes
|
||||
// from the expire time
|
||||
// we xor the expire with the rng, to make the id look "nicer"
|
||||
let expire_masked = expire ^ dbg!(u64::from_le_bytes(uuid_bytes[0..8].try_into().unwrap()));
|
||||
let expire_masked = expire ^ u64::from_le_bytes(uuid_bytes[0..8].try_into().unwrap());
|
||||
uuid_bytes[9..].copy_from_slice(&expire_masked.to_le_bytes()[0..7]);
|
||||
|
||||
UploadId(Uuid::from_bytes(uuid_bytes))
|
||||
|
|
|
|||
131
src/main.rs
131
src/main.rs
|
|
@ -3,16 +3,21 @@
|
|||
use crate::expire_queue::{ExpireQueue, InvalidUploadIdError, UploadId};
|
||||
use crate::token::{UploadToken, ValidTokens};
|
||||
use dotenv::dotenv;
|
||||
use rocket::http::RawStr;
|
||||
use rocket::http::{ContentType, RawStr};
|
||||
use rocket::request::FromParam;
|
||||
use rocket::response::NamedFile;
|
||||
use rocket::response::{NamedFile, Redirect, Responder};
|
||||
use rocket::*;
|
||||
use rocket_upload::MultipartDatas;
|
||||
use rust_embed::RustEmbed;
|
||||
use serde::Serialize;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::env::current_dir;
|
||||
use std::fs::{create_dir, read_dir, remove_dir_all};
|
||||
use std::env::{self, current_dir};
|
||||
use std::fs::{create_dir, read_dir, remove_dir_all, File};
|
||||
use std::io;
|
||||
use std::io::Cursor;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use std::thread::{sleep, spawn, JoinHandle};
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
|
||||
|
|
@ -29,9 +34,30 @@ impl<'r> FromParam<'r> for UploadId {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(RustEmbed)]
|
||||
#[folder = "templates/"]
|
||||
struct Templates;
|
||||
|
||||
struct HtmlResponse(Cow<'static, [u8]>);
|
||||
|
||||
impl<'r> Responder<'r> for HtmlResponse {
|
||||
fn respond_to(self, _: &Request) -> response::Result<'r> {
|
||||
match self.0 {
|
||||
Cow::Borrowed(s) => Response::build()
|
||||
.header(ContentType::HTML)
|
||||
.sized_body(Cursor::new(s))
|
||||
.ok(),
|
||||
Cow::Owned(s) => Response::build()
|
||||
.header(ContentType::HTML)
|
||||
.sized_body(Cursor::new(s))
|
||||
.ok(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/")]
|
||||
fn home() -> &'static str {
|
||||
"Home"
|
||||
fn home() -> HtmlResponse {
|
||||
HtmlResponse(Templates::get("index.html").unwrap_or(Cow::Borrowed(b"Template not found")))
|
||||
}
|
||||
|
||||
fn now() -> u64 {
|
||||
|
|
@ -64,6 +90,95 @@ fn upload(
|
|||
Ok(id.as_string())
|
||||
}
|
||||
|
||||
#[derive(Debug, Responder)]
|
||||
enum UploadResponse {
|
||||
Data(String),
|
||||
Redirect(Redirect),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct UploadData {
|
||||
success: bool,
|
||||
error: Option<&'static str>,
|
||||
urls: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[post("/upload", data = "<data>")]
|
||||
fn post_upload(
|
||||
data: MultipartDatas,
|
||||
accepted_tokens: State<ValidTokens>,
|
||||
basedir: State<PathBuf>,
|
||||
expire_queue: State<ExpireQueue>,
|
||||
) -> UploadResponse {
|
||||
let mut fields: HashMap<String, String> = data
|
||||
.texts
|
||||
.into_iter()
|
||||
.map(|text| (text.key, text.value))
|
||||
.collect();
|
||||
let expire = fields
|
||||
.get("expire")
|
||||
.and_then(|expire| u64::from_str(expire).ok());
|
||||
let ajax = fields.get("ajax").is_some();
|
||||
let token = fields.remove("token").unwrap_or_default();
|
||||
|
||||
if !accepted_tokens.contains(&token) {
|
||||
if ajax {
|
||||
UploadResponse::Data(
|
||||
serde_json::to_string(&UploadData {
|
||||
success: false,
|
||||
error: Some("invalid token"),
|
||||
urls: None,
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
} else {
|
||||
UploadResponse::Redirect(Redirect::to("/?error=invalid%20token"))
|
||||
}
|
||||
} else {
|
||||
match data
|
||||
.files
|
||||
.into_iter()
|
||||
.map(|file| {
|
||||
let id = UploadId::generate(now() + expire.unwrap_or(THOUSAND_YEARS));
|
||||
expire_queue.push(id);
|
||||
let name = &file.filename;
|
||||
|
||||
let mut path: PathBuf = basedir.join(id.as_string());
|
||||
create_dir(&path)?;
|
||||
path.push(name);
|
||||
|
||||
let mut file = File::open(&file.path)?;
|
||||
io::copy(&mut file, &mut File::create(&path)?)?;
|
||||
Ok(format!("{}/{}", id.as_string(), &name))
|
||||
})
|
||||
.collect::<io::Result<Vec<String>>>()
|
||||
{
|
||||
Ok(urls) => {
|
||||
if ajax {
|
||||
UploadResponse::Data(
|
||||
serde_json::to_string(&UploadData {
|
||||
success: true,
|
||||
error: None,
|
||||
urls: Some(urls),
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
} else {
|
||||
UploadResponse::Redirect(Redirect::to(""))
|
||||
}
|
||||
}
|
||||
Err(_) => UploadResponse::Data(
|
||||
serde_json::to_string(&UploadData {
|
||||
success: false,
|
||||
error: Some("error while moving file"),
|
||||
urls: None,
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/<id>/<name..>")]
|
||||
fn download(id: UploadId, name: PathBuf, basedir: State<PathBuf>) -> Option<NamedFile> {
|
||||
if id.is_expired(now()) {
|
||||
|
|
@ -97,7 +212,7 @@ fn main() {
|
|||
.manage(tokens)
|
||||
.manage(basedir.clone())
|
||||
.manage(expire_queue)
|
||||
.mount("/", routes![home, upload, download])
|
||||
.mount("/", routes![home, upload, post_upload, download])
|
||||
.launch();
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue