107 lines
4 KiB
Rust
107 lines
4 KiB
Rust
use anyhow::Result;
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct Config {
|
|
pub database_url: String,
|
|
pub jwt_secret: String,
|
|
pub jwt_expiry_secs: u64,
|
|
pub cache_max_capacity: usize,
|
|
pub cache_idle_timeout_secs: u64,
|
|
pub cache_sweep_interval_secs: u64,
|
|
/// Comma-separated allowed CORS origins, or "*" for permissive. Empty = no CORS headers.
|
|
pub cors_origins: Vec<String>,
|
|
/// Base URL of the MinIO/S3 endpoint (no trailing slash).
|
|
pub cdn_endpoint: String,
|
|
/// Bucket name used for CDN object storage.
|
|
pub cdn_bucket: String,
|
|
/// Dev-only: when true, all auth/permission checks are bypassed and every
|
|
/// request is treated as a super-admin. NEVER enable in production.
|
|
pub disable_auth: bool,
|
|
}
|
|
|
|
impl Config {
|
|
pub fn from_env() -> Result<Self> {
|
|
Ok(Self {
|
|
database_url: std::env::var("DATABASE_URL")?,
|
|
jwt_secret: std::env::var("JWT_SECRET")?,
|
|
jwt_expiry_secs: std::env::var("JWT_EXPIRY_SECS")
|
|
.unwrap_or_else(|_| "3600".into())
|
|
.parse()?,
|
|
cache_max_capacity: std::env::var("CACHE_MAX_CAPACITY")
|
|
.unwrap_or_else(|_| "10000".into())
|
|
.parse()?,
|
|
cache_idle_timeout_secs: std::env::var("CACHE_IDLE_TIMEOUT_SECS")
|
|
.unwrap_or_else(|_| "300".into())
|
|
.parse()?,
|
|
cache_sweep_interval_secs: std::env::var("CACHE_SWEEP_INTERVAL_SECS")
|
|
.unwrap_or_else(|_| "60".into())
|
|
.parse()?,
|
|
cors_origins: std::env::var("CORS_ORIGINS")
|
|
.unwrap_or_default()
|
|
.split(',')
|
|
.map(|s| s.trim().to_string())
|
|
.filter(|s| !s.is_empty())
|
|
.collect(),
|
|
cdn_endpoint: std::env::var("CDN_ENDPOINT")
|
|
.unwrap_or_else(|_| "http://localhost:9000".into()),
|
|
cdn_bucket: std::env::var("CDN_BUCKET").unwrap_or_else(|_| "mercury".into()),
|
|
disable_auth: std::env::var("DISABLE_AUTH")
|
|
.map(|v| matches!(v.trim().to_lowercase().as_str(), "1" | "true"))
|
|
.unwrap_or(false),
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use std::sync::Mutex;
|
|
|
|
// Serializes tests that mutate the process-global DISABLE_AUTH env var,
|
|
// since cargo runs tests in parallel by default.
|
|
static DISABLE_AUTH_ENV_LOCK: Mutex<()> = Mutex::new(());
|
|
|
|
#[test]
|
|
fn test_config_defaults() {
|
|
std::env::set_var("DATABASE_URL", "postgres://test");
|
|
std::env::set_var("JWT_SECRET", "secret");
|
|
let cfg = Config::from_env().unwrap();
|
|
assert_eq!(cfg.jwt_expiry_secs, 3600);
|
|
assert_eq!(cfg.cache_max_capacity, 10_000);
|
|
assert_eq!(cfg.cache_idle_timeout_secs, 300);
|
|
assert_eq!(cfg.cache_sweep_interval_secs, 60);
|
|
assert!(cfg.cors_origins.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn test_config_disable_auth_default_false() {
|
|
let _guard = DISABLE_AUTH_ENV_LOCK.lock().unwrap();
|
|
std::env::set_var("DATABASE_URL", "postgres://test");
|
|
std::env::set_var("JWT_SECRET", "secret");
|
|
std::env::remove_var("DISABLE_AUTH");
|
|
let cfg = Config::from_env().unwrap();
|
|
assert!(!cfg.disable_auth);
|
|
}
|
|
|
|
#[test]
|
|
fn test_config_disable_auth_true() {
|
|
let _guard = DISABLE_AUTH_ENV_LOCK.lock().unwrap();
|
|
std::env::set_var("DATABASE_URL", "postgres://test");
|
|
std::env::set_var("JWT_SECRET", "secret");
|
|
std::env::set_var("DISABLE_AUTH", "true");
|
|
let cfg = Config::from_env().unwrap();
|
|
assert!(cfg.disable_auth);
|
|
std::env::remove_var("DISABLE_AUTH");
|
|
}
|
|
|
|
#[test]
|
|
fn test_config_disable_auth_numeric_true() {
|
|
let _guard = DISABLE_AUTH_ENV_LOCK.lock().unwrap();
|
|
std::env::set_var("DATABASE_URL", "postgres://test");
|
|
std::env::set_var("JWT_SECRET", "secret");
|
|
std::env::set_var("DISABLE_AUTH", "1");
|
|
let cfg = Config::from_env().unwrap();
|
|
assert!(cfg.disable_auth);
|
|
std::env::remove_var("DISABLE_AUTH");
|
|
}
|
|
}
|