[web] Add support for reading from a configuration file
This commit is contained in:
parent
5087d4738d
commit
7f3b9ef2ec
|
@ -12,6 +12,8 @@ log = "0.4"
|
||||||
env_logger = "0.9"
|
env_logger = "0.9"
|
||||||
askama = "0.10"
|
askama = "0.10"
|
||||||
argh = "0.1.6"
|
argh = "0.1.6"
|
||||||
|
serde = "1.0"
|
||||||
|
serde_yaml = "0.8"
|
||||||
rusttype = { version = "0.9.2", optional = true }
|
rusttype = { version = "0.9.2", optional = true }
|
||||||
image = { version = "0.23.14", optional = true }
|
image = { version = "0.23.14", optional = true }
|
||||||
lazy_static = { version = "1.4.0", optional = true }
|
lazy_static = { version = "1.4.0", optional = true }
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
|
use std::fs::OpenOptions;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
use pronouns_today::InstanceSettings;
|
||||||
use argh::FromArgs;
|
use argh::FromArgs;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(FromArgs, PartialEq, Debug)]
|
#[derive(FromArgs, PartialEq, Debug)]
|
||||||
/// Everything you need to selfhost a pronouns.today server
|
/// Everything you need to selfhost a pronouns.today server
|
||||||
|
@ -45,12 +50,14 @@ pub struct Run {
|
||||||
option,
|
option,
|
||||||
default = "String::from(\"/etc/pronouns_today.yml\")",
|
default = "String::from(\"/etc/pronouns_today.yml\")",
|
||||||
short = 'c'))]
|
short = 'c'))]
|
||||||
/// path to the config file (generated if needed)
|
/// path to the config file (generated if needed). Defaults to
|
||||||
|
/// /etc/pronouns_today.yml
|
||||||
pub config: String,
|
pub config: String,
|
||||||
|
|
||||||
#[argh(switch)]
|
#[argh(switch)]
|
||||||
/// don't generate a config file if not present
|
/// don't attempt to read or generate a config file, just use command line args. Missing
|
||||||
pub no_generate_cfg: bool,
|
/// options will be filled in using sane defaults
|
||||||
|
pub no_read_cfg: bool,
|
||||||
|
|
||||||
#[argh(option, short = 'p')]
|
#[argh(option, short = 'p')]
|
||||||
/// the port to listen on
|
/// the port to listen on
|
||||||
|
@ -61,3 +68,81 @@ pub struct Run {
|
||||||
/// used in the custom URLs, e.g. "acaqeawdym")
|
/// used in the custom URLs, e.g. "acaqeawdym")
|
||||||
pub default_prefstr: Option<String>,
|
pub default_prefstr: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Run {
|
||||||
|
/// Read the config from the provided file, and apply overrides from args
|
||||||
|
///
|
||||||
|
/// If `--no-read-cfg` was passed, no config file is present, and there aren't
|
||||||
|
/// enough args to populate the configuration, returns an [`Err`] with a list of
|
||||||
|
/// arguments that were missing.
|
||||||
|
pub fn load_config(&self) -> Result<Conf, ConfigError> {
|
||||||
|
if !self.no_read_cfg {
|
||||||
|
match File::open(&self.config) {
|
||||||
|
Ok(raw_cfg) => {
|
||||||
|
serde_yaml::from_reader(raw_cfg)
|
||||||
|
.map_err(ConfigError::MalformedConfig)
|
||||||
|
}
|
||||||
|
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
|
||||||
|
OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.open(&self.config)
|
||||||
|
.and_then(|mut c|
|
||||||
|
c.write_all(include_bytes!("../assets/default_config.yml"))
|
||||||
|
)
|
||||||
|
.map_err(ConfigError::IoError)?;
|
||||||
|
Err(ConfigError::ConfigCreated(self.config.clone()))
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
Err(ConfigError::IoError(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(Conf::default())
|
||||||
|
}.map(|config| config.update_with(&self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize, Debug)]
|
||||||
|
/// A parsed config file for Pronouns.Today web
|
||||||
|
pub struct Conf {
|
||||||
|
|
||||||
|
#[serde(flatten)]
|
||||||
|
/// General settings for the pronouns.today library
|
||||||
|
pub instance_settings: InstanceSettings,
|
||||||
|
|
||||||
|
/// The port for the server to bind to. Defaults to 1312
|
||||||
|
pub port: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Conf {
|
||||||
|
fn update_with(mut self, args: &Run) -> Conf {
|
||||||
|
self.port = args.port.unwrap_or(self.port);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Conf {
|
||||||
|
fn default() -> Self {
|
||||||
|
Conf {
|
||||||
|
instance_settings: InstanceSettings::default(),
|
||||||
|
port: 1312,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An error occured while trying to load the configuration
|
||||||
|
pub enum ConfigError {
|
||||||
|
|
||||||
|
/// There wase a problem reading/writing the config file
|
||||||
|
IoError(std::io::Error),
|
||||||
|
|
||||||
|
/// There wase a problem deserializing the config file
|
||||||
|
MalformedConfig(serde_yaml::Error),
|
||||||
|
|
||||||
|
/// The config file was missing, but has been created
|
||||||
|
///
|
||||||
|
/// The program should now prompt the user to fill the config in, and then exit. The
|
||||||
|
/// provided [`String`] contains the path to the new config file
|
||||||
|
ConfigCreated(String),
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,9 @@ pub mod contrast;
|
||||||
pub mod statics;
|
pub mod statics;
|
||||||
pub mod configuration;
|
pub mod configuration;
|
||||||
|
|
||||||
|
use configuration::ConfigError;
|
||||||
|
|
||||||
|
use std::process::exit;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::{self, Display};
|
use std::fmt::{self, Display};
|
||||||
|
|
||||||
|
@ -142,6 +145,7 @@ async fn not_found() -> impl Responder {
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
async fn main() -> std::io::Result<()> {
|
async fn main() -> std::io::Result<()> {
|
||||||
|
|
||||||
|
env_logger::init();
|
||||||
let args: configuration::PronounsTodayArgs = argh::from_env();
|
let args: configuration::PronounsTodayArgs = argh::from_env();
|
||||||
|
|
||||||
match args.command {
|
match args.command {
|
||||||
|
@ -149,14 +153,30 @@ async fn main() -> std::io::Result<()> {
|
||||||
println!("Support for dumping statics not yet implemented");
|
println!("Support for dumping statics not yet implemented");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
configuration::SubCommand::Run(_subargs) => {
|
configuration::SubCommand::Run(subargs) => {
|
||||||
|
let config = match subargs.load_config() {
|
||||||
|
Ok(config) => config,
|
||||||
|
Err(ConfigError::IoError(e)) => {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
Err(ConfigError::MalformedConfig(e)) => {
|
||||||
|
eprintln!("Error parsing config file:\n{}", e);
|
||||||
|
exit(1001);
|
||||||
|
}
|
||||||
|
Err(ConfigError::ConfigCreated(path)) => {
|
||||||
|
println!("A config file has been generated at {}! Please check it out
|
||||||
|
and modify it to your liking, and then run this command
|
||||||
|
again", path);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
log::info!("Starting with configuration {:?}", config);
|
||||||
start_server().await
|
start_server().await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn start_server() -> std::io::Result<()> {
|
async fn start_server() -> std::io::Result<()> {
|
||||||
env_logger::init();
|
|
||||||
println!("Starting pronouns-today-web on 127.0.0.1:8080");
|
println!("Starting pronouns-today-web on 127.0.0.1:8080");
|
||||||
HttpServer::new(|| {
|
HttpServer::new(|| {
|
||||||
let logger = Logger::default();
|
let logger = Logger::default();
|
||||||
|
|
Loading…
Reference in New Issue