diff options
author | boastful-squirrel <boastful.squirrel@gmail.com> | 2019-04-27 10:12:42 +0000 |
---|---|---|
committer | boastful-squirrel <boastful.squirrel@gmail.com> | 2019-04-27 10:12:42 +0000 |
commit | fd3392636ce013cf8193a0881fa08680a8239698 (patch) | |
tree | 463ef6ebfbf7f338bd689a2e24b107294c7cee5d /src | |
parent | Made ColorScheme, SortingMethod and SortingOrder enums derive Copy (diff) | |
download | miniserve-fd3392636ce013cf8193a0881fa08680a8239698.tar.gz miniserve-fd3392636ce013cf8193a0881fa08680a8239698.zip |
Themed errors
Diffstat (limited to 'src')
-rw-r--r-- | src/file_upload.rs | 108 | ||||
-rw-r--r-- | src/listing.rs | 28 | ||||
-rw-r--r-- | src/main.rs | 6 | ||||
-rw-r--r-- | src/renderer.rs | 120 | ||||
-rw-r--r-- | src/themes.rs | 5 |
5 files changed, 222 insertions, 45 deletions
diff --git a/src/file_upload.rs b/src/file_upload.rs index f444396..88a4649 100644 --- a/src/file_upload.rs +++ b/src/file_upload.rs @@ -10,8 +10,9 @@ use std::{ }; use crate::errors::{self, ContextualError, ContextualErrorKind}; -use crate::listing::QueryParameters; +use crate::listing::{QueryParameters, SortingMethod, SortingOrder}; use crate::renderer; +use crate::themes::ColorScheme; /// Create future to save file. fn save_file( @@ -29,7 +30,7 @@ fn save_file( Ok(file) => file, Err(e) => { return Box::new(future::err(ContextualErrorKind::IOError( - format!("Failed to create file in {}", file_path.display()), + format!("Failed to create file {}", file_path.display()), e, ))); } @@ -115,43 +116,77 @@ fn handle_multipart( /// server root directory. Any path which will go outside of this directory is considered /// invalid. /// This method returns future. -pub fn upload_file(req: &HttpRequest<crate::MiniserveConfig>) -> FutureResponse<HttpResponse> { +pub fn upload_file( + req: &HttpRequest<crate::MiniserveConfig>, + default_color_scheme: ColorScheme, +) -> FutureResponse<HttpResponse> { let return_path = if let Some(header) = req.headers().get(header::REFERER) { header.to_str().unwrap_or("/").to_owned() } else { "/".to_string() }; - let app_root_dir = match req.state().path.canonicalize() { - Ok(dir) => dir, - Err(e) => { - let err = ContextualError::new(ContextualErrorKind::IOError( - "Failed to resolve path served by miniserve".to_string(), - e, - )); - return Box::new(create_error_response(&err.to_string(), &return_path)); - } - }; - - let path = match Query::<QueryParameters>::extract(req) { + let (path, sort_method, sort_order, color_scheme) = match Query::<QueryParameters>::extract(req) + { Ok(query) => { + let sort_param = query.sort; + let order_param = query.order; + let theme_param = query.theme.unwrap_or(default_color_scheme); + if let Some(path) = query.path.clone() { if let Ok(stripped_path) = path.strip_prefix(Component::RootDir) { - stripped_path.to_owned() + ( + stripped_path.to_owned(), + sort_param, + order_param, + theme_param, + ) } else { - path.clone() + (path.clone(), sort_param, order_param, theme_param) } } else { let err = ContextualError::new(ContextualErrorKind::InvalidHTTPRequestError( "Missing query parameter 'path'".to_string(), )); - return Box::new(create_error_response(&err.to_string(), &return_path)); + return Box::new(create_error_response( + &err.to_string(), + &return_path, + sort_param, + order_param, + theme_param, + default_color_scheme, + )); } } Err(e) => { let err = ContextualError::new(ContextualErrorKind::InvalidHTTPRequestError(e.to_string())); - return Box::new(create_error_response(&err.to_string(), &return_path)); + return Box::new(create_error_response( + &err.to_string(), + &return_path, + None, + None, + default_color_scheme, + default_color_scheme, + )); + } + }; + + let app_root_dir = match req.state().path.canonicalize() { + Ok(dir) => dir, + Err(e) => { + let err = ContextualError::new(ContextualErrorKind::IOError( + "Failed to resolve path served by miniserve".to_string(), + e, + )); + return Box::new(create_error_response( + &err.to_string(), + &return_path, + sort_method, + sort_order, + color_scheme, + default_color_scheme, + )); } }; @@ -162,7 +197,14 @@ pub fn upload_file(req: &HttpRequest<crate::MiniserveConfig>) -> FutureResponse< let err = ContextualError::new(ContextualErrorKind::InvalidHTTPRequestError( "Invalid value for 'path' parameter".to_string(), )); - return Box::new(create_error_response(&err.to_string(), &return_path)); + return Box::new(create_error_response( + &err.to_string(), + &return_path, + sort_method, + sort_order, + color_scheme, + default_color_scheme, + )); } }; let overwrite_files = req.state().overwrite_files; @@ -178,7 +220,14 @@ pub fn upload_file(req: &HttpRequest<crate::MiniserveConfig>) -> FutureResponse< .header(header::LOCATION, return_path.to_string()) .finish(), ), - Err(e) => create_error_response(&e.to_string(), &return_path), + Err(e) => create_error_response( + &e.to_string(), + &return_path, + sort_method, + sort_order, + color_scheme, + default_color_scheme, + ), }), ) } @@ -187,11 +236,26 @@ pub fn upload_file(req: &HttpRequest<crate::MiniserveConfig>) -> FutureResponse< fn create_error_response( description: &str, return_path: &str, + sorting_method: Option<SortingMethod>, + sorting_order: Option<SortingOrder>, + color_scheme: ColorScheme, + default_color_scheme: ColorScheme, ) -> FutureResult<HttpResponse, actix_web::error::Error> { errors::log_error_chain(description.to_string()); future::ok( HttpResponse::BadRequest() .content_type("text/html; charset=utf-8") - .body(renderer::render_error(description, return_path).into_string()), + .body( + renderer::render_error( + description, + return_path, + sorting_method, + sorting_order, + color_scheme, + default_color_scheme, + true, + ) + .into_string(), + ), ) } diff --git a/src/listing.rs b/src/listing.rs index 7cc125d..cadbb99 100644 --- a/src/listing.rs +++ b/src/listing.rs @@ -18,10 +18,10 @@ use crate::themes; #[derive(Deserialize)] pub struct QueryParameters { pub path: Option<PathBuf>, - sort: Option<SortingMethod>, - order: Option<SortingOrder>, + pub sort: Option<SortingMethod>, + pub order: Option<SortingOrder>, + pub theme: Option<themes::ColorScheme>, download: Option<archive::CompressionMethod>, - theme: Option<themes::ColorScheme>, } /// Available sorting methods @@ -145,12 +145,7 @@ pub fn directory_listing<S>( let (sort_method, sort_order, download, color_scheme) = if let Ok(query) = Query::<QueryParameters>::extract(req) { - ( - query.sort, - query.order, - query.download.clone(), - query.theme, - ) + (query.sort, query.order, query.download.clone(), query.theme) } else { (None, None, None, None) }; @@ -159,7 +154,7 @@ pub fn directory_listing<S>( for entry in dir.path.read_dir()? { if dir.is_visible(&entry) { - let entry = entry.unwrap(); + let entry = entry?; let p = match entry.path().strip_prefix(&dir.path) { Ok(p) => base.join(p), Err(_) => continue, @@ -267,7 +262,18 @@ pub fn directory_listing<S>( errors::log_error_chain(err.to_string()); Ok(HttpResponse::Ok() .status(http::StatusCode::INTERNAL_SERVER_ERROR) - .body(renderer::render_error(&err.to_string(), serve_path).into_string())) + .body( + renderer::render_error( + &err.to_string(), + serve_path, + sort_method, + sort_order, + color_scheme, + default_color_scheme, + false, + ) + .into_string(), + )) } } } else { diff --git a/src/main.rs b/src/main.rs index a9d44df..bd71763 100644 --- a/src/main.rs +++ b/src/main.rs @@ -274,9 +274,11 @@ fn configure_app(app: App<MiniserveConfig>) -> App<MiniserveConfig> { if let Some(s) = s { if app.state().file_upload { + let default_color_scheme = app.state().default_color_scheme; // Allow file upload - app.resource(&upload_route, |r| { - r.method(Method::POST).f(file_upload::upload_file) + app.resource(&upload_route, move |r| { + r.method(Method::POST) + .f(move |file| file_upload::upload_file(file, default_color_scheme)) }) // Handle directories .handler(&full_route, s) diff --git a/src/renderer.rs b/src/renderer.rs index 76990de..2ed69d0 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -22,6 +22,15 @@ pub fn page( upload_route: &str, current_dir: &str, ) -> Markup { + let upload_action = build_upload_action( + upload_route, + current_dir, + sort_method, + sort_order, + color_scheme, + default_color_scheme, + ); + html! { (page_header(serve_path, color_scheme, file_upload)) body#drop-container { @@ -39,12 +48,12 @@ pub fn page( div.toolbar { div.download { @for compression_method in CompressionMethod::iter() { - (archive_button(compression_method)) + (archive_button(compression_method, sort_method, sort_order, color_scheme, default_color_scheme)) } } @if file_upload { div.upload { - form id="file_submit" action={(upload_route) "?path=" (current_dir)} method="POST" enctype="multipart/form-data" { + form id="file_submit" action=(upload_action) method="POST" enctype="multipart/form-data" { p { "Select a file to upload or drag it anywhere into the window" } div { input#file-input type="file" name="file_to_upload" required="" {} @@ -86,6 +95,29 @@ pub fn page( } } +/// Build the action of the upload form +fn build_upload_action( + upload_route: &str, + current_dir: &str, + sort_method: Option<SortingMethod>, + sort_order: Option<SortingOrder>, + color_scheme: ColorScheme, + default_color_scheme: ColorScheme, +) -> String { + let mut upload_action = format!("{}?path={}", upload_route, current_dir); + if let Some(sorting_method) = sort_method { + upload_action = format!("{}&sort={}", upload_action, &sorting_method); + } + if let Some(sorting_order) = sort_order { + upload_action = format!("{}&order={}", upload_action, &sorting_order); + } + if color_scheme != default_color_scheme { + upload_action = format!("{}&theme={}", upload_action, color_scheme.to_slug()); + } + + upload_action +} + /// Partial: color scheme selector fn color_scheme_selector( sort_method: Option<SortingMethod>, @@ -151,8 +183,30 @@ fn color_scheme_link( } /// Partial: archive button -fn archive_button(compress_method: CompressionMethod) -> Markup { - let link = format!("?download={}", compress_method); +fn archive_button( + compress_method: CompressionMethod, + sort_method: Option<SortingMethod>, + sort_order: Option<SortingOrder>, + color_scheme: ColorScheme, + default_color_scheme: ColorScheme, +) -> Markup { + let link = + if sort_method.is_none() && sort_order.is_none() && color_scheme == default_color_scheme { + format!("?download={}", compress_method) + } else { + format!( + "{}&download={}", + parametrized_link( + "", + sort_method, + sort_order, + color_scheme, + default_color_scheme + ), + compress_method + ) + }; + let text = format!("Download .{}", compress_method.extension()); html! { @@ -325,7 +379,7 @@ fn css(color_scheme: ColorScheme) -> Markup { font-weight: bold; color: {directory_link_color}; }} - a.file, a.file:visited {{ + a.file, a.file:visited, .error-back, .error-back:visited {{ color: {file_link_color}; }} a.symlink, a.symlink:visited {{ @@ -581,6 +635,22 @@ fn css(color_scheme: ColorScheme) -> Markup { width: 100%; text-align: center; }} + .error {{ + margin: 2rem; + }} + .error p {{ + margin: 1rem 0; + font-size: 0.9rem; + word-break: break-all; + }} + .error p:first-of-type {{ + font-size: 1.25rem; + color: {error_color}; + margin-bottom: 2rem; + }} + .error-nav {{ + margin-top: 4rem; + }} @media (max-width: 760px) {{ nav {{ padding: 0 2.5rem; @@ -661,7 +731,8 @@ fn css(color_scheme: ColorScheme) -> Markup { drag_border_color = theme.drag_border_color, drag_text_color = theme.drag_text_color, size_background_color = theme.size_background_color, - size_text_color = theme.size_text_color); + size_text_color = theme.size_text_color, + error_color = theme.error_color); (PreEscaped(css)) } @@ -761,11 +832,40 @@ fn humanize_systemtime(src_time: Option<SystemTime>) -> Option<String> { } /// Renders an error on the webpage -pub fn render_error(error_description: &str, return_address: &str) -> Markup { +pub fn render_error( + error_description: &str, + return_address: &str, + sort_method: Option<SortingMethod>, + sort_order: Option<SortingOrder>, + color_scheme: ColorScheme, + default_color_scheme: ColorScheme, + has_referer: bool, +) -> Markup { + let link = if has_referer { + return_address.to_string() + } else { + parametrized_link( + return_address, + sort_method, + sort_order, + color_scheme, + default_color_scheme, + ) + }; + html! { - pre { (error_description) } - a href=(return_address) { - "Go back to file listing" + body { + (page_header("Error", color_scheme, false)) + div.error { + @for error in error_description.lines() { + p { (error) } + } + div.error-nav { + a.error-back href=(link) { + "Go back to file listing" + } + } + } } } } diff --git a/src/themes.rs b/src/themes.rs index 49a1ad3..65e9ab2 100644 --- a/src/themes.rs +++ b/src/themes.rs @@ -81,6 +81,7 @@ impl ColorScheme { drag_text_color: "#fefefe".to_string(), size_background_color: "#5294e2".to_string(), size_text_color: "#fefefe".to_string(), + error_color: "#e44b4b".to_string(), }, ColorScheme::Zenburn => Theme { background: "#3f3f3f".to_string(), @@ -123,6 +124,7 @@ impl ColorScheme { drag_text_color: "#efefef".to_string(), size_background_color: "#7f9f7f".to_string(), size_text_color: "#efefef".to_string(), + error_color: "#d06565".to_string(), }, ColorScheme::Monokai => Theme { background: "#272822".to_string(), @@ -165,6 +167,7 @@ impl ColorScheme { drag_text_color: "#F8F8F2".to_string(), size_background_color: "#75715E".to_string(), size_text_color: "#F8F8F2".to_string(), + error_color: "#d02929".to_string(), }, ColorScheme::Squirrel => Theme { background: "#FFFFFF".to_string(), @@ -207,6 +210,7 @@ impl ColorScheme { drag_text_color: "#ffffff".to_string(), size_background_color: "#323232".to_string(), size_text_color: "#FFFFFF".to_string(), + error_color: "#d02424".to_string(), }, } } @@ -254,4 +258,5 @@ pub struct Theme { pub drag_text_color: String, pub size_background_color: String, pub size_text_color: String, + pub error_color: String, } |