add support for yaml files

feature/yaml-config
Edward Shen 2021-07-09 19:14:53 -04:00
parent e78315025d
commit ce03ce0baf
Signed by: edward
GPG Key ID: 19182661E818369F
5 changed files with 147 additions and 67 deletions

View File

@ -18,6 +18,8 @@
# MangaDex@Home configuration file
# We are pleased to have you here
# May fate stay the night with you!
#
# Default values are commented out.
# The size in mebibytes of the cache
# You can use megabytes instead in a pinch,
@ -25,37 +27,31 @@
max_cache_size_in_mebibytes: 0
server_settings:
# The client secret
# Keep this secret at all costs :P
secret: ina_is_the_cutest
# The client secret. Keep this secret at all costs :P
secret: suichan wa kyou mo kawaii!
# The port for the webserver to listen on
# 443 is recommended for maximum appeal
port: 443
# The port for the webserver to listen on. 443 is recommended for max appeal.
# port: 443
# This controls the value the server receives
# for your upload speed
# This controls the value the server receives for your upload speed.
external_max_kilobits_per_second: 0
#
# Stuff that you probably don't need to change
# Advanced settings
#
# The external port to broadcast to the backend
# Keep this at 0 unless you know what you're doing
# 0 means broadcast the same value as `port`
external_port: 0
# The external hostname to listen on. Keep this at 0.0.0.0 unless you know
# what you're doing.
# hostname: 0.0.0.0
# How long to wait for the graceful shutdown (Ctrl-C or SIGINT)
# This is rounded to a multiple of 5 seconds
graceful_shutdown_wait_seconds: 60
# The external port to broadcast to the backend. Keep this at 0 unless you
# know what you're doing. 0 means broadcast the same value as `port`.
# external_port: 0
# The external hostname to listen on
# Keep this at 0.0.0.0 unless you know what you're doing
hostname: 0.0.0.0
# How long to wait at most for the graceful shutdown (Ctrl-C or SIGINT).
# graceful_shutdown_wait_seconds: 60
# The external ip to broadcast to the webserver
# The default of null means the backend will infer it
# from where it was sent from, which may fail in the
# presence of multiple IPs
external_ip: ~
# The external ip to broadcast to the webserver. The default of null (~) means
# the backend will infer it from where it was sent from, which may fail in the
# presence of multiple IPs.
# external_ip: ~

6
src/cache/fs.rs vendored
View File

