From a315baa258e00f6db5d93d557da1ce2bc0a24d4f Mon Sep 17 00:00:00 2001 From: minish Date: Thu, 7 Dec 2023 13:31:27 -0500 Subject: [PATCH] config restructure + motd option --- Cargo.lock | 151 +++++++++++++++++++++++++++----------------- archived/Cargo.lock | 7 ++ archived/Cargo.toml | 2 +- archived/src/lib.rs | 26 ++++---- src/config.rs | 34 ++++++++-- src/engine.rs | 60 ++++++------------ src/index.rs | 6 +- src/main.rs | 34 +++++----- src/new.rs | 4 +- 9 files changed, 185 insertions(+), 139 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6632a01..109f947 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "ahash" version = "0.7.6" @@ -114,9 +129,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "axum" -version = "0.6.1" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08b108ad2665fa3f6e6a517c3d80ec3e77d224c47d605167aefaa5d7ef97fa48" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" dependencies = [ "async-trait", "axum-core", @@ -141,16 +156,15 @@ dependencies = [ "sync_wrapper", "tokio", "tower", - "tower-http", "tower-layer", "tower-service", ] [[package]] name = "axum-core" -version = "0.3.0" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79b8558f5a0581152dc94dcd289132a1d377494bdeafcd41869b3258e3e2ad92" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" dependencies = [ "async-trait", "bytes", @@ -165,14 +179,29 @@ dependencies = [ [[package]] name = "axum-macros" -version = "0.3.0" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4df0fc33ada14a338b799002f7e8657711422b25d4e16afb032708d6b185621" +checksum = "cdca6a10ecad987bda04e95606ef85a5417dcaac1a78455242d72e031e2b6b62" dependencies = [ "heck", "proc-macro2", "quote", - "syn 1.0.104", + "syn 2.0.38", +] + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", ] [[package]] @@ -471,10 +500,16 @@ dependencies = [ ] [[package]] -name = "h2" -version = "0.3.15" +name = "gimli" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "h2" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" dependencies = [ "bytes", "fnv", @@ -482,7 +517,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 1.9.2", + "indexmap 2.0.2", "slab", "tokio", "tokio-util", @@ -527,9 +562,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "http" -version = "0.2.8" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" dependencies = [ "bytes", "fnv", @@ -547,12 +582,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "http-range-header" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" - [[package]] name = "httparse" version = "1.8.0" @@ -567,9 +596,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.23" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -642,9 +671,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.4" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" @@ -663,9 +692,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.137" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "lock_api" @@ -705,15 +734,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" [[package]] -name = "mio" -version = "0.8.5" +name = "miniz_oxide" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", - "log", "wasi", - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] [[package]] @@ -745,6 +782,15 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.16.0" @@ -887,6 +933,12 @@ dependencies = [ "bitflags", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustversion" version = "1.0.9" @@ -1039,9 +1091,9 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" dependencies = [ "libc", "winapi", @@ -1122,14 +1174,14 @@ dependencies = [ [[package]] name = "tokio" -version = "1.22.0" +version = "1.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76ce4a75fb488c605c54bf610f221cea8b0dafb53333c1a67e8ee199dcd2ae3" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" dependencies = [ "autocfg", + "backtrace", "bytes", "libc", - "memchr", "mio", "num_cpus", "parking_lot", @@ -1137,18 +1189,18 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "winapi", + "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "1.8.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 1.0.104", + "syn 2.0.38", ] [[package]] @@ -1230,25 +1282,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "tower-http" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c530c8675c1dbf98facee631536fa116b5fb6382d7dd6dc1b118d970eafe3ba" -dependencies = [ - "bitflags", - "bytes", - "futures-core", - "futures-util", - "http", - "http-body", - "http-range-header", - "pin-project-lite", - "tower", - "tower-layer", - "tower-service", -] - [[package]] name = "tower-layer" version = "0.3.2" diff --git a/archived/Cargo.lock b/archived/Cargo.lock index 9fe28b6..a678206 100644 --- a/archived/Cargo.lock +++ b/archived/Cargo.lock @@ -8,6 +8,7 @@ version = "0.2.0" dependencies = [ "bytes", "once_cell", + "rustc-hash", ] [[package]] @@ -21,3 +22,9 @@ name = "once_cell" version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" diff --git a/archived/Cargo.toml b/archived/Cargo.toml index 18621e5..7f659de 100644 --- a/archived/Cargo.toml +++ b/archived/Cargo.toml @@ -6,4 +6,4 @@ license = "MIT" [dependencies] bytes = "1.3.0" -once_cell = "1.3.1" +once_cell = "1.3.1" \ No newline at end of file diff --git a/archived/src/lib.rs b/archived/src/lib.rs index 00003bd..cfb154a 100644 --- a/archived/src/lib.rs +++ b/archived/src/lib.rs @@ -29,7 +29,11 @@ impl Archive { } } */ - pub fn with_full_scan(full_scan_frequency: Duration, entry_lifetime: Duration, capacity: usize) -> Self { + pub fn with_full_scan( + full_scan_frequency: Duration, + entry_lifetime: Duration, + capacity: usize, + ) -> Self { Self { cache_table: HashMap::with_capacity(256), full_scan_frequency: Some(full_scan_frequency), @@ -67,11 +71,7 @@ impl Archive { .map(|cache_entry| &cache_entry.value) } - pub fn get_or_insert( - &mut self, - key: String, - factory: F, - ) -> &Bytes + pub fn get_or_insert(&mut self, key: String, factory: F) -> &Bytes where F: Fn() -> Bytes, { @@ -87,15 +87,15 @@ impl Archive { &occupied.into_mut().value } - Entry::Vacant(vacant) => &vacant.insert(CacheEntry::new(factory(), self.entry_lifetime)).value, + Entry::Vacant(vacant) => { + &vacant + .insert(CacheEntry::new(factory(), self.entry_lifetime)) + .value + } } } - pub fn insert( - &mut self, - key: String, - value: Bytes, - ) -> Option { + pub fn insert(&mut self, key: String, value: Bytes) -> Option { let now = SystemTime::now(); self.try_full_scan_expired_items(now); @@ -144,7 +144,7 @@ impl Archive { Some(()) } - None => None + None => None, } } diff --git a/src/config.rs b/src/config.rs index 2b087ce..8ad95b5 100644 --- a/src/config.rs +++ b/src/config.rs @@ -7,7 +7,7 @@ use tracing_subscriber::filter::LevelFilter; #[derive(Deserialize)] pub struct Config { pub engine: EngineConfig, - pub cache: CacheConfig, + pub http: HttpConfig, pub logger: LoggerConfig, } @@ -22,7 +22,18 @@ pub struct EngineConfig { pub save_path: PathBuf, /// Authentication key for new uploads, will be required if this is specified. (optional) - pub upload_key: Option, + #[serde(default)] + pub upload_key: String, + + /// Configuration for cache system + pub cache: CacheConfig, + + /// Motd displayed when the server's index page is visited. + /// + /// This isn't explicitly engine-related but the engine is what gets passed to routes, + /// so it is here for now. + #[serde(default = "default_motd")] + pub motd: String, } #[serde_as] @@ -45,11 +56,26 @@ pub struct CacheConfig { pub mem_capacity: usize, } +fn default_motd() -> String { + "breeze file server (v%version%) - currently hosting %uplcount% files".to_string() +} + +#[derive(Deserialize)] +pub struct HttpConfig { + pub listen_on: String, +} + +fn default_level_filter() -> LevelFilter { + LevelFilter::WARN +} + #[serde_as] #[derive(Deserialize)] pub struct LoggerConfig { /// Minimum level a log must be for it to be shown. /// This defaults to "warn" if not specified. - #[serde_as(as = "Option")] - pub level: Option, + #[serde_as(as = "DisplayFromStr")] + #[serde(default = "default_level_filter")] + // yes... kind of a hack but serde doesn't have anything better + pub level: LevelFilter, } diff --git a/src/engine.rs b/src/engine.rs index 69272cb..152eccd 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -2,7 +2,6 @@ use std::{ ffi::OsStr, path::{Path, PathBuf}, sync::atomic::{AtomicUsize, Ordering}, - time::Duration, }; use archived::Archive; @@ -21,66 +20,47 @@ use tokio_stream::StreamExt; use tracing::{debug, error, info}; use walkdir::WalkDir; -use crate::view::{ViewError, ViewSuccess}; +use crate::{ + config, + view::{ViewError, ViewSuccess}, +}; /// breeze engine! this is the core of everything pub struct Engine { - // ------ STATE ------ // /// The in-memory cache that cached uploads are stored in. cache: RwLock, /// Cached count of uploaded files. pub upl_count: AtomicUsize, - // ------ CONFIG ------ // - /// The base URL that the server will be accessed from. - /// It is only used for formatting returned upload URLs. - pub base_url: String, - - /// The path on disk that uploads are saved to. - save_path: PathBuf, - - /// The authorisation key required for uploading new files. - /// If it is empty, no key will be required. - pub upload_key: String, - - /// The maximum size for an upload to be stored in cache. - /// Anything bigger skips cache and is read/written to - /// directly from disk. - cache_max_length: usize, + /// Engine configuration + pub cfg: config::EngineConfig, } impl Engine { /// Creates a new instance of the breeze engine. - pub fn new( - base_url: String, - save_path: PathBuf, - upload_key: String, - cache_max_length: usize, - cache_lifetime: Duration, - cache_full_scan_freq: Duration, // how often the cache will be scanned for expired items - cache_mem_capacity: usize, - ) -> Self { + pub fn new(cfg: config::EngineConfig) -> Self { Self { cache: RwLock::new(Archive::with_full_scan( - cache_full_scan_freq, - cache_lifetime, - cache_mem_capacity, + cfg.cache.scan_freq, + cfg.cache.upload_lifetime, + cfg.cache.mem_capacity, )), - upl_count: AtomicUsize::new(WalkDir::new(&save_path).min_depth(1).into_iter().count()), // count the amount of files in the save path and initialise our cached count with it + upl_count: AtomicUsize::new( + WalkDir::new(&cfg.save_path) + .min_depth(1) + .into_iter() + .count(), + ), // count the amount of files in the save path and initialise our cached count with it - base_url, - save_path, - upload_key, - - cache_max_length, + cfg, } } /// Returns if an upload would be able to be cached #[inline(always)] fn will_use_cache(&self, length: usize) -> bool { - length <= self.cache_max_length + length <= self.cfg.cache.max_length } /// Check if an upload exists in cache or on disk @@ -128,7 +108,7 @@ impl Engine { .to_string(); // path on disk - let mut path = self.save_path.clone(); + let mut path = self.cfg.save_path.clone(); path.push(&id); path.set_extension(original_extension); @@ -238,7 +218,7 @@ impl Engine { .to_string(); // path on disk - let mut path = self.save_path.clone(); + let mut path = self.cfg.save_path.clone(); path.push(&name); // check if the upload exists, if not then 404 diff --git a/src/index.rs b/src/index.rs index 5a1a9b8..fe46a10 100644 --- a/src/index.rs +++ b/src/index.rs @@ -6,7 +6,11 @@ use axum::extract::State; pub async fn index(State(engine): State>) -> String { let count = engine.upl_count.load(Ordering::Relaxed); - format!("minish's image host, currently hosting {} files", count) + let motd = engine.cfg.motd.clone(); + + motd + .replace("%version%", env!("CARGO_PKG_VERSION")) + .replace("%uplcount%", &count.to_string()) } pub async fn robots_txt() -> &'static str { diff --git a/src/main.rs b/src/main.rs index d7b8069..413354a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,6 @@ use axum::{ }; use tokio::{fs, signal}; use tracing::{info, warn}; -use tracing_subscriber::filter::LevelFilter; mod config; mod engine; @@ -35,30 +34,22 @@ async fn main() { .await .expect("failed to read config file! make sure it exists and you have read permissions"); - let c: config::Config = toml::from_str(&config_str).expect("invalid config! check that you have included all required options and structured it properly (no config options expecting a number getting a string, etc.)"); + let cfg: config::Config = toml::from_str(&config_str).expect("invalid config! check that you have included all required options and structured it properly (no config options expecting a number getting a string, etc.)"); tracing_subscriber::fmt() - .with_max_level(c.logger.level.unwrap_or(LevelFilter::WARN)) + .with_max_level(cfg.logger.level) .init(); - if !c.engine.save_path.exists() || !c.engine.save_path.is_dir() { + if !cfg.engine.save_path.exists() || !cfg.engine.save_path.is_dir() { panic!("the save path does not exist or is not a directory! this is invalid"); } - if c.engine.upload_key.is_none() { + if cfg.engine.upload_key.is_empty() { warn!("engine upload_key is empty! no key will be required for uploading new files"); } // create engine - let engine = Engine::new( - c.engine.base_url, - c.engine.save_path, - c.engine.upload_key.unwrap_or_default(), - c.cache.max_length, - c.cache.upload_lifetime, - c.cache.scan_freq, - c.cache.mem_capacity, - ); + let engine = Engine::new(cfg.engine); // build main router let app = Router::new() @@ -69,11 +60,16 @@ async fn main() { .with_state(Arc::new(engine)); // start web server - axum::Server::bind(&"0.0.0.0:8000".parse().unwrap()) - .serve(app.into_make_service()) - .with_graceful_shutdown(shutdown_signal()) - .await - .unwrap(); + axum::Server::bind( + &cfg.http + .listen_on + .parse() + .expect("failed to parse listen_on address"), + ) + .serve(app.into_make_service()) + .with_graceful_shutdown(shutdown_signal()) + .await + .expect("failed to start server"); } async fn shutdown_signal() { diff --git a/src/new.rs b/src/new.rs index d1fba6a..eba5f80 100644 --- a/src/new.rs +++ b/src/new.rs @@ -20,7 +20,7 @@ pub async fn new( const EMPTY_STRING: &String = &String::new(); // check upload key, if i need to - if !engine.upload_key.is_empty() && key.unwrap_or(EMPTY_STRING) != &engine.upload_key { + if !engine.cfg.upload_key.is_empty() && key.unwrap_or(EMPTY_STRING) != &engine.cfg.upload_key { return Err(StatusCode::FORBIDDEN); } @@ -40,7 +40,7 @@ pub async fn new( .unwrap_or_default() .to_string(); - let url = format!("{}/p/{}", engine.base_url, name); + let url = format!("{}/p/{}", engine.cfg.base_url, name); // read and parse content-length, and if it fails just assume it's really high so it doesn't cache let content_length = headers