cli.rs
⎇
Raw
1use std::path::PathBuf;
2
3use clap::Parser;
4
5#[derive(Parser, Debug)]
6#[command(
7 name = "filebrowser-ng",
8 about = "A simple, noob-friendly web file browser",
9 version
10)]
11pub struct Cli {
12 /// Root folder the server has access to (required).
13 /// All user folders and shares are relative to this.
14 #[arg(long)]
15 pub root: PathBuf,
16
17 /// Display name of the root folder in the UI. Defaults to the folder's
18 /// own name, which in a container is the mount point (e.g. "data").
19 #[arg(long, env = "FILEBROWSER_ROOT_NAME")]
20 pub root_name: Option<String>,
21
22 /// Path to the SQLite database file (required).
23 #[arg(long)]
24 pub db: PathBuf,
25
26 /// Port to listen on.
27 #[arg(long, default_value_t = 8080)]
28 pub port: u16,
29
30 /// Address to bind. Use 0.0.0.0 to expose beyond localhost.
31 #[arg(long, default_value = "127.0.0.1")]
32 pub bind: String,
33
34 /// Folder for the thumbnail cache. Without it thumbnails are off and
35 /// the grid shows icons only. Entries not used for a day are swept.
36 #[arg(long, env = "FILEBROWSER_CACHE")]
37 pub cache: Option<PathBuf>,
38
39 /// Assume the server runs behind a TLS-terminating reverse proxy
40 /// (sets the `Secure` attribute on the session cookie).
41 /// The env form takes `true` or `false`.
42 #[arg(long, env = "FILEBROWSER_HTTPS")]
43 pub https: bool,
44
45 /// Public base URL of this server, e.g. `https://files.example.com`.
46 /// Share and WebDAV links in the UI are built from it. Without it they
47 /// use whatever address the browser is connected to.
48 #[arg(long, env = "FILEBROWSER_PUBLIC_URL")]
49 pub public_url: Option<String>,
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55 use clap::Parser;
56 use std::path::PathBuf;
57
58 /// `defaults_applied` asserts `!https`, which the env var would flip.
59 static ENV_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
60
61 #[test]
62 fn defaults_applied() {
63 let _guard = ENV_LOCK.lock().unwrap();
64 let c = Cli::try_parse_from(["filebrowser-ng", "--root", "/r", "--db", "/d"]).unwrap();
65 assert_eq!(c.root, PathBuf::from("/r"));
66 assert_eq!(c.db, PathBuf::from("/d"));
67 assert_eq!(c.port, 8080);
68 assert_eq!(c.bind, "127.0.0.1");
69 assert_eq!(c.cache, None);
70 assert!(!c.https);
71 }
72
73 #[test]
74 fn flags_override_defaults() {
75 let c = Cli::try_parse_from([
76 "filebrowser-ng",
77 "--root",
78 "/r",
79 "--db",
80 "/d",
81 "--port",
82 "9000",
83 "--bind",
84 "0.0.0.0",
85 "--https",
86 ])
87 .unwrap();
88 assert_eq!(c.port, 9000);
89 assert_eq!(c.bind, "0.0.0.0");
90 assert!(c.https);
91 }
92
93 #[test]
94 fn https_from_env() {
95 // Env-driven flags carry the daemon's whole config: the compose file
96 // has no `command:` and must still be able to turn this on.
97 let _guard = ENV_LOCK.lock().unwrap();
98 // clap accepts only these two literals for a flag read from the env.
99 for (val, want) in [("true", true), ("false", false)] {
100 // SAFETY: the process env is shared; ENV_LOCK serialises every test
101 // that reads or writes it.
102 unsafe { std::env::set_var("FILEBROWSER_HTTPS", val) };
103 let c = Cli::try_parse_from(["filebrowser-ng", "--root", "/r", "--db", "/d"]).unwrap();
104 assert_eq!(c.https, want, "FILEBROWSER_HTTPS={val}");
105 }
106 unsafe { std::env::remove_var("FILEBROWSER_HTTPS") };
107 }
108
109 #[test]
110 fn required_args_enforced() {
111 assert!(Cli::try_parse_from(["filebrowser-ng", "--root", "/r"]).is_err());
112 assert!(Cli::try_parse_from(["filebrowser-ng"]).is_err());
113 }
114}
115