import base64 from pathlib import Path from typing import Optional from pydantic import field_validator from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): """应用配置,从环境变量加载.""" # 安全配置 encrypt_key: str # AES-256 密钥 (base64) api_token: str # API 认证 Token # 路径配置 data_dir: Path = Path('./data') # 服务器配置 host: str = '0.0.0.0' port: int = 8000 model_config = SettingsConfigDict( env_prefix='GM_', env_file='.env', env_file_encoding='utf-8', ) @field_validator('encrypt_key') @classmethod def validate_encrypt_key(cls, v: str) -> str: """验证 encrypt_key 是否为有效的 base64 格式且长度足够.""" # Check if valid base64 try: decoded = base64.b64decode(v, validate=True) except Exception: raise ValueError('encrypt_key must be valid base64 string') # Check length (AES-256 requires 32 bytes) if len(decoded) < 32: raise ValueError('encrypt_key must decode to at least 32 bytes for AES-256') return v @property def db_path(self) -> Path: """SQLite 数据库路径.""" return self.data_dir / 'git_manager.db' @property def ssh_keys_dir(self) -> Path: """SSH 密钥存储目录.""" return self.data_dir / 'ssh_keys' @property def repos_dir(self) -> Path: """仓库镜像存储目录.""" return self.data_dir / 'repos' # Lazy initialization - settings is loaded on first access _settings: Optional[Settings] = None def get_settings() -> Settings: """获取全局配置实例(懒加载).""" global _settings if _settings is None: _settings = Settings() return _settings