diff --git a/file-service/src/html.rs b/file-service/src/html.rs index b11881b..4539696 100644 --- a/file-service/src/html.rs +++ b/file-service/src/html.rs @@ -37,7 +37,7 @@ impl Form { impl Html for Form { fn to_html_string(&self) -> String { let encoding = match self.encoding { - Some(ref encoding) => format!("encoding=\"{encoding}\"", encoding = encoding), + Some(ref encoding) => format!("enctype=\"{encoding}\"", encoding = encoding), None => format!(""), }; format!( @@ -141,6 +141,7 @@ impl Html for Label { #[derive(Clone, Debug)] pub struct Button { + ty: Option, name: Option, label: String, } @@ -148,20 +149,30 @@ pub struct Button { impl Button { pub fn new(label: &str) -> Self { Self { + ty: None, name: None, label: label.to_owned(), } } + + pub fn with_type(mut self, ty: &str) -> Self { + self.ty = Some(ty.to_owned()); + self + } } impl Html for Button { fn to_html_string(&self) -> String { + let ty = match self.ty { + Some(ref ty) => format!("type={}", ty), + None => "".to_owned(), + }; let name = match self.name { Some(ref name) => format!("name={}", name), None => "".to_owned(), }; format!( - "", + "", name = name, label = self.label ) diff --git a/file-service/src/main.rs b/file-service/src/main.rs index b43dac8..2a2a000 100644 --- a/file-service/src/main.rs +++ b/file-service/src/main.rs @@ -8,6 +8,7 @@ use build_html::Html; use bytes::Buf; use futures_util::StreamExt; use std::{ + collections::HashMap, io::Read, net::{IpAddr, Ipv4Addr, SocketAddr}, path::PathBuf, @@ -218,19 +219,23 @@ fn script(_: &mut Request) -> IronResult { } */ -fn serve_file( - file: FileHandle, +fn serve_file( + info: FileInfo, + file: F, old_etags: Option, -) -> http::Result>> { +) -> http::Result>> +where + F: FnOnce() -> Result, ReadFileError>, +{ match old_etags { - Some(old_etags) if old_etags != file.info.hash => warp::http::Response::builder() - .header("content-type", file.info.file_type) + Some(old_etags) if old_etags != info.hash => warp::http::Response::builder() + .header("content-type", info.file_type) .status(StatusCode::NOT_MODIFIED) .body(vec![]), - _ => match file.content() { + _ => match file() { Ok(content) => warp::http::Response::builder() - .header("content-type", file.info.file_type) - .header("etag", file.info.hash) + .header("content-type", info.file_type) + .header("etag", info.hash) .status(StatusCode::OK) .body(content), Err(_) => warp::http::Response::builder() @@ -240,7 +245,9 @@ fn serve_file( } } -async fn collect_content(mut part: Part) -> Result<(Option, Vec), String> { +async fn collect_content( + mut part: Part, +) -> Result<(Option, Option, Vec), String> { let mut content: Vec = Vec::new(); while let Some(Ok(data)) = part.data().await { @@ -248,13 +255,17 @@ async fn collect_content(mut part: Part) -> Result<(Option, Vec), St reader.read_to_end(&mut content).unwrap(); } - Ok((part.filename().map(|s| s.to_owned()), content)) + Ok(( + part.content_type().map(|s| s.to_owned()), + part.filename().map(|s| s.to_owned()), + content, + )) } async fn collect_multipart( mut stream: warp::filters::multipart::FormData, -) -> Result, Vec)>, warp::Error> { - let mut content: Vec<(Option, Vec)> = Vec::new(); +) -> Result, Option, Vec)>, warp::Error> { + let mut content: Vec<(Option, Option, Vec)> = Vec::new(); while let Some(part) = stream.next().await { match part { @@ -266,6 +277,40 @@ async fn collect_multipart( Ok(content) } +async fn handle_upload( + form: warp::filters::multipart::FormData, + app: Arc>, +) -> warp::http::Result> { + let files = collect_multipart(form).await; + match files { + Ok(files) => { + for (_, filename, content) in files { + match filename { + Some(filename) => { + app.write().unwrap().add_file(filename, content).unwrap(); + } + None => { + return warp::http::Response::builder() + .status(StatusCode::BAD_REQUEST) + .body("".to_owned()) + } + } + } + } + Err(_err) => { + return warp::http::Response::builder() + .status(StatusCode::BAD_REQUEST) + .body("".to_owned()) + } + } + + // println!("file length: {:?}", files.map(|f| f.len())); + warp::http::Response::builder() + .header("location", "/") + .status(StatusCode::SEE_OTHER) + .body("".to_owned()) +} + #[tokio::main] pub async fn main() { /* @@ -315,11 +360,15 @@ pub async fn main() { &std::env::var("FILE_SHARE_DIR").unwrap(), )))); + let app_filter = { + let app = app.clone(); + warp::any().map(move || app.clone()) + }; + let log = warp::log("file_service"); - let root = warp::path!().and(warp::get()).map({ - let app = app.clone(); - move || { + let root = warp::path!().and(warp::get()).and(app_filter.clone()).map({ + move |app: Arc>| { info!("root handler"); let app = app.read().unwrap(); match app.list_files() { @@ -341,8 +390,18 @@ pub async fn main() { } }); + let post_upload_handler = warp::path!("upload") + .and(warp::post()) + .and(warp::filters::multipart::form().max_length(1024 * 1024 * 32)) + .and(app_filter.clone()) + .then( + |form: warp::filters::multipart::FormData, app: Arc>| { + handle_upload(form, app) + }, + ); + /* - let post_handler = warp::path!(String) + let post_delete_handler = warp::path!(String) .and(warp::post()) .and(warp::filters::body::form()) .map(|id: String, form: HashMap| { @@ -365,7 +424,7 @@ pub async fn main() { .unwrap() .get_file(&FileId::from(id)) { - Ok(file) => serve_file(file, old_etags), + Ok(file) => serve_file(file.info.clone(), || file.thumbnail(), old_etags), Err(_err) => warp::http::Response::builder() .status(StatusCode::NOT_FOUND) .body(vec![]), @@ -382,46 +441,19 @@ pub async fn main() { .unwrap() .get_file(&FileId::from(id)) { - Ok(file) => serve_file(file, old_etags), + Ok(file) => serve_file(file.info.clone(), || file.content(), old_etags), Err(_err) => warp::http::Response::builder() .status(StatusCode::NOT_FOUND) .body(vec![]), } }); - let upload = warp::path!() - .and(warp::post()) - .and(warp::filters::multipart::form().max_length(1024 * 1024 * 32)) - .then(|form: warp::filters::multipart::FormData| async move { - let files = collect_multipart(form).await; - /* - for (filename, content) in files { - app.write() - .unwrap() - .add_file(Some(filename), content) - .unwrap(); - } - */ - println!("file length: {:?}", files.map(|f| f.len())); - warp::reply() - }); - - let delete = warp::path!(String).and(warp::delete()).map(|id: String| { - println!("delete {}", id); - warp::reply() - }); - - /* let server = warp::serve( - root.or(post_handler) - .or(file) + root.or(post_upload_handler) .or(thumbnail) - .or(upload) - .or(delete) + .or(file) .with(log), ); - */ - let server = warp::serve(root.or(upload).or(thumbnail).or(file).or(delete).with(log)); server .run(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 8002)) .await; diff --git a/file-service/src/pages.rs b/file-service/src/pages.rs index c69b075..3dfbc5e 100644 --- a/file-service/src/pages.rs +++ b/file-service/src/pages.rs @@ -10,14 +10,15 @@ pub fn index(handles: Vec>) -> build_html::Htm .with_header(1, "Admin list of files") .with_html( Form::new() + .with_path("/upload") .with_method("post") .with_encoding("multipart/form-data") .with_container( Container::new(ContainerType::Div) - .with_html(Input::new("file", "file")) + .with_html(Input::new("file", "file").with_id("for-selector-input")) .with_html(Label::new("for-selector-input", "Select a file")), ) - .with_html(Button::new("Upload file")), + .with_html(Button::new("Upload file").with_type("submit")), ); for handle in handles {