2.7 KiB · text History 25c4237
use sea_orm::DatabaseConnection;
use std::env;

/// Runtime configuration, sourced entirely from environment variables so the
/// same binary works in local dev, compose, and production without a config
/// file. Every field has a sensible default that matches `compose.yml`.
#[derive(Debug, Clone)]
pub struct Config {
    pub database_url: String,
    pub bind_address: String,
    pub session_duration_hours: i64,
    pub registration_token_duration_days: i64,
    /// Run pending migrations on webservice boot.
    pub auto_migrate: bool,
    /// Allow public self-registration (still requires a valid invite token).
    pub registration_enabled: bool,
    /// Base of the osm-tiles server; the browser loads `{base}/{theme}/{z}/{x}/{y}.png`.
    /// Must be a host whose referer allowlist includes this app's domain.
    pub tiles_url: String,
    /// Photon, behind osm-tiles' `/geocode/`. Empty disables search.
    pub geocode_url: String,
    /// The `/poi/` sidecar. Empty skips it and uses Photon reverse only.
    pub poi_url: String,
    /// Where the map opens before any area exists.
    pub default_lat: f64,
    pub default_lon: f64,
}

impl Config {
    pub fn from_env() -> Self {
        Config {
            database_url: env::var("DATABASE_URL").unwrap_or_else(|_| {
                "postgres://app_user:app_password@localhost:5441/app".to_string()
            }),
            bind_address: env::var("BIND_ADDRESS").unwrap_or_else(|_| "0.0.0.0:4011".to_string()),
            session_duration_hours: parse_or("SESSION_DURATION_HOURS", 24),
            registration_token_duration_days: parse_or("REGISTRATION_TOKEN_DURATION_DAYS", 7),
            auto_migrate: parse_bool("AUTO_MIGRATE", true),
            registration_enabled: parse_bool("REGISTRATION_ENABLED", false),
            tiles_url: env::var("TILES_URL").unwrap_or_else(|_| "https://tiles.neiam.org".to_string()),
            geocode_url: env::var("GEOCODE_URL")
                .unwrap_or_else(|_| "https://tiles.neiam.org/geocode".to_string()),
            poi_url: env::var("POI_URL").unwrap_or_else(|_| "https://tiles.neiam.org/poi".to_string()),
            default_lat: parse_or("DEFAULT_LAT", 42.3601),
            default_lon: parse_or("DEFAULT_LON", -71.0589),
        }
    }
}

fn parse_or<T: std::str::FromStr>(key: &str, default: T) -> T {
    env::var(key).ok().and_then(|v| v.parse().ok()).unwrap_or(default)
}

fn parse_bool(key: &str, default: bool) -> bool {
    env::var(key)
        .map(|v| matches!(v.to_ascii_lowercase().as_str(), "1" | "true" | "yes" | "on"))
        .unwrap_or(default)
}

pub async fn create_db_connection(database_url: &str) -> Result<DatabaseConnection, sea_orm::DbErr> {
    sea_orm::Database::connect(database_url).await
}