from pathlib import Path from urllib.parse import urlsplit from pydantic import Field, model_validator from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): app_name: str = "Nalati Tourism QA API" app_env: str = "development" api_prefix: str = "/api" database_url: str = "postgresql+psycopg://nalati:change-this-password@127.0.0.1:5432/nalati_rag" cors_origins: str = "http://localhost:5173,http://127.0.0.1:5173" auth_allowed_origins: str = ( "http://localhost:5173,http://127.0.0.1:5173,http://localhost:8000,http://127.0.0.1:8000" ) auth_cookie_secure: bool = True auth_session_seconds: int = Field(28800, ge=300, le=604800) model_config = SettingsConfigDict( env_file=Path(__file__).resolve().parents[2] / ".env", extra="ignore" ) @property def cors_origin_list(self) -> list[str]: return [x.strip() for x in self.cors_origins.split(",") if x.strip()] @property def auth_origin_list(self) -> list[str]: return [x.strip() for x in self.auth_allowed_origins.split(",") if x.strip()] @model_validator(mode="after") def validate_auth_settings(self): if not self.auth_cookie_secure and self.app_env not in {"development", "test"}: raise ValueError("非开发/测试环境必须使用 Secure Cookie 与 HTTPS") if not self.auth_origin_list: raise ValueError("必须显式设置认证允许的来源") for origin in [*self.auth_origin_list, *self.cors_origin_list]: parsed = urlsplit(origin) if ( parsed.scheme not in {"http", "https"} or not parsed.hostname or parsed.netloc != parsed.hostname + (f":{parsed.port}" if parsed.port else "") or parsed.path or parsed.query or parsed.fragment or "*" in origin or (self.app_env not in {"development", "test"} and parsed.scheme != "https") ): raise ValueError("认证和跨域来源须为明确的 HTTP(S) Origin,不允许通配符或路径") return self settings = Settings()