use config::{Config, ConfigError, Environment, File}; use serde::Deserialize; #[derive(Debug, Deserialize, Clone)] pub struct S3Config { pub endpoint: String, pub region: String, pub bucket: String, pub access_key: String, pub secret_key: String, /// Public base URL used to build emote image URLs returned in API responses. /// Example: "https://s3.eu-central-1.wasabisys.com/open3dlab-emoji" pub public_url: String, } #[derive(Debug, Deserialize, Clone)] pub struct DatabaseConfig { /// Database URL. /// SQLite example: "sqlite://mikebase.db" /// PostgreSQL example: "postgresql://user:pass@localhost/mikebase" pub url: String, } #[derive(Debug, Deserialize, Clone)] pub struct ServerConfig { #[serde(default = "default_host")] pub host: String, #[serde(default = "default_port")] pub port: u16, } fn default_host() -> String { "0.0.0.0".to_string() } fn default_port() -> u16 { 3000 } #[derive(Debug, Deserialize, Clone)] pub struct AuthConfig { pub username: String, pub password: String, } #[derive(Debug, Deserialize, Clone)] pub struct ImportConfig { #[serde(default = "default_allowed_hosts")] pub allowed_hosts: Vec, } fn default_allowed_hosts() -> Vec { vec!["smutba.se".to_string()] } #[derive(Debug, Deserialize, Clone)] pub struct AppConfig { pub s3: S3Config, pub database: DatabaseConfig, #[serde(default)] pub server: ServerConfig, pub auth: Option, #[serde(default)] pub import: ImportConfig, } impl Default for ServerConfig { fn default() -> Self { Self { host: default_host(), port: default_port(), } } } impl Default for ImportConfig { fn default() -> Self { Self { allowed_hosts: default_allowed_hosts(), } } } impl AppConfig { pub fn load() -> Result { let cfg = Config::builder() // Optional config file (config.toml) .add_source(File::with_name("config").required(false)) // Environment variables with prefix APP (e.g. APP__S3__BUCKET) .add_source( Environment::with_prefix("APP") .separator("__") .try_parsing(true), ) .build()?; cfg.try_deserialize() } }