use std::path::PathBuf; use clap::Parser; #[derive(Parser, Debug)] #[command( name = "filebrowser-ng", about = "A simple, noob-friendly web file browser", version )] pub struct Cli { /// Root folder the server has access to (required). /// All user folders and shares are relative to this. #[arg(long)] pub root: PathBuf, /// Display name of the root folder in the UI. Defaults to the folder's /// own name, which in a container is the mount point (e.g. "data"). #[arg(long, env = "FILEBROWSER_ROOT_NAME")] pub root_name: Option, /// Path to the SQLite database file (required). #[arg(long)] pub db: PathBuf, /// Port to listen on. #[arg(long, default_value_t = 8080)] pub port: u16, /// Address to bind. Use 0.0.0.0 to expose beyond localhost. #[arg(long, default_value = "127.0.0.1")] pub bind: String, /// Folder for the thumbnail cache. Without it thumbnails are off and /// the grid shows icons only. Entries not used for a day are swept. #[arg(long, env = "FILEBROWSER_CACHE")] pub cache: Option, /// Assume the server runs behind a TLS-terminating reverse proxy /// (sets the `Secure` attribute on the session cookie). /// The env form takes `true` or `false`. #[arg(long, env = "FILEBROWSER_HTTPS")] pub https: bool, /// Public base URL of this server, e.g. `https://files.example.com`. /// Share and WebDAV links in the UI are built from it. Without it they /// use whatever address the browser is connected to. #[arg(long, env = "FILEBROWSER_PUBLIC_URL")] pub public_url: Option, } #[cfg(test)] mod tests { use super::*; use clap::Parser; use std::path::PathBuf; /// `defaults_applied` asserts `!https`, which the env var would flip. static ENV_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(()); #[test] fn defaults_applied() { let _guard = ENV_LOCK.lock().unwrap(); let c = Cli::try_parse_from(["filebrowser-ng", "--root", "/r", "--db", "/d"]).unwrap(); assert_eq!(c.root, PathBuf::from("/r")); assert_eq!(c.db, PathBuf::from("/d")); assert_eq!(c.port, 8080); assert_eq!(c.bind, "127.0.0.1"); assert_eq!(c.cache, None); assert!(!c.https); } #[test] fn flags_override_defaults() { let c = Cli::try_parse_from([ "filebrowser-ng", "--root", "/r", "--db", "/d", "--port", "9000", "--bind", "0.0.0.0", "--https", ]) .unwrap(); assert_eq!(c.port, 9000); assert_eq!(c.bind, "0.0.0.0"); assert!(c.https); } #[test] fn https_from_env() { // Env-driven flags carry the daemon's whole config: the compose file // has no `command:` and must still be able to turn this on. let _guard = ENV_LOCK.lock().unwrap(); // clap accepts only these two literals for a flag read from the env. for (val, want) in [("true", true), ("false", false)] { // SAFETY: the process env is shared; ENV_LOCK serialises every test // that reads or writes it. unsafe { std::env::set_var("FILEBROWSER_HTTPS", val) }; let c = Cli::try_parse_from(["filebrowser-ng", "--root", "/r", "--db", "/d"]).unwrap(); assert_eq!(c.https, want, "FILEBROWSER_HTTPS={val}"); } unsafe { std::env::remove_var("FILEBROWSER_HTTPS") }; } #[test] fn required_args_enforced() { assert!(Cli::try_parse_from(["filebrowser-ng", "--root", "/r"]).is_err()); assert!(Cli::try_parse_from(["filebrowser-ng"]).is_err()); } }