All checks were successful
Push Workflows / rustfmt (push) Successful in 16s
Push Workflows / tailwind-build (push) Successful in 15s
Push Workflows / test (push) Successful in 27s
Push Workflows / docs (push) Successful in 25s
Push Workflows / clippy (push) Successful in 19s
Push Workflows / build (push) Successful in 50s
Push Workflows / nix-build (push) Successful in 5m13s
Enable by default in release mode
169 lines
4.2 KiB
Rust
169 lines
4.2 KiB
Rust
use serde::Deserialize;
|
|
|
|
/// Enable secure cookies by default only in release mode
|
|
/// (Secure cookies can't be set over HTTP. Rough assumption: development is done over HTTP)
|
|
const DEFAULT_COOKIES_SECURE: bool = cfg!(not(debug_assertions));
|
|
|
|
#[derive(Debug, Clone, Deserialize)]
|
|
pub struct AuthConfig {
|
|
pub open_signup: bool,
|
|
pub cookies_secure: bool,
|
|
}
|
|
|
|
/// Build a connection URI from parts
|
|
fn format_uri(
|
|
scheme: &str,
|
|
username: &Option<String>,
|
|
password: &Option<String>,
|
|
host: &str,
|
|
port: &Option<u16>,
|
|
path: &Option<String>,
|
|
) -> String {
|
|
let mut url = format!("{scheme}://");
|
|
|
|
if let Some(username) = username {
|
|
url.push_str(username);
|
|
|
|
if let Some(password) = password {
|
|
url.push_str(&format!(":{password}"));
|
|
}
|
|
|
|
url.push('@');
|
|
}
|
|
|
|
url.push_str(host);
|
|
|
|
if let Some(port) = port {
|
|
url.push_str(&format!(":{port}"));
|
|
}
|
|
|
|
if let Some(path) = path {
|
|
url.push_str(&format!("/{path}"));
|
|
}
|
|
|
|
url
|
|
}
|
|
|
|
#[derive(Debug, Clone, Deserialize)]
|
|
pub struct DatabaseConfig {
|
|
#[serde(flatten)]
|
|
connection: DatabaseConnectionConfig,
|
|
}
|
|
|
|
impl DatabaseConfig {
|
|
/// Get the configured database connection URI
|
|
pub fn connection_uri(&self) -> String {
|
|
self.connection.as_uri()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Deserialize)]
|
|
#[serde(untagged)]
|
|
enum DatabaseConnectionConfig {
|
|
FromUrl {
|
|
url: String,
|
|
},
|
|
FromParts {
|
|
host: String,
|
|
port: Option<u16>,
|
|
database: Option<String>,
|
|
username: Option<String>,
|
|
password: Option<String>,
|
|
},
|
|
}
|
|
|
|
impl DatabaseConnectionConfig {
|
|
/// Convert this configuration into the Postgres connection URI
|
|
pub fn as_uri(&self) -> String {
|
|
match self {
|
|
Self::FromUrl { url } => url.clone(),
|
|
Self::FromParts {
|
|
host,
|
|
port,
|
|
database,
|
|
username,
|
|
password,
|
|
} => format_uri("postgres", username, password, host, port, database),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Deserialize)]
|
|
#[serde(untagged)]
|
|
enum KeyValStoreConnectionConfig {
|
|
FromUrl {
|
|
url: String,
|
|
},
|
|
FromParts {
|
|
scheme: Option<String>,
|
|
host: String,
|
|
port: Option<u16>,
|
|
database: Option<String>,
|
|
username: Option<String>,
|
|
password: Option<String>,
|
|
},
|
|
}
|
|
|
|
impl KeyValStoreConnectionConfig {
|
|
/// Convert this configuration into the Redis connection URI
|
|
pub fn as_uri(&self) -> String {
|
|
match self {
|
|
Self::FromUrl { url } => url.clone(),
|
|
Self::FromParts {
|
|
scheme,
|
|
host,
|
|
port,
|
|
database,
|
|
username,
|
|
password,
|
|
} => format_uri(
|
|
scheme.as_deref().unwrap_or("redis"),
|
|
username,
|
|
password,
|
|
host,
|
|
port,
|
|
database,
|
|
),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Deserialize)]
|
|
pub struct KeyValStoreConfig {
|
|
#[serde(flatten)]
|
|
connection: KeyValStoreConnectionConfig,
|
|
}
|
|
|
|
impl KeyValStoreConfig {
|
|
/// Get the configured database connection URI
|
|
pub fn connection_uri(&self) -> String {
|
|
self.connection.as_uri()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Deserialize)]
|
|
/// Top-level application configuration
|
|
pub struct Config {
|
|
pub auth: AuthConfig,
|
|
pub database: DatabaseConfig,
|
|
pub key_val_store: KeyValStoreConfig,
|
|
}
|
|
|
|
/// Parse configuration from the expected files and environment variables
|
|
pub fn load_config() -> Result<Config, config::ConfigError> {
|
|
use config::{Environment, File};
|
|
|
|
let pkg_name = env!("CARGO_PKG_NAME");
|
|
|
|
config::Config::builder()
|
|
.set_default("server.port", 8080)?
|
|
.set_default("auth.open_signup", false)?
|
|
.set_default("auth.cookies_secure", DEFAULT_COOKIES_SECURE)?
|
|
.add_source(File::with_name(&format!("/etc/{pkg_name}/config")).required(false))
|
|
.add_source(File::with_name(&format!("/etc/{pkg_name}")).required(false))
|
|
.add_source(File::with_name("config").required(false))
|
|
.add_source(Environment::with_prefix(pkg_name).separator("_"))
|
|
.build()?
|
|
.try_deserialize()
|
|
}
|