aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/file_upload.rs108
-rw-r--r--src/listing.rs28
-rw-r--r--src/main.rs6
-rw-r--r--src/renderer.rs120
-rw-r--r--src/themes.rs5
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,
}