use std::env; #[derive(Clone, Debug)] pub struct Config { pub bind_addr: String, pub canonical_origin: String, pub secure_cookies: bool, pub db_path: String, pub data_dir: String, pub frontend_dir: String, pub worker_image: String, pub fetch_timeout_secs: u64, pub backtest_timeout_secs: u64, pub run_concurrency: usize, pub fetch_concurrency: usize, pub session_hours: i64, pub bootstrap_admin_email: Option, pub bootstrap_admin_password: Option, pub ai_base_url: String, pub ai_model: String, pub ai_daily_request_cap: i64, pub ai_input_token_cap: i64, pub ai_output_token_cap: i64, pub ai_enabled_poc: bool, pub default_run_limit_per_day: i64, pub version: String, } fn envs(k: &str, d: &str) -> String { env::var(k).ok().filter(|v| !v.is_empty()).unwrap_or_else(|| d.to_string()) } impl Config { pub fn from_env() -> Self { let canonical_origin = envs("ORIGIN", ""); Config { bind_addr: envs("BIND", "127.0.0.1:8787"), canonical_origin: canonical_origin.clone(), secure_cookies: canonical_origin.starts_with("https://"), db_path: envs("DB_PATH", "server-data/strategy-lab.sqlite3"), data_dir: envs("DATA_DIR", "server-data/data"), frontend_dir: envs("FRONTEND_DIR", "frontend/dist"), worker_image: envs("WORKER_IMAGE", "strategy-lab-worker:local"), fetch_timeout_secs: envs("FETCH_TIMEOUT_SECS", "1800").parse().unwrap_or(1800), backtest_timeout_secs: envs("BACKTEST_TIMEOUT_SECS", "1800").parse().unwrap_or(1800), run_concurrency: envs("RUN_CONCURRENCY", "1").parse().unwrap_or(1), fetch_concurrency: envs("FETCH_CONCURRENCY", "2").parse().unwrap_or(2), session_hours: envs("SESSION_HOURS", "336").parse().unwrap_or(336), bootstrap_admin_email: env::var("BOOTSTRAP_ADMIN_EMAIL").ok(), bootstrap_admin_password: env::var("BOOTSTRAP_ADMIN_PASSWORD").ok(), ai_base_url: envs("AI_BASE_URL", "https://opencode.ai/zen/go/v1"), ai_model: envs("AI_MODEL", "glm-5.3-flash"), ai_daily_request_cap: envs("AI_DAILY_REQUEST_CAP", "20").parse().unwrap_or(20), ai_input_token_cap: envs("AI_INPUT_TOKEN_CAP", "8000").parse().unwrap_or(8000), ai_output_token_cap: envs("AI_OUTPUT_TOKEN_CAP", "6000").parse().unwrap_or(6000), ai_enabled_poc: envs("AI_ENABLED_POC", "true") == "true", default_run_limit_per_day: envs("DEFAULT_RUN_LIMIT_PER_DAY", "10").parse().unwrap_or(10), version: envs("APP_VERSION", env!("CARGO_PKG_VERSION")), } } } pub const MAX_CODE_LEN: usize = 256_000; pub const MAX_SYMBOLS: usize = 5; pub const MAX_RANGE_YEARS: i64 = 15;