import os from dataclasses import dataclass def as_bool(value: str | None, default: bool = False) -> bool: if value is None: return default return value.lower() in {"1", "true", "yes", "on"} @dataclass(frozen=True) class Settings: database_url: str app_env: str allowed_origins: tuple[str, ...] session_cookie_name: str session_cookie_secure: bool session_ttl_seconds: int csrf_secret: str email_verification_enabled: bool phone_verification_enabled: bool test_free_period_limit: int test_vip_period_limit: int tts_upstream_url: str tts_api_key: str tts_timeout_seconds: int vision_model: str audio_storage_dir: str audio_retention_seconds: int smtp_host: str smtp_port: int smtp_user: str smtp_password: str smtp_from: str aliyun_access_key_id: str aliyun_access_key_secret: str aliyun_scheme_name: str aliyun_sign_name: str aliyun_template_code: str @classmethod def from_env(cls) -> "Settings": database_url = os.getenv("DATABASE_URL") if not database_url: raise RuntimeError("DATABASE_URL is required") origins = tuple(item.strip().rstrip("/") for item in os.getenv("ALLOWED_ORIGINS", "").split(",") if item.strip()) return cls( database_url=database_url, app_env=os.getenv("APP_ENV", "test"), allowed_origins=origins, session_cookie_name=os.getenv("SESSION_COOKIE_NAME", "kaotings_session"), session_cookie_secure=as_bool(os.getenv("SESSION_COOKIE_SECURE")), session_ttl_seconds=int(os.getenv("SESSION_TTL_SECONDS", "2592000")), csrf_secret=os.getenv("CSRF_SECRET", "development-only-change-me"), email_verification_enabled=as_bool(os.getenv("EMAIL_VERIFICATION_ENABLED")), phone_verification_enabled=as_bool(os.getenv("PHONE_VERIFICATION_ENABLED")), test_free_period_limit=int(os.getenv("TEST_FREE_PERIOD_LIMIT", "10000")), test_vip_period_limit=int(os.getenv("TEST_VIP_PERIOD_LIMIT", "100000")), tts_upstream_url=os.getenv("TTS_UPSTREAM_URL", "").rstrip("/"), tts_api_key=os.getenv("TTS_API_KEY", ""), tts_timeout_seconds=int(os.getenv("TTS_TIMEOUT_SECONDS", "120")), vision_model=os.getenv("VISION_MODEL", "Qwen3-VL-30B"), audio_storage_dir=os.getenv("AUDIO_STORAGE_DIR", "./data/audio"), audio_retention_seconds=int(os.getenv("AUDIO_RETENTION_SECONDS", "604800")), smtp_host=os.getenv("SMTP_HOST", ""), smtp_port=int(os.getenv("SMTP_PORT", "465")), smtp_user=os.getenv("SMTP_USER", ""), smtp_password=os.getenv("SMTP_PASSWORD", ""), smtp_from=os.getenv("SMTP_FROM", os.getenv("SMTP_USER", "")), aliyun_access_key_id=os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID", ""), aliyun_access_key_secret=os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET", ""), aliyun_scheme_name=os.getenv("ALIYUN_PNVS_SCHEME_NAME", ""), aliyun_sign_name=os.getenv("ALIYUN_PNVS_SIGN_NAME", ""), aliyun_template_code=os.getenv("ALIYUN_PNVS_TEMPLATE_CODE", ""), ) settings = Settings.from_env()