@ -111,21 +111,21 @@ pub(super) async fn read_file(
return None;
}
let header = if let Some(header) = Header::from_slice(&header_bytes) {
let file_header = if let Some(header) = Header::from_slice(&header_bytes) {
header
} else {
warn!("Found file, but encrypted header was invalid. Assuming corrupted!");
return None;
};
let secret_stream = if let Ok(stream) = SecretStream::init_pull(&header, key) {
let secret_stream = if let Ok(stream) = SecretStream::init_pull(&file_header, key) {
stream
} else {
warn!("Failed to init secret stream with key and header. Assuming corrupted!");
return None;
};
maybe_header = Some(header);
maybe_header = Some(file_header);
reader = Some(Box::pin(EncryptedDiskReader::new(file, secret_stream)));
}

View File

@ -2,8 +2,8 @@ use std::fmt::{Display, Formatter};
use std::fs::{File, OpenOptions};
use std::hint::unreachable_unchecked;
use std::io::{ErrorKind, Write};
use std::net::{IpAddr, SocketAddr};
use std::num::{NonZeroU16, NonZeroU64};
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::num::NonZeroU16;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::sync::atomic::{AtomicBool, Ordering};
@ -64,7 +64,7 @@ pub fn load_config() -> Result<Config, serde_yaml::Error> {
Ok(config)
}
/// Represents a fully parsed config file.
/// Represents a fully parsed config, from a variety of sources.
pub struct Config {
pub cache_type: CacheType,
pub cache_path: PathBuf,
@ -75,27 +75,93 @@ pub struct Config {
pub bind_address: SocketAddr,
pub external_address: Option<SocketAddr>,
pub ephemeral_disk_encryption: bool,
pub unstable_options: Vec<UnstableOptions>,
pub network_speed: KilobitsPerSecond,
pub disk_quota: Mebibytes,
pub memory_quota: Mebibytes,
pub unstable_options: Vec<UnstableOptions>,
pub override_upstream: Option<Url>,
pub enable_metrics: bool,
}
impl Config {
fn from_cli_and_file(cli_args: CliArgs, file_args: YamlArgs) -> Self {
let file_extended_options = file_args.extended_options.unwrap_or_default();
let log_level = match (cli_args.quiet, cli_args.verbose) {
(n, _) if n > 2 => LevelFilter::Off,
(2, _) => LevelFilter::Error,
(1, _) => LevelFilter::Warn,
// Use log level from file if no flags were provided to CLI
(0, 0) => file_args.extended_options.logging_level,
(0, 0) => file_extended_options
.logging_level
.unwrap_or(LevelFilter::Info),
(_, 1) => LevelFilter::Debug,
(_, n) if n > 1 => LevelFilter::Trace,
// compiler can't figure it out
_ => unsafe { unreachable_unchecked() },
};
todo!()
let bind_port = cli_args
.port
.unwrap_or(file_args.server_settings.port)
.get();
// This needs to be outside because rust isn't smart enough yet to
// realize a disjointed borrow of a moved value is ok. This will be
// fixed in Rust 2021.
let external_port = file_args
.server_settings
.external_port
.map_or(bind_port, Port::get);
Self {
cache_type: cli_args
.cache_type
.or(file_extended_options.cache_type)
.unwrap_or_default(),
cache_path: cli_args
.cache_path
.or(file_extended_options.cache_path)
.unwrap_or_else(|| PathBuf::from_str("./cache").unwrap()),
shutdown_timeout: file_args
.server_settings
.graceful_shutdown_wait_seconds
.unwrap_or(unsafe { NonZeroU16::new_unchecked(60) }),
log_level,
// secret should never be in CLI
client_secret: file_args.server_settings.secret,
port: cli_args.port.unwrap_or(file_args.server_settings.port),
bind_address: SocketAddr::new(
file_args
.server_settings
.hostname
.unwrap_or_else(|| IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))),
bind_port,
),
external_address: file_args
.server_settings
.external_ip
.map(|ip_addr| SocketAddr::new(ip_addr, external_port)),
ephemeral_disk_encryption: cli_args
.ephemeral_disk_encryption
.or(file_extended_options.ephemeral_disk_encryption)
.unwrap_or_default(),
network_speed: cli_args
.network_speed
.unwrap_or(file_args.server_settings.external_max_kilobits_per_second),
disk_quota: cli_args
.disk_quota
.unwrap_or(file_args.max_cache_size_in_mebibytes),
memory_quota: cli_args
.memory_quota
.or(file_extended_options.memory_quota)
.unwrap_or_default(),
enable_metrics: file_extended_options.enable_metrics.unwrap_or_default(),
// Unstable options (and related) should never be in yaml config
unstable_options: cli_args.unstable_options,
override_upstream: cli_args.override_upstream,
}
}
}
@ -105,13 +171,14 @@ struct YamlArgs {
max_cache_size_in_mebibytes: Mebibytes,
server_settings: YamlServerSettings,
// This implementation custom options
extended_options: YamlExtendedOptions,
extended_options: Option<YamlExtendedOptions>,
}
// Naming is legacy
#[derive(Deserialize)]
struct YamlServerSettings {
secret: ClientSecret,
#[serde(default)]
port: Port,
external_max_kilobits_per_second: KilobitsPerSecond,
external_port: Option<Port>,
@ -124,21 +191,14 @@ struct YamlServerSettings {
#[derive(Deserialize, Serialize)]
pub struct ClientSecret(String);
#[derive(Deserialize)]
#[derive(Deserialize, Default)]
struct YamlExtendedOptions {
memory_quota: Option<NonZeroU64>,
#[serde(default)]
cache_type: CacheType,
#[serde(default)]
ephemeral_disk_encryption: bool,
#[serde(default)]
enable_metrics: bool,
#[serde(default = "default_logging_level")]
logging_level: LevelFilter,
}
const fn default_logging_level() -> LevelFilter {
LevelFilter::Info
memory_quota: Option<Mebibytes>,
cache_type: Option<CacheType>,
ephemeral_disk_encryption: Option<bool>,
enable_metrics: Option<bool>,
logging_level: Option<LevelFilter>,
cache_path: Option<PathBuf>,
}
#[derive(Deserialize, Copy, Clone)]
@ -172,25 +232,25 @@ impl Default for CacheType {
#[clap(version = crate_version!(), author = crate_authors!(), about = crate_description!())]
struct CliArgs {
/// The port to listen on.
#[clap(short, long, default_value = "42069")]
pub port: Port,
/// How large, in bytes, the in-memory cache should be. Note that this does
/// not include runtime memory usage.
#[clap(short, long)]
pub port: Option<Port>,
/// How large, in mebibytes, the in-memory cache should be. Note that this
/// does not include runtime memory usage.
#[clap(long)]
pub memory_quota: Option<NonZeroU64>,
/// How large, in bytes, the on-disk cache should be. Note that actual
pub memory_quota: Option<Mebibytes>,
/// How large, in mebibytes, the on-disk cache should be. Note that actual
/// values may be larger for metadata information.
#[clap(long)]
pub disk_quota: u64,
pub disk_quota: Option<Mebibytes>,
/// Sets the location of the disk cache.
#[clap(long, default_value = "./cache")]
pub cache_path: PathBuf,
#[clap(long)]
pub cache_path: Option<PathBuf>,
/// The network speed to advertise to Mangadex@Home control server.
#[clap(long)]
pub network_speed: NonZeroU64,
pub network_speed: Option<KilobitsPerSecond>,
/// Changes verbosity. Default verbosity is INFO, while increasing counts of
/// verbose flags increases the verbosity to DEBUG and TRACE, respectively.
#[clap(short, long, parse(from_occurrences))]
#[clap(short, long, parse(from_occurrences), conflicts_with = "quiet")]
pub verbose: usize,
/// Changes verbosity. Default verbosity is INFO, while increasing counts of
/// quiet flags decreases the verbosity to WARN, ERROR, and no logs,
@ -205,11 +265,11 @@ struct CliArgs {
/// encrypted with a key generated at runtime. There are implications to
/// performance, privacy, and usability with this flag enabled.
#[clap(short, long)]
pub ephemeral_disk_encryption: bool,
pub ephemeral_disk_encryption: Option<bool>,
#[clap(short, long)]
pub config_path: Option<PathBuf>,
#[clap(default_value = "on_disk")]
pub cache_type: CacheType,
#[clap(short, long)]
pub cache_type: Option<CacheType>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]

View File

@ -93,7 +93,9 @@ async fn main() -> Result<(), Box<dyn Error>> {
ENCRYPTION_KEY.set(gen_key()).unwrap();
}
metrics::init();
if config.enable_metrics {
metrics::init();
}
// HTTP Server init

View File

@ -1,5 +1,5 @@
use std::fmt::Display;
use std::num::{NonZeroU16, NonZeroU64};
use std::num::{NonZeroU16, NonZeroU64, ParseIntError};
use std::str::FromStr;
use serde::{Deserialize, Serialize};
@ -8,6 +8,18 @@ use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub struct Port(NonZeroU16);
impl Port {
pub const fn get(self) -> u16 {
self.0.get()
}
}
impl Default for Port {
fn default() -> Self {
Self(unsafe { NonZeroU16::new_unchecked(443) })
}
}
impl FromStr for Port {
type Err = <NonZeroU16 as FromStr>::Err;
@ -25,9 +37,11 @@ impl Display for Port {
#[derive(Copy, Clone, Serialize, Deserialize, Default, Debug, Hash, Eq, PartialEq)]
pub struct Mebibytes(usize);
impl Mebibytes {
pub const fn get(self) -> usize {
self.0
impl FromStr for Mebibytes {
type Err = ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.parse::<usize>().map(Self)
}
}
@ -48,6 +62,14 @@ impl From<Mebibytes> for Bytes {
#[derive(Copy, Clone, Deserialize, Debug, Hash, Eq, PartialEq)]
pub struct KilobitsPerSecond(NonZeroU64);
impl FromStr for KilobitsPerSecond {
type Err = ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.parse::<NonZeroU64>().map(Self)
}
}
#[derive(Copy, Clone, Serialize, Debug, Hash, Eq, PartialEq)]
pub struct BytesPerSecond(NonZeroU64);