2023-03-12 10:20:40 +03:00
|
|
|
use anyhow::{bail, Context, Result};
|
2022-11-11 16:46:07 +03:00
|
|
|
use clap::builder::PossibleValuesParser;
|
|
|
|
use clap::{value_parser, Arg, ArgAction, ArgMatches, Command};
|
2022-07-06 07:11:00 +03:00
|
|
|
use clap_complete::{generate, Generator, Shell};
|
2023-11-04 11:58:19 +03:00
|
|
|
use serde::{Deserialize, Deserializer};
|
2022-06-15 14:33:51 +03:00
|
|
|
use std::env;
|
|
|
|
use std::net::IpAddr;
|
2022-05-26 11:17:55 +03:00
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
|
2022-06-19 06:26:03 +03:00
|
|
|
use crate::auth::AccessControl;
|
2023-11-04 12:10:38 +03:00
|
|
|
use crate::http_logger::HttpLogger;
|
2022-07-08 14:30:05 +03:00
|
|
|
use crate::utils::encode_uri;
|
2022-05-26 11:17:55 +03:00
|
|
|
|
2022-11-11 16:46:07 +03:00
|
|
|
pub fn build_cli() -> Command {
|
2022-06-29 04:19:09 +03:00
|
|
|
let app = Command::new(env!("CARGO_CRATE_NAME"))
|
2022-06-15 14:33:51 +03:00
|
|
|
.version(env!("CARGO_PKG_VERSION"))
|
|
|
|
.author(env!("CARGO_PKG_AUTHORS"))
|
|
|
|
.about(concat!(
|
|
|
|
env!("CARGO_PKG_DESCRIPTION"),
|
|
|
|
" - ",
|
|
|
|
env!("CARGO_PKG_REPOSITORY")
|
|
|
|
))
|
2022-11-11 16:46:07 +03:00
|
|
|
.arg(
|
2023-11-04 11:58:19 +03:00
|
|
|
Arg::new("serve-path")
|
2023-05-30 11:49:16 +03:00
|
|
|
.env("DUFS_SERVE_PATH")
|
2023-02-19 17:48:41 +03:00
|
|
|
.hide_env(true)
|
2022-11-11 16:46:07 +03:00
|
|
|
.value_parser(value_parser!(PathBuf))
|
2023-11-04 11:58:19 +03:00
|
|
|
.help("Specific path to serve [default: .]"),
|
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::new("config")
|
|
|
|
.env("DUFS_SERVE_PATH")
|
|
|
|
.hide_env(true)
|
|
|
|
.short('c')
|
|
|
|
.long("config")
|
|
|
|
.value_parser(value_parser!(PathBuf))
|
|
|
|
.help("Specify configuration file"),
|
2022-11-11 16:46:07 +03:00
|
|
|
)
|
2022-05-29 12:01:30 +03:00
|
|
|
.arg(
|
2022-06-17 03:41:01 +03:00
|
|
|
Arg::new("bind")
|
2023-02-19 06:40:14 +03:00
|
|
|
.env("DUFS_BIND")
|
2023-02-19 17:48:41 +03:00
|
|
|
.hide_env(true)
|
2022-05-29 12:01:30 +03:00
|
|
|
.short('b')
|
|
|
|
.long("bind")
|
2022-11-11 03:57:44 +03:00
|
|
|
.help("Specify bind address or unix socket")
|
2022-08-27 05:30:08 +03:00
|
|
|
.action(ArgAction::Append)
|
2022-11-11 16:46:07 +03:00
|
|
|
.value_delimiter(',')
|
|
|
|
.value_name("addrs"),
|
2022-05-29 12:01:30 +03:00
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::new("port")
|
2023-02-19 06:40:14 +03:00
|
|
|
.env("DUFS_PORT")
|
2023-02-19 17:48:41 +03:00
|
|
|
.hide_env(true)
|
2022-05-29 12:01:30 +03:00
|
|
|
.short('p')
|
|
|
|
.long("port")
|
2022-11-11 16:46:07 +03:00
|
|
|
.value_parser(value_parser!(u16))
|
2023-11-04 11:58:19 +03:00
|
|
|
.help("Specify port to listen on [default: 5000]")
|
2022-05-29 12:01:30 +03:00
|
|
|
.value_name("port"),
|
|
|
|
)
|
2022-06-02 03:32:31 +03:00
|
|
|
.arg(
|
|
|
|
Arg::new("path-prefix")
|
2023-02-19 06:40:14 +03:00
|
|
|
.env("DUFS_PATH_PREFIX")
|
2023-02-19 17:48:41 +03:00
|
|
|
.hide_env(true)
|
2022-06-02 03:32:31 +03:00
|
|
|
.long("path-prefix")
|
|
|
|
.value_name("path")
|
2022-07-07 10:44:25 +03:00
|
|
|
.help("Specify a path prefix"),
|
2022-06-02 03:32:31 +03:00
|
|
|
)
|
2022-06-25 03:15:16 +03:00
|
|
|
.arg(
|
|
|
|
Arg::new("hidden")
|
2023-02-19 06:40:14 +03:00
|
|
|
.env("DUFS_HIDDEN")
|
2023-02-19 17:48:41 +03:00
|
|
|
.hide_env(true)
|
2022-06-25 03:15:16 +03:00
|
|
|
.long("hidden")
|
2023-11-04 11:58:19 +03:00
|
|
|
.help("Hide paths from directory listings, e.g. tmp,*.log,*.lock")
|
2022-06-25 04:57:58 +03:00
|
|
|
.value_name("value"),
|
2022-06-25 03:15:16 +03:00
|
|
|
)
|
2022-06-17 03:41:01 +03:00
|
|
|
.arg(
|
|
|
|
Arg::new("auth")
|
2023-02-19 06:40:14 +03:00
|
|
|
.env("DUFS_AUTH")
|
2023-02-19 17:48:41 +03:00
|
|
|
.hide_env(true)
|
2022-06-17 03:41:01 +03:00
|
|
|
.short('a')
|
|
|
|
.long("auth")
|
2023-11-03 16:08:05 +03:00
|
|
|
.help("Add auth roles, e.g. user:pass@/dir1:rw,/dir2")
|
2022-08-27 05:30:08 +03:00
|
|
|
.action(ArgAction::Append)
|
2023-06-01 13:52:05 +03:00
|
|
|
.value_delimiter('|')
|
2022-11-11 16:46:07 +03:00
|
|
|
.value_name("rules"),
|
2022-06-17 03:41:01 +03:00
|
|
|
)
|
2022-06-20 10:11:39 +03:00
|
|
|
.arg(
|
|
|
|
Arg::new("auth-method")
|
2023-11-03 15:36:23 +03:00
|
|
|
.hide(true)
|
2023-02-19 06:40:14 +03:00
|
|
|
.env("DUFS_AUTH_METHOD")
|
2023-02-19 17:48:41 +03:00
|
|
|
.hide_env(true)
|
2022-06-20 10:11:39 +03:00
|
|
|
.long("auth-method")
|
|
|
|
.help("Select auth method")
|
2022-11-11 16:46:07 +03:00
|
|
|
.value_parser(PossibleValuesParser::new(["basic", "digest"]))
|
2022-06-20 10:11:39 +03:00
|
|
|
.default_value("digest")
|
|
|
|
.value_name("value"),
|
|
|
|
)
|
2022-05-29 12:01:30 +03:00
|
|
|
.arg(
|
2022-05-31 03:38:30 +03:00
|
|
|
Arg::new("allow-all")
|
2023-02-19 06:40:14 +03:00
|
|
|
.env("DUFS_ALLOW_ALL")
|
2023-02-19 17:48:41 +03:00
|
|
|
.hide_env(true)
|
2022-05-31 03:38:30 +03:00
|
|
|
.short('A')
|
|
|
|
.long("allow-all")
|
2022-11-11 16:46:07 +03:00
|
|
|
.action(ArgAction::SetTrue)
|
2022-05-31 03:38:30 +03:00
|
|
|
.help("Allow all operations"),
|
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::new("allow-upload")
|
2023-02-19 06:40:14 +03:00
|
|
|
.env("DUFS_ALLOW_UPLOAD")
|
2023-02-19 17:48:41 +03:00
|
|
|
.hide_env(true)
|
2022-05-31 03:38:30 +03:00
|
|
|
.long("allow-upload")
|
2022-11-11 16:46:07 +03:00
|
|
|
.action(ArgAction::SetTrue)
|
2022-06-02 14:32:19 +03:00
|
|
|
.help("Allow upload files/folders"),
|
2022-05-31 03:38:30 +03:00
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::new("allow-delete")
|
2023-02-19 06:40:14 +03:00
|
|
|
.env("DUFS_ALLOW_DELETE")
|
2023-02-19 17:48:41 +03:00
|
|
|
.hide_env(true)
|
2022-05-31 17:38:00 +03:00
|
|
|
.long("allow-delete")
|
2022-11-11 16:46:07 +03:00
|
|
|
.action(ArgAction::SetTrue)
|
2022-06-02 14:32:19 +03:00
|
|
|
.help("Allow delete files/folders"),
|
2022-05-29 12:01:30 +03:00
|
|
|
)
|
2022-06-21 02:23:20 +03:00
|
|
|
.arg(
|
|
|
|
Arg::new("allow-search")
|
2023-02-19 06:40:14 +03:00
|
|
|
.env("DUFS_ALLOW_SEARCH")
|
2023-02-19 17:48:41 +03:00
|
|
|
.hide_env(true)
|
2022-06-21 02:23:20 +03:00
|
|
|
.long("allow-search")
|
2022-11-11 16:46:07 +03:00
|
|
|
.action(ArgAction::SetTrue)
|
2022-06-21 02:23:20 +03:00
|
|
|
.help("Allow search files/folders"),
|
|
|
|
)
|
2022-05-31 15:53:14 +03:00
|
|
|
.arg(
|
|
|
|
Arg::new("allow-symlink")
|
2023-02-19 06:40:14 +03:00
|
|
|
.env("DUFS_ALLOW_SYMLINK")
|
2023-02-19 17:48:41 +03:00
|
|
|
.hide_env(true)
|
2022-05-31 17:38:00 +03:00
|
|
|
.long("allow-symlink")
|
2022-11-11 16:46:07 +03:00
|
|
|
.action(ArgAction::SetTrue)
|
2022-06-02 14:32:19 +03:00
|
|
|
.help("Allow symlink to files/folders outside root directory"),
|
2022-05-31 15:53:14 +03:00
|
|
|
)
|
2022-12-10 06:09:42 +03:00
|
|
|
.arg(
|
|
|
|
Arg::new("allow-archive")
|
2023-02-19 06:40:14 +03:00
|
|
|
.env("DUFS_ALLOW_ARCHIVE")
|
2023-02-19 17:48:41 +03:00
|
|
|
.hide_env(true)
|
2022-12-10 06:09:42 +03:00
|
|
|
.long("allow-archive")
|
|
|
|
.action(ArgAction::SetTrue)
|
|
|
|
.help("Allow zip archive generation"),
|
|
|
|
)
|
2022-06-19 12:27:09 +03:00
|
|
|
.arg(
|
|
|
|
Arg::new("enable-cors")
|
2023-02-19 06:40:14 +03:00
|
|
|
.env("DUFS_ENABLE_CORS")
|
2023-02-19 17:48:41 +03:00
|
|
|
.hide_env(true)
|
2022-06-19 12:27:09 +03:00
|
|
|
.long("enable-cors")
|
2022-11-11 16:46:07 +03:00
|
|
|
.action(ArgAction::SetTrue)
|
2022-06-19 12:27:09 +03:00
|
|
|
.help("Enable CORS, sets `Access-Control-Allow-Origin: *`"),
|
|
|
|
)
|
2022-06-01 17:49:55 +03:00
|
|
|
.arg(
|
|
|
|
Arg::new("render-index")
|
2023-02-19 06:40:14 +03:00
|
|
|
.env("DUFS_RENDER_INDEX")
|
2023-02-19 17:48:41 +03:00
|
|
|
.hide_env(true)
|
2022-06-01 17:49:55 +03:00
|
|
|
.long("render-index")
|
2022-11-11 16:46:07 +03:00
|
|
|
.action(ArgAction::SetTrue)
|
2022-06-20 14:40:09 +03:00
|
|
|
.help("Serve index.html when requesting a directory, returns 404 if not found index.html"),
|
2022-06-01 17:49:55 +03:00
|
|
|
)
|
2022-06-17 03:41:01 +03:00
|
|
|
.arg(
|
|
|
|
Arg::new("render-try-index")
|
2023-02-19 06:40:14 +03:00
|
|
|
.env("DUFS_RENDER_TRY_INDEX")
|
2023-02-19 17:48:41 +03:00
|
|
|
.hide_env(true)
|
2022-06-17 03:41:01 +03:00
|
|
|
.long("render-try-index")
|
2022-11-11 16:46:07 +03:00
|
|
|
.action(ArgAction::SetTrue)
|
2022-06-25 03:15:16 +03:00
|
|
|
.help("Serve index.html when requesting a directory, returns directory listing if not found index.html"),
|
2022-06-17 03:41:01 +03:00
|
|
|
)
|
2022-06-01 17:49:55 +03:00
|
|
|
.arg(
|
|
|
|
Arg::new("render-spa")
|
2023-02-19 06:40:14 +03:00
|
|
|
.env("DUFS_RENDER_SPA")
|
2023-02-19 17:48:41 +03:00
|
|
|
.hide_env(true)
|
2022-06-01 17:49:55 +03:00
|
|
|
.long("render-spa")
|
2022-11-11 16:46:07 +03:00
|
|
|
.action(ArgAction::SetTrue)
|
2022-06-20 14:40:09 +03:00
|
|
|
.help("Serve SPA(Single Page Application)"),
|
2022-09-05 05:30:45 +03:00
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::new("assets")
|
2023-02-19 06:40:14 +03:00
|
|
|
.env("DUFS_ASSETS")
|
2023-02-19 17:48:41 +03:00
|
|
|
.hide_env(true)
|
2022-09-05 05:30:45 +03:00
|
|
|
.long("assets")
|
2023-11-04 11:58:19 +03:00
|
|
|
.help("Set the path to the assets directory for overriding the built-in assets")
|
2022-11-11 16:46:07 +03:00
|
|
|
.value_parser(value_parser!(PathBuf))
|
2022-09-05 05:30:45 +03:00
|
|
|
.value_name("path")
|
2023-11-04 11:58:19 +03:00
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::new("log-format")
|
|
|
|
.env("DUFS_LOG_FORMAT")
|
|
|
|
.hide_env(true)
|
|
|
|
.long("log-format")
|
|
|
|
.value_name("format")
|
|
|
|
.help("Customize http log format"),
|
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::new("completions")
|
|
|
|
.long("completions")
|
|
|
|
.value_name("shell")
|
|
|
|
.value_parser(value_parser!(Shell))
|
|
|
|
.help("Print shell completion script for <shell>"),
|
2022-06-29 04:19:09 +03:00
|
|
|
);
|
|
|
|
|
|
|
|
#[cfg(feature = "tls")]
|
|
|
|
let app = app
|
2022-06-02 06:06:41 +03:00
|
|
|
.arg(
|
|
|
|
Arg::new("tls-cert")
|
2023-02-19 06:40:14 +03:00
|
|
|
.env("DUFS_TLS_CERT")
|
2023-02-19 17:48:41 +03:00
|
|
|
.hide_env(true)
|
2022-06-02 06:06:41 +03:00
|
|
|
.long("tls-cert")
|
|
|
|
.value_name("path")
|
2022-11-11 16:46:07 +03:00
|
|
|
.value_parser(value_parser!(PathBuf))
|
2022-06-02 06:06:41 +03:00
|
|
|
.help("Path to an SSL/TLS certificate to serve with HTTPS"),
|
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::new("tls-key")
|
2023-02-19 06:40:14 +03:00
|
|
|
.env("DUFS_TLS_KEY")
|
2023-02-19 17:48:41 +03:00
|
|
|
.hide_env(true)
|
2022-06-02 06:06:41 +03:00
|
|
|
.long("tls-key")
|
|
|
|
.value_name("path")
|
2022-11-11 16:46:07 +03:00
|
|
|
.value_parser(value_parser!(PathBuf))
|
2022-06-02 06:06:41 +03:00
|
|
|
.help("Path to the SSL/TLS certificate's private key"),
|
2022-06-29 04:19:09 +03:00
|
|
|
);
|
|
|
|
|
2023-11-04 11:58:19 +03:00
|
|
|
app
|
2022-05-26 11:17:55 +03:00
|
|
|
}
|
|
|
|
|
2022-07-06 07:11:00 +03:00
|
|
|
pub fn print_completions<G: Generator>(gen: G, cmd: &mut Command) {
|
|
|
|
generate(gen, cmd, cmd.get_name().to_string(), &mut std::io::stdout());
|
2022-05-26 11:17:55 +03:00
|
|
|
}
|
|
|
|
|
2023-11-04 11:58:19 +03:00
|
|
|
#[derive(Debug, Deserialize, Default)]
|
|
|
|
#[serde(default)]
|
|
|
|
#[serde(rename_all = "kebab-case")]
|
2022-05-26 11:17:55 +03:00
|
|
|
pub struct Args {
|
2023-11-04 11:58:19 +03:00
|
|
|
#[serde(default = "default_serve_path")]
|
|
|
|
pub serve_path: PathBuf,
|
|
|
|
#[serde(deserialize_with = "deserialize_bind_addrs")]
|
|
|
|
#[serde(rename = "bind")]
|
2022-11-11 03:57:44 +03:00
|
|
|
pub addrs: Vec<BindAddr>,
|
2022-06-15 14:33:51 +03:00
|
|
|
pub port: u16,
|
2023-11-04 11:58:19 +03:00
|
|
|
#[serde(skip)]
|
2022-06-19 09:23:10 +03:00
|
|
|
pub path_is_file: bool,
|
2022-06-04 14:08:18 +03:00
|
|
|
pub path_prefix: String,
|
2023-11-04 11:58:19 +03:00
|
|
|
#[serde(skip)]
|
2022-06-04 14:08:18 +03:00
|
|
|
pub uri_prefix: String,
|
2022-07-19 15:37:14 +03:00
|
|
|
pub hidden: Vec<String>,
|
2023-11-04 11:58:19 +03:00
|
|
|
#[serde(deserialize_with = "deserialize_access_control")]
|
2022-06-19 06:26:03 +03:00
|
|
|
pub auth: AccessControl,
|
2023-11-04 11:58:19 +03:00
|
|
|
pub allow_all: bool,
|
2022-05-31 03:38:30 +03:00
|
|
|
pub allow_upload: bool,
|
|
|
|
pub allow_delete: bool,
|
2022-06-21 02:23:20 +03:00
|
|
|
pub allow_search: bool,
|
2022-05-31 15:53:14 +03:00
|
|
|
pub allow_symlink: bool,
|
2022-12-10 06:09:42 +03:00
|
|
|
pub allow_archive: bool,
|
2022-06-01 17:49:55 +03:00
|
|
|
pub render_index: bool,
|
|
|
|
pub render_spa: bool,
|
2022-06-17 14:02:13 +03:00
|
|
|
pub render_try_index: bool,
|
2022-06-19 12:27:09 +03:00
|
|
|
pub enable_cors: bool,
|
2023-11-04 11:58:19 +03:00
|
|
|
pub assets: Option<PathBuf>,
|
|
|
|
#[serde(deserialize_with = "deserialize_log_http")]
|
|
|
|
#[serde(rename = "log-format")]
|
2023-11-04 12:10:38 +03:00
|
|
|
pub http_logger: HttpLogger,
|
2023-11-04 11:58:19 +03:00
|
|
|
pub tls_cert: Option<PathBuf>,
|
|
|
|
pub tls_key: Option<PathBuf>,
|
2022-05-26 11:17:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Args {
|
|
|
|
/// Parse command-line arguments.
|
|
|
|
///
|
2022-12-10 04:18:54 +03:00
|
|
|
/// If a parsing error occurred, exit the process and print out informative
|
2022-05-26 11:17:55 +03:00
|
|
|
/// error message to user.
|
2023-02-21 12:23:24 +03:00
|
|
|
pub fn parse(matches: ArgMatches) -> Result<Args> {
|
2023-11-04 11:58:19 +03:00
|
|
|
let mut args = Self {
|
|
|
|
serve_path: default_serve_path(),
|
|
|
|
addrs: BindAddr::parse_addrs(&["0.0.0.0", "::"]).unwrap(),
|
|
|
|
port: 5000,
|
|
|
|
..Default::default()
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Some(config_path) = matches.get_one::<PathBuf>("config") {
|
|
|
|
let contents = std::fs::read_to_string(config_path)
|
|
|
|
.with_context(|| format!("Failed to read config at {}", config_path.display()))?;
|
|
|
|
args = serde_yaml::from_str(&contents)
|
|
|
|
.with_context(|| format!("Failed to load config at {}", config_path.display()))?;
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(path) = matches.get_one::<PathBuf>("serve-path") {
|
|
|
|
args.serve_path = path.clone()
|
|
|
|
}
|
|
|
|
args.serve_path = Self::sanitize_path(args.serve_path)?;
|
|
|
|
|
|
|
|
if let Some(port) = matches.get_one::<u16>("port") {
|
|
|
|
args.port = *port
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(addrs) = matches.get_many::<String>("bind") {
|
|
|
|
let addrs: Vec<_> = addrs.map(|v| v.as_str()).collect();
|
|
|
|
args.addrs = BindAddr::parse_addrs(&addrs)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
args.path_is_file = args.serve_path.metadata()?.is_file();
|
|
|
|
if let Some(path_prefix) = matches.get_one::<String>("path-prefix") {
|
|
|
|
args.path_prefix = path_prefix.clone();
|
|
|
|
}
|
|
|
|
args.path_prefix = args.path_prefix.trim_matches('/').to_string();
|
|
|
|
|
|
|
|
args.uri_prefix = if args.path_prefix.is_empty() {
|
2022-06-04 14:08:18 +03:00
|
|
|
"/".to_owned()
|
|
|
|
} else {
|
2023-11-04 11:58:19 +03:00
|
|
|
format!("/{}/", &encode_uri(&args.path_prefix))
|
2022-06-04 14:08:18 +03:00
|
|
|
};
|
2023-11-04 11:58:19 +03:00
|
|
|
|
|
|
|
if let Some(hidden) = matches
|
2022-11-11 16:46:07 +03:00
|
|
|
.get_one::<String>("hidden")
|
2022-09-09 16:30:27 +03:00
|
|
|
.map(|v| v.split(',').map(|x| x.to_string()).collect())
|
2023-11-04 11:58:19 +03:00
|
|
|
{
|
|
|
|
args.hidden = hidden;
|
|
|
|
}
|
|
|
|
|
|
|
|
if !args.enable_cors {
|
|
|
|
args.enable_cors = matches.get_flag("enable-cors");
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(rules) = matches.get_many::<String>("auth") {
|
|
|
|
let rules: Vec<_> = rules.map(|v| v.as_str()).collect();
|
|
|
|
args.auth = AccessControl::new(&rules)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
if !args.allow_all {
|
|
|
|
args.allow_all = matches.get_flag("allow-all");
|
|
|
|
}
|
|
|
|
|
|
|
|
let allow_all = args.allow_all;
|
|
|
|
|
|
|
|
if !args.allow_upload {
|
|
|
|
args.allow_upload = allow_all || matches.get_flag("allow-upload");
|
|
|
|
}
|
|
|
|
if !args.allow_delete {
|
|
|
|
args.allow_delete = allow_all || matches.get_flag("allow-delete");
|
|
|
|
}
|
|
|
|
if !args.allow_search {
|
|
|
|
args.allow_search = allow_all || matches.get_flag("allow-search");
|
|
|
|
}
|
|
|
|
if !args.allow_symlink {
|
|
|
|
args.allow_symlink = allow_all || matches.get_flag("allow-symlink");
|
|
|
|
}
|
|
|
|
if !args.allow_archive {
|
|
|
|
args.allow_archive = allow_all || matches.get_flag("allow-archive");
|
|
|
|
}
|
|
|
|
if !args.render_index {
|
|
|
|
args.render_index = matches.get_flag("render-index");
|
|
|
|
}
|
|
|
|
|
|
|
|
if !args.render_try_index {
|
|
|
|
args.render_try_index = matches.get_flag("render-try-index");
|
|
|
|
}
|
|
|
|
|
|
|
|
if !args.render_spa {
|
|
|
|
args.render_spa = matches.get_flag("render-spa");
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(log_format) = matches.get_one::<String>("log-format") {
|
2023-11-04 12:10:38 +03:00
|
|
|
args.http_logger = log_format.parse()?;
|
2023-11-04 11:58:19 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(assets_path) = matches.get_one::<PathBuf>("assets") {
|
|
|
|
args.assets = Some(assets_path.clone());
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(assets_path) = &args.assets {
|
|
|
|
args.assets = Some(Args::sanitize_assets_path(assets_path)?);
|
|
|
|
}
|
|
|
|
|
2022-06-29 04:19:09 +03:00
|
|
|
#[cfg(feature = "tls")]
|
2023-11-04 11:58:19 +03:00
|
|
|
{
|
|
|
|
if let Some(tls_cert) = matches.get_one::<PathBuf>("tls-cert") {
|
|
|
|
args.tls_cert = Some(tls_cert.clone())
|
2022-06-02 06:06:41 +03:00
|
|
|
}
|
2022-05-26 11:17:55 +03:00
|
|
|
|
2023-11-04 11:58:19 +03:00
|
|
|
if let Some(tls_key) = matches.get_one::<PathBuf>("tls-key") {
|
|
|
|
args.tls_key = Some(tls_key.clone())
|
|
|
|
}
|
2022-05-26 11:17:55 +03:00
|
|
|
|
2023-11-04 11:58:19 +03:00
|
|
|
match (&args.tls_cert, &args.tls_key) {
|
|
|
|
(Some(_), Some(_)) => {}
|
|
|
|
(Some(_), _) => bail!("No tls-key set"),
|
|
|
|
(_, Some(_)) => bail!("No tls-cert set"),
|
|
|
|
(None, None) => {}
|
2022-06-15 14:33:51 +03:00
|
|
|
}
|
|
|
|
}
|
2023-11-04 11:58:19 +03:00
|
|
|
#[cfg(not(feature = "tls"))]
|
|
|
|
{
|
|
|
|
args.tls_cert = None;
|
|
|
|
args.tls_key = None;
|
2022-06-15 14:33:51 +03:00
|
|
|
}
|
2023-11-04 11:58:19 +03:00
|
|
|
|
|
|
|
Ok(args)
|
2022-06-15 14:33:51 +03:00
|
|
|
}
|
|
|
|
|
2023-11-04 11:58:19 +03:00
|
|
|
fn sanitize_path<P: AsRef<Path>>(path: P) -> Result<PathBuf> {
|
2022-05-26 11:17:55 +03:00
|
|
|
let path = path.as_ref();
|
|
|
|
if !path.exists() {
|
2023-02-21 12:23:24 +03:00
|
|
|
bail!("Path `{}` doesn't exist", path.display());
|
2022-05-26 11:17:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
env::current_dir()
|
|
|
|
.and_then(|mut p| {
|
|
|
|
p.push(path); // If path is absolute, it replaces the current path.
|
2022-05-31 17:38:00 +03:00
|
|
|
std::fs::canonicalize(p)
|
2022-05-26 11:17:55 +03:00
|
|
|
})
|
2023-03-12 10:20:40 +03:00
|
|
|
.with_context(|| format!("Failed to access path `{}`", path.display()))
|
2022-05-26 11:17:55 +03:00
|
|
|
}
|
2022-09-05 05:30:45 +03:00
|
|
|
|
2023-11-04 11:58:19 +03:00
|
|
|
fn sanitize_assets_path<P: AsRef<Path>>(path: P) -> Result<PathBuf> {
|
|
|
|
let path = Self::sanitize_path(path)?;
|
2022-09-05 05:30:45 +03:00
|
|
|
if !path.join("index.html").exists() {
|
2023-02-21 12:23:24 +03:00
|
|
|
bail!("Path `{}` doesn't contains index.html", path.display());
|
2022-09-05 05:30:45 +03:00
|
|
|
}
|
|
|
|
Ok(path)
|
|
|
|
}
|
2022-06-06 05:52:12 +03:00
|
|
|
}
|
2022-11-11 03:57:44 +03:00
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
|
|
|
pub enum BindAddr {
|
|
|
|
Address(IpAddr),
|
|
|
|
Path(PathBuf),
|
|
|
|
}
|
2023-11-04 11:58:19 +03:00
|
|
|
|
|
|
|
impl BindAddr {
|
|
|
|
fn parse_addrs(addrs: &[&str]) -> Result<Vec<Self>> {
|
|
|
|
let mut bind_addrs = vec![];
|
|
|
|
let mut invalid_addrs = vec![];
|
|
|
|
for addr in addrs {
|
|
|
|
match addr.parse::<IpAddr>() {
|
|
|
|
Ok(v) => {
|
|
|
|
bind_addrs.push(BindAddr::Address(v));
|
|
|
|
}
|
|
|
|
Err(_) => {
|
|
|
|
if cfg!(unix) {
|
|
|
|
bind_addrs.push(BindAddr::Path(PathBuf::from(addr)));
|
|
|
|
} else {
|
|
|
|
invalid_addrs.push(*addr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !invalid_addrs.is_empty() {
|
|
|
|
bail!("Invalid bind address `{}`", invalid_addrs.join(","));
|
|
|
|
}
|
|
|
|
Ok(bind_addrs)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn deserialize_bind_addrs<'de, D>(deserializer: D) -> Result<Vec<BindAddr>, D::Error>
|
|
|
|
where
|
|
|
|
D: Deserializer<'de>,
|
|
|
|
{
|
|
|
|
let addrs: Vec<&str> = Vec::deserialize(deserializer)?;
|
|
|
|
BindAddr::parse_addrs(&addrs).map_err(serde::de::Error::custom)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn deserialize_access_control<'de, D>(deserializer: D) -> Result<AccessControl, D::Error>
|
|
|
|
where
|
|
|
|
D: Deserializer<'de>,
|
|
|
|
{
|
|
|
|
let rules: Vec<&str> = Vec::deserialize(deserializer)?;
|
|
|
|
AccessControl::new(&rules).map_err(serde::de::Error::custom)
|
|
|
|
}
|
|
|
|
|
2023-11-04 12:10:38 +03:00
|
|
|
fn deserialize_log_http<'de, D>(deserializer: D) -> Result<HttpLogger, D::Error>
|
2023-11-04 11:58:19 +03:00
|
|
|
where
|
|
|
|
D: Deserializer<'de>,
|
|
|
|
{
|
|
|
|
let value: String = Deserialize::deserialize(deserializer)?;
|
|
|
|
value.parse().map_err(serde::de::Error::custom)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn default_serve_path() -> PathBuf {
|
|
|
|
PathBuf::from(".")
|
|
|
|
}
|