aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--Cargo.lock12
-rw-r--r--Cargo.toml4
-rw-r--r--README.md1
-rw-r--r--src/args.rs10
-rw-r--r--src/errors.rs20
-rw-r--r--src/file_upload.rs152
-rw-r--r--src/listing.rs9
-rw-r--r--src/main.rs30
-rw-r--r--src/renderer.rs145
-rw-r--r--src/themes.rs50
10 files changed, 411 insertions, 22 deletions
diff --git a/Cargo.lock b/Cargo.lock
index f48ca03..db2959b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -87,7 +87,7 @@ dependencies = [
"percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_urlencoded 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
"sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -778,7 +778,7 @@ dependencies = [
"maud 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
"nanoid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
"simplelog 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
"structopt 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
"tar 0.4.22 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1219,7 +1219,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde"
-version = "1.0.89"
+version = "1.0.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1242,7 +1242,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1252,7 +1252,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -2013,7 +2013,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27"
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
-"checksum serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)" = "92514fb95f900c9b5126e32d020f5c6d40564c27a5ea6d1d7d9f157a96623560"
+"checksum serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)" = "aa5f7c20820475babd2c077c3ab5f8c77a31c15e16ea38687b4c02d3e48680f4"
"checksum serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6eabf4b5914e88e24eea240bb7c9f9a2cbc1bbbe8d961d381975ec3c6b806c"
"checksum serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d"
"checksum serde_urlencoded 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d48f9f99cd749a2de71d29da5f948de7f2764cc5a9d7f3c97e3514d4ee6eabf2"
diff --git a/Cargo.toml b/Cargo.toml
index 4689a80..5384ee7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -36,10 +36,10 @@ structopt = "0.2.15"
chrono = "0.4.6"
chrono-humanize = "0.0.11"
maud = { version = "0.20.0", features = ["actix-web"] }
-serde = { version = "1.0.89", features = ["derive"] }
+serde = { version = "1.0.90", features = ["derive"] }
tar = "0.4.22"
bytes = "0.4.12"
futures = "0.1.26"
libflate = "0.1.21"
failure = "0.1.5"
-log = "0.4.6" \ No newline at end of file
+log = "0.4.6"
diff --git a/README.md b/README.md
index 2b8e8b3..ce02116 100644
--- a/README.md
+++ b/README.md
@@ -45,6 +45,7 @@ Sometimes this is just a more practical and quick way than doing things properly
- Authentication support with username and password
- Mega fast and highly parallel (thanks to [Rust](https://www.rust-lang.org/) and [Actix](https://actix.rs/))
- Folder download (compressed in .tar.gz)
+- File uploading
## Known limitations
diff --git a/src/args.rs b/src/args.rs
index bcee0bc..3e54d08 100644
--- a/src/args.rs
+++ b/src/args.rs
@@ -60,6 +60,14 @@ struct CLIArgs {
)
)]
color_scheme: themes::ColorScheme,
+
+ /// Enable file uploading
+ #[structopt(short = "u", long = "upload-files")]
+ file_upload: bool,
+
+ /// Enable overriding existing files during file upload
+ #[structopt(short = "o", long = "overwrite-files")]
+ overwrite_files: bool,
}
/// Checks wether an interface is valid, i.e. it can be parsed into an IP address
@@ -116,5 +124,7 @@ pub fn parse_args() -> crate::MiniserveConfig {
no_symlinks: args.no_symlinks,
random_route,
default_color_scheme,
+ overwrite_files: args.overwrite_files,
+ file_upload: args.file_upload,
}
}
diff --git a/src/errors.rs b/src/errors.rs
index 2aa5f58..21d9e07 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -1,6 +1,26 @@
use failure::{Backtrace, Context, Fail};
use std::fmt::{self, Debug, Display};
+/// Kinds of errors which might happen during file upload
+#[derive(Debug, Fail)]
+pub enum FileUploadErrorKind {
+ /// This error will occur when file overriding is off and a file with same name already exists
+ #[fail(display = "File with this name already exists")]
+ FileExist,
+ /// This error will occur when the server fails to process the HTTP header during file upload
+ #[fail(display = "Failed to parse incoming request")]
+ ParseError,
+ /// This error will occur when we fail to process the multipart request
+ #[fail(display = "Failed to process multipart request")]
+ MultipartError(actix_web::error::MultipartError),
+ /// This error may occur when trying to write the incoming file to disk
+ #[fail(display = "Failed to create or write to file")]
+ IOError(std::io::Error),
+ /// This error will occur when we he have insuffictent permissions to create new file
+ #[fail(display = "Insufficient permissions to create file")]
+ InsufficientPermissions,
+}
+
/// Kinds of errors which might happen during the generation of an archive
#[derive(Debug, Fail)]
pub enum CompressionErrorKind {
diff --git a/src/file_upload.rs b/src/file_upload.rs
new file mode 100644
index 0000000..273d12c
--- /dev/null
+++ b/src/file_upload.rs
@@ -0,0 +1,152 @@
+use crate::errors::FileUploadErrorKind;
+use crate::renderer::file_upload_error;
+use actix_web::{
+ dev, http::header, multipart, FromRequest, FutureResponse, HttpMessage, HttpRequest,
+ HttpResponse, Query,
+};
+use futures::{future, Future, Stream};
+use serde::Deserialize;
+use std::{
+ fs,
+ io::Write,
+ path::{Component, PathBuf},
+};
+
+/// Query parameters
+#[derive(Debug, Deserialize)]
+struct QueryParameters {
+ path: PathBuf,
+}
+
+/// Create future to save file.
+fn save_file(
+ field: multipart::Field<dev::Payload>,
+ file_path: PathBuf,
+ overwrite_files: bool,
+) -> Box<Future<Item = i64, Error = FileUploadErrorKind>> {
+ if !overwrite_files && file_path.exists() {
+ return Box::new(future::err(FileUploadErrorKind::FileExist));
+ }
+ let mut file = match std::fs::File::create(file_path) {
+ Ok(file) => file,
+ Err(e) => {
+ return Box::new(future::err(FileUploadErrorKind::IOError(e)));
+ }
+ };
+ Box::new(
+ field
+ .map_err(FileUploadErrorKind::MultipartError)
+ .fold(0i64, move |acc, bytes| {
+ let rt = file
+ .write_all(bytes.as_ref())
+ .map(|_| acc + bytes.len() as i64)
+ .map_err(FileUploadErrorKind::IOError);
+ future::result(rt)
+ }),
+ )
+}
+
+/// Create new future to handle file as multipart data.
+fn handle_multipart(
+ item: multipart::MultipartItem<dev::Payload>,
+ mut file_path: PathBuf,
+ overwrite_files: bool,
+) -> Box<Stream<Item = i64, Error = FileUploadErrorKind>> {
+ match item {
+ multipart::MultipartItem::Field(field) => {
+ let filename = field
+ .headers()
+ .get(header::CONTENT_DISPOSITION)
+ .ok_or(FileUploadErrorKind::ParseError)
+ .and_then(|cd| {
+ header::ContentDisposition::from_raw(cd)
+ .map_err(|_| FileUploadErrorKind::ParseError)
+ })
+ .and_then(|content_disposition| {
+ content_disposition
+ .get_filename()
+ .ok_or(FileUploadErrorKind::ParseError)
+ .map(String::from)
+ });
+ let err = |e: FileUploadErrorKind| Box::new(future::err(e).into_stream());
+ match filename {
+ Ok(f) => {
+ match fs::metadata(&file_path) {
+ Ok(metadata) => {
+ if !metadata.is_dir() || metadata.permissions().readonly() {
+ return err(FileUploadErrorKind::InsufficientPermissions);
+ }
+ }
+ Err(_) => {
+ return err(FileUploadErrorKind::InsufficientPermissions);
+ }
+ }
+ file_path = file_path.join(f);
+ Box::new(save_file(field, file_path, overwrite_files).into_stream())
+ }
+ Err(e) => err(e),
+ }
+ }
+ multipart::MultipartItem::Nested(mp) => Box::new(
+ mp.map_err(FileUploadErrorKind::MultipartError)
+ .map(move |item| handle_multipart(item, file_path.clone(), overwrite_files))
+ .flatten(),
+ ),
+ }
+}
+
+/// Handle incoming request to upload file.
+/// Target file path is expected as path parameter in URI and is interpreted as relative from
+/// 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> {
+ let app_root_dir = req.state().path.clone().canonicalize().unwrap();
+ let path = match Query::<QueryParameters>::extract(req) {
+ Ok(query) => {
+ if let Ok(stripped_path) = query.path.strip_prefix(Component::RootDir) {
+ stripped_path.to_owned()
+ } else {
+ query.path.clone()
+ }
+ }
+ Err(_) => {
+ return Box::new(future::ok(
+ HttpResponse::BadRequest().body("Unspecified parameter path"),
+ ))
+ }
+ };
+ // this is really ugly I will try to think about something smarter
+ let return_path: String = req.headers()[header::REFERER]
+ .clone()
+ .to_str()
+ .unwrap_or("/")
+ .to_owned();
+ let r_p2 = return_path.clone();
+
+ // If the target path is under the app root directory, save the file.
+ let target_dir = match &app_root_dir.clone().join(path.clone()).canonicalize() {
+ Ok(path) if path.starts_with(&app_root_dir) => path.clone(),
+ _ => return Box::new(future::ok(HttpResponse::BadRequest().body("Invalid path"))),
+ };
+ let overwrite_files = req.state().overwrite_files;
+ Box::new(
+ req.multipart()
+ .map_err(FileUploadErrorKind::MultipartError)
+ .map(move |item| handle_multipart(item, target_dir.clone(), overwrite_files))
+ .flatten()
+ .collect()
+ .map(move |_| {
+ HttpResponse::TemporaryRedirect()
+ .header(header::LOCATION, return_path.to_string())
+ .finish()
+ })
+ .or_else(move |e| {
+ let error_description = format!("{}", e);
+ future::ok(
+ HttpResponse::BadRequest()
+ .body(file_upload_error(&error_description, &r_p2.clone()).into_string()),
+ )
+ }),
+ )
+}
diff --git a/src/listing.rs b/src/listing.rs
index de84dc6..b7070f3 100644
--- a/src/listing.rs
+++ b/src/listing.rs
@@ -144,14 +144,20 @@ pub fn directory_listing<S>(
dir: &fs::Directory,
req: &HttpRequest<S>,
skip_symlinks: bool,
+ file_upload: bool,
random_route: Option<String>,
default_color_scheme: themes::ColorScheme,
+ upload_route: String,
) -> Result<HttpResponse, io::Error> {
let title = format!("Index of {}", req.path());
let base = Path::new(req.path());
let random_route = format!("/{}", random_route.unwrap_or_default());
let is_root = base.parent().is_none() || req.path() == random_route;
let page_parent = base.parent().map(|p| p.display().to_string());
+ let current_dir = match base.strip_prefix(random_route) {
+ Ok(c_d) => Path::new("/").join(c_d),
+ Err(_) => base.to_path_buf(),
+ };
let (sort_method, sort_order, download, color_scheme) =
if let Ok(query) = Query::<QueryParameters>::extract(req) {
@@ -292,6 +298,9 @@ pub fn directory_listing<S>(
sort_method,
sort_order,
color_scheme,
+ file_upload,
+ &upload_route,
+ &current_dir.display().to_string(),
)
.into_string(),
))
diff --git a/src/main.rs b/src/main.rs
index 79a5db2..64de8d3 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,5 +1,6 @@
#![feature(proc_macro_hygiene)]
+use actix_web::http::Method;
use actix_web::{fs, middleware, server, App};
use clap::crate_version;
use simplelog::{Config, LevelFilter, TermLogger};
@@ -13,6 +14,7 @@ mod archive;
mod args;
mod auth;
mod errors;
+mod file_upload;
mod listing;
mod renderer;
mod themes;
@@ -46,6 +48,12 @@ pub struct MiniserveConfig {
/// Default color scheme
pub default_color_scheme: themes::ColorScheme,
+
+ /// Enable file upload
+ pub file_upload: bool,
+
+ /// Enable upload to override existing files
+ pub overwrite_files: bool,
}
fn main() {
@@ -178,14 +186,21 @@ fn main() {
/// Configures the Actix application
fn configure_app(app: App<MiniserveConfig>) -> App<MiniserveConfig> {
+ let upload_route;
let s = {
let path = &app.state().path;
let no_symlinks = app.state().no_symlinks;
let random_route = app.state().random_route.clone();
let default_color_scheme = app.state().default_color_scheme.clone();
+ let file_upload = app.state().file_upload;
+ upload_route = match app.state().random_route.clone() {
+ Some(random_route) => format!("/{}/upload", random_route),
+ None => "/upload".to_string(),
+ };
if path.is_file() {
None
} else {
+ let u_r = upload_route.clone();
Some(
fs::StaticFiles::new(path)
.expect("Couldn't create path")
@@ -195,8 +210,10 @@ fn configure_app(app: App<MiniserveConfig>) -> App<MiniserveConfig> {
dir,
req,
no_symlinks,
+ file_upload,
random_route.clone(),
default_color_scheme.clone(),
+ u_r.clone(),
)
}),
)
@@ -207,8 +224,17 @@ fn configure_app(app: App<MiniserveConfig>) -> App<MiniserveConfig> {
let full_route = format!("/{}", random_route);
if let Some(s) = s {
- // Handle directories
- app.handler(&full_route, s)
+ if app.state().file_upload {
+ // Allow file upload
+ app.resource(&upload_route, |r| {
+ r.method(Method::POST).f(file_upload::upload_file)
+ })
+ // Handle directories
+ .handler(&full_route, s)
+ } else {
+ // Handle directories
+ app.handler(&full_route, s)
+ }
} else {
// Handle single files
app.resource(&full_route, |r| r.f(listing::file_handler))
diff --git a/src/renderer.rs b/src/renderer.rs
index abb8730..bd7b272 100644
--- a/src/renderer.rs
+++ b/src/renderer.rs
@@ -16,19 +16,38 @@ pub fn page(
sort_method: Option<listing::SortingMethod>,
sort_order: Option<listing::SortingOrder>,
color_scheme: themes::ColorScheme,
+ file_upload: bool,
+ upload_route: &str,
+ current_dir: &str,
) -> Markup {
html! {
(page_header(page_title, &color_scheme))
- body {
+ body#drop-container {
+ div.drag-form {
+ div.drag-title {
+ h1 { "Drop your file here to upload it" }
+ }
+ }
(color_scheme_selector(&sort_method, &sort_order, &color_scheme))
div.container {
- span #top { }
+ span#top { }
h1.title { (page_title) }
div.download {
- @for compression_method in archive::CompressionMethod::get_compression_methods() {
+ @for compression_method in archive::CompressionMethod::get_compression_methods() {
(archive_button(compression_method))
}
}
+ @if file_upload {
+ div.upload {
+ form id="file_submit" action={(upload_route) "?path=" (current_dir)} 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" {}
+ button type="submit" { "Upload file" }
+ }
+ }
+ }
+ }
table {
thead {
th { (build_link("name", "Name", &sort_method, &sort_order, &color_scheme)) }
@@ -269,6 +288,7 @@ fn css(color_scheme: &themes::ColorScheme) -> Markup {
font-weight: 300;
color: {text_color};
background: {background};
+ position: relative;
}}
.container {{
padding: 1.5rem 5rem;
@@ -488,6 +508,50 @@ fn css(color_scheme: &themes::ColorScheme) -> Markup {
.download a:not(:last-of-type) {{
margin-right: 1rem;
}}
+ .upload {{
+ display: flex;
+ justify-content: flex-end;
+ margin-top: 1rem;
+ }}
+ .upload p {{
+ font-size: 0.8rem;
+ margin-bottom: 1rem;
+ color: {upload_text_color};
+ }}
+ .upload form {{
+ padding: 1rem;
+ border: 1px solid {upload_form_border_color};
+ background: {upload_form_background};
+ }}
+ .upload button {{
+ background: {upload_button_background};
+ padding: 0.5rem;
+ border-radius: 0.2rem;
+ color: {upload_button_text_color};
+ border: none;
+ }}
+ .upload div {{
+ display: flex;
+ align-items: baseline;
+ justify-content: space-between;
+ }}
+ .drag-form {{
+ display: none;
+ background: {drag_background};
+ position: absolute;
+ border: 0.5rem dashed {drag_border_color};
+ width: calc(100% - 1rem);
+ height: calc(100% - 1rem);
+ text-align: center;
+ z-index: 2;
+ }}
+ .drag-title {{
+ position: fixed;
+ color: {drag_text_color};
+ top: 50%;
+ width: 100%;
+ text-align: center;
+ }}
@media (max-width: 760px) {{
nav {{
padding: 0 2.5rem;
@@ -496,7 +560,7 @@ fn css(color_scheme: &themes::ColorScheme) -> Markup {
padding: 1.5rem 2.5rem;
}}
h1 {{
- font-size: 1.375em;
+ font-size: 1.4em;
}}
td:not(:nth-child(1)), th:not(:nth-child(1)){{
display: none;
@@ -508,10 +572,21 @@ fn css(color_scheme: &themes::ColorScheme) -> Markup {
padding-bottom: 1rem;
}}
.back {{
- right: 1.5rem;
+ display: initial;
+ }}
+ .upload {{
+ margin-top: 2rem;
+ }}
+ .upload form {{
+ width: 100%;
}}
.back {{
- display: initial;
+ right: 1.5rem;
+ }}
+ }}
+ @media (max-width: 600px) {{
+ h1 {{
+ font-size: 1.375em;
}}
}}
@media (max-width: 400px) {{
@@ -557,7 +632,15 @@ fn css(color_scheme: &themes::ColorScheme) -> Markup {
switch_theme_border = theme.switch_theme_border,
change_theme_link_color = theme.change_theme_link_color,
change_theme_link_color_hover = theme.change_theme_link_color_hover,
- field_color = theme.field_color);
+ field_color = theme.field_color,
+ upload_text_color = theme.upload_text_color,
+ upload_form_border_color = theme.upload_form_border_color,
+ upload_form_background = theme.upload_form_background,
+ upload_button_background = theme.upload_button_background,
+ upload_button_text_color = theme.upload_button_text_color,
+ drag_background = theme.drag_background,
+ drag_border_color = theme.drag_border_color,
+ drag_text_color = theme.drag_text_color);
(PreEscaped(css))
}
@@ -596,6 +679,43 @@ fn page_header(page_title: &str, color_scheme: &themes::ColorScheme) -> Markup {
meta name="viewport" content="width=device-width, initial-scale=1";
title { (page_title) }
style { (css(&color_scheme)) }
+ (PreEscaped(r#"
+ <script>
+ window.onload = function() {
+ const dropContainer = document.querySelector('#drop-container');
+ const dragForm = document.querySelector('.drag-form');
+ const fileInput = document.querySelector('#file-input');
+ const collection = [];
+
+ dropContainer.ondragover = function(e) {
+ e.preventDefault();
+ }
+
+ dropContainer.ondragenter = function(e) {
+ e.preventDefault();
+ if (collection.length === 0) {
+ dragForm.style.display = 'initial';
+ }
+ collection.push(e.target);
+ };
+
+ dropContainer.ondragleave = function(e) {
+ e.preventDefault();
+ collection.splice(collection.indexOf(e.target), 1);
+ if (collection.length === 0) {
+ dragForm.style.display = 'none';
+ }
+ };
+
+ dropContainer.ondrop = function(e) {
+ e.preventDefault();
+ fileInput.files = e.dataTransfer.files;
+ file_submit.submit();
+ dragForm.style.display = 'none';
+ };
+ }
+ </script>
+ "#))
}
}
}
@@ -621,3 +741,14 @@ fn humanize_systemtime(src_time: Option<SystemTime>) -> Option<String> {
.and_then(|from_now| Duration::from_std(from_now).ok())
.map(|duration| HumanTime::from(duration).to_text_en(Accuracy::Rough, Tense::Past))
}
+
+/// Renders error page when file uploading fails
+pub fn file_upload_error(error_description: &str, return_address: &str) -> Markup {
+ html! {
+ h1 { "File uploading failed" }
+ p { (error_description) }
+ a href=(return_address) {
+ "back"
+ }
+ }
+}
diff --git a/src/themes.rs b/src/themes.rs
index d04eab2..917e56e 100644
--- a/src/themes.rs
+++ b/src/themes.rs
@@ -90,6 +90,14 @@ impl ColorScheme {
change_theme_link_color: "#fefefe".to_string(),
change_theme_link_color_hover: "#fefefe".to_string(),
field_color: "#859cb9".to_string(),
+ upload_text_color: "#fefefe".to_string(),
+ upload_form_border_color: "#353946".to_string(),
+ upload_form_background: "#4b5162".to_string(),
+ upload_button_background: "#ea95ff".to_string(),
+ upload_button_text_color: "#ffffff".to_string(),
+ drag_background: "#3333338f".to_string(),
+ drag_border_color: "#fefefe".to_string(),
+ drag_text_color: "#fefefe".to_string(),
},
ColorScheme::Zenburn => Theme {
background: "#3f3f3f".to_string(),
@@ -123,6 +131,14 @@ impl ColorScheme {
change_theme_link_color: "#efefef".to_string(),
change_theme_link_color_hover: "#efefef".to_string(),
field_color: "#9fc3a1".to_string(),
+ upload_text_color: "#efefef".to_string(),
+ upload_form_border_color: "#4a4949".to_string(),
+ upload_form_background: "#777777".to_string(),
+ upload_button_background: "#cc9393".to_string(),
+ upload_button_text_color: "#efefef".to_string(),
+ drag_background: "#3333338f".to_string(),
+ drag_border_color: "#efefef".to_string(),
+ drag_text_color: "#efefef".to_string(),
},
ColorScheme::Monokai => Theme {
background: "#272822".to_string(),
@@ -156,6 +172,14 @@ impl ColorScheme {
change_theme_link_color: "#F8F8F2".to_string(),
change_theme_link_color_hover: "#F8F8F2".to_string(),
field_color: "#ccc7a7".to_string(),
+ upload_text_color: "#F8F8F2".to_string(),
+ upload_form_border_color: "#3B3A32".to_string(),
+ upload_form_background: "#49483E".to_string(),
+ upload_button_background: "#AE81FF".to_string(),
+ upload_button_text_color: "#F8F8F0".to_string(),
+ drag_background: "#3333338f".to_string(),
+ drag_border_color: "#F8F8F2".to_string(),
+ drag_text_color: "#F8F8F2".to_string(),
},
ColorScheme::Squirrel => Theme {
background: "#FFFFFF".to_string(),
@@ -163,7 +187,7 @@ impl ColorScheme {
directory_link_color: "#d02474".to_string(),
file_link_color: "#0086B3".to_string(),
symlink_link_color: "#ED6A43".to_string(),
- table_background: "#F5F5F5".to_string(),
+ table_background: "#ffffff".to_string(),
table_text_color: "#323232".to_string(),
table_header_background: "#323232".to_string(),
table_header_text_color: "#F5F5F5".to_string(),
@@ -174,12 +198,12 @@ impl ColorScheme {
root_link_color: "#323232".to_string(),
download_button_background: "#d02474".to_string(),
download_button_background_hover: "#f52d8a".to_string(),
- download_button_link_color: "#F8F8F0".to_string(),
- download_button_link_color_hover: "#F8F8F0".to_string(),
+ download_button_link_color: "#FFFFFF".to_string(),
+ download_button_link_color_hover: "#FFFFFF".to_string(),
back_button_background: "#d02474".to_string(),
back_button_background_hover: "#d02474".to_string(),
- back_button_link_color: "#F8F8F0".to_string(),
- back_button_link_color_hover: "#F8F8F0".to_string(),
+ back_button_link_color: "#FFFFFF".to_string(),
+ back_button_link_color_hover: "#FFFFFF".to_string(),
date_text_color: "#797979".to_string(),
at_color: "#797979".to_string(),
switch_theme_background: "#323232".to_string(),
@@ -189,6 +213,14 @@ impl ColorScheme {
change_theme_link_color: "#F5F5F5".to_string(),
change_theme_link_color_hover: "#F5F5F5".to_string(),
field_color: "#797979".to_string(),
+ upload_text_color: "#323232".to_string(),
+ upload_form_border_color: "#d2d2d2".to_string(),
+ upload_form_background: "#f2f2f2".to_string(),
+ upload_button_background: "#d02474".to_string(),
+ upload_button_text_color: "#FFFFFF".to_string(),
+ drag_background: "#3333338f".to_string(),
+ drag_border_color: "#ffffff".to_string(),
+ drag_text_color: "#ffffff".to_string(),
},
}
}
@@ -227,4 +259,12 @@ pub struct Theme {
pub change_theme_link_color: String,
pub change_theme_link_color_hover: String,
pub field_color: String,
+ pub upload_text_color: String,
+ pub upload_form_border_color: String,
+ pub upload_form_background: String,
+ pub upload_button_background: String,
+ pub upload_button_text_color: String,
+ pub drag_background: String,
+ pub drag_border_color: String,
+ pub drag_text_color: String,
}