"""全局配置:pydantic-settings,全部来自环境变量 / .env。 规则(docs/technical-review.md §1.3): - 密钥不得硬编码;SECRET_KEY 缺失或仍为模板值时,生产环境拒绝启动。 - DATABASE_URL 可切换 PostgreSQL(扩展接口 1)。 """ from functools import lru_cache from pathlib import Path from pydantic import Field from pydantic_settings import BaseSettings, SettingsConfigDict _TEMPLATE_MARKERS = ("change-me",) class Settings(BaseSettings): model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore") # --- 基础 --- environment: str = "local" # local / production secret_key: str = Field(min_length=16) # --- 数据库 --- database_url: str = "sqlite:///./data/app.db" # --- 文件存储 --- storage_root: str = "./data" # --- 套餐 --- default_storage_quota: int = 104_857_600 # 100 MB default_max_file_size: int = 20_971_520 # 20 MB # --- 限流 --- rate_limit_per_token_per_min: int = 60 rate_limit_per_ip_per_min: int = 30 # --- CORS --- frontend_origin: str = "http://localhost:5173" @property def is_production(self) -> bool: return self.environment == "production" @property def storage_root_path(self) -> Path: return Path(self.storage_root).resolve() def validate_secrets(self) -> None: """生产环境禁止携带模板密钥启动。""" if not self.is_production: return for field_name in ("secret_key",): value = getattr(self, field_name).lower() if any(marker in value for marker in _TEMPLATE_MARKERS): raise RuntimeError( f"配置错误:{field_name} 仍为模板值,生产环境禁止启动。" "请运行: python -c \"import secrets; print(secrets.token_urlsafe(48))\"" ) @lru_cache def get_settings() -> Settings: s = Settings() # type: ignore[call-arg] s.validate_secrets() return s