100 lines
4.0 KiB
Python
100 lines
4.0 KiB
Python
"""
|
||
核心配置
|
||
|
||
集中管理环境变量和应用配置,遵循 12-factor 原则。
|
||
"""
|
||
|
||
import os
|
||
from pathlib import Path
|
||
from typing import Optional
|
||
|
||
try:
|
||
from dotenv import load_dotenv
|
||
except ModuleNotFoundError: # pragma: no cover - optional dependency in lightweight envs
|
||
def load_dotenv(*_args, **_kwargs):
|
||
return False
|
||
|
||
# 仅从项目根目录 .env 加载,作为唯一配置源
|
||
BACKEND_DIR = Path(__file__).resolve().parents[2]
|
||
PROJECT_ROOT = BACKEND_DIR.parent
|
||
load_dotenv(PROJECT_ROOT / ".env", override=False)
|
||
|
||
# 默认数据库固定到 Docker PostgreSQL,不再提供 SQLite 回落
|
||
DEFAULT_DB_HOST = os.getenv("DB_HOST", "127.0.0.1")
|
||
DEFAULT_DB_PORT = os.getenv("DB_PORT", "5435")
|
||
DEFAULT_DB_NAME = os.getenv("POSTGRES_DB", "pmflow")
|
||
DEFAULT_DB_USER = os.getenv("POSTGRES_USER", "pmflow")
|
||
DEFAULT_DB_PASSWORD = os.getenv("POSTGRES_PASSWORD", "pmflow_pass")
|
||
DEFAULT_DATABASE_URL = (
|
||
f"postgresql+asyncpg://{DEFAULT_DB_USER}:{DEFAULT_DB_PASSWORD}"
|
||
f"@{DEFAULT_DB_HOST}:{DEFAULT_DB_PORT}/{DEFAULT_DB_NAME}"
|
||
)
|
||
TRUE_VALUES = {"true", "1", "yes", "on"}
|
||
DEBUG_ENABLED = os.getenv("DEBUG", "false").lower() in TRUE_VALUES
|
||
|
||
|
||
def _env_flag(name: str, default: bool) -> bool:
|
||
raw = os.getenv(name)
|
||
if raw is None:
|
||
return default
|
||
return raw.strip().lower() in TRUE_VALUES
|
||
|
||
|
||
def _env_csv(name: str) -> tuple[str, ...]:
|
||
raw = os.getenv(name, "")
|
||
return tuple(item.strip() for item in raw.split(",") if item.strip())
|
||
|
||
|
||
class Settings:
|
||
"""应用配置(从环境变量读取)"""
|
||
|
||
# 应用
|
||
app_name: str = os.getenv("APP_NAME", "Neural Sync API")
|
||
debug: bool = DEBUG_ENABLED
|
||
startup_model_probe_enabled: bool = _env_flag("STARTUP_MODEL_PROBE_ENABLED", not DEBUG_ENABLED)
|
||
feishu_long_connection_enabled: bool = _env_flag("FEISHU_LONG_CONNECTION_ENABLED", not DEBUG_ENABLED)
|
||
world_model_warmup_enabled: bool = _env_flag("STARTUP_WORLD_MODEL_WARMUP_ENABLED", not DEBUG_ENABLED)
|
||
schedulers_enabled: bool = _env_flag("STARTUP_SCHEDULERS_ENABLED", True)
|
||
background_workers_enabled: bool = _env_flag("STARTUP_BACKGROUND_WORKERS_ENABLED", True)
|
||
api_route_profile: str = os.getenv("API_ROUTE_PROFILE", "full").strip().lower() or "full"
|
||
api_optional_route_groups: tuple[str, ...] = _env_csv("API_OPTIONAL_ROUTE_GROUPS")
|
||
|
||
# 数据库
|
||
database_url: str = os.getenv("DATABASE_URL", DEFAULT_DATABASE_URL)
|
||
db_pool_size: int = int(os.getenv("DB_POOL_SIZE", "20"))
|
||
db_max_overflow: int = int(os.getenv("DB_MAX_OVERFLOW", "40"))
|
||
db_pool_timeout: int = int(os.getenv("DB_POOL_TIMEOUT", "30"))
|
||
db_pool_recycle: int = int(os.getenv("DB_POOL_RECYCLE", "1800"))
|
||
|
||
# JWT
|
||
secret_key: str = os.getenv("SECRET_KEY", "your-secret-key-change-in-production")
|
||
algorithm: str = os.getenv("JWT_ALGORITHM", "HS256")
|
||
access_token_expire_minutes: int = int(os.getenv("ACCESS_TOKEN_EXPIRE_MINUTES", str(60 * 24 * 7)))
|
||
|
||
# CORS
|
||
cors_origins: str = os.getenv("CORS_ORIGINS", "*")
|
||
|
||
# LLM
|
||
llm_provider: str = os.getenv("LLM_PROVIDER", "ollama").lower()
|
||
ollama_host: str = os.getenv("OLLAMA_HOST", "http://127.0.0.1:11434")
|
||
business_context: Optional[str] = os.getenv("BUSINESS_CONTEXT")
|
||
|
||
# Redis(可选,用于 AI 结果缓存)
|
||
redis_url: Optional[str] = os.getenv("REDIS_URL")
|
||
|
||
# 多租户(预留)
|
||
default_tenant_id: str = os.getenv("DEFAULT_TENANT_ID", "default")
|
||
|
||
# GitHub App
|
||
github_app_slug: Optional[str] = os.getenv("GITHUB_APP_SLUG")
|
||
github_app_install_url: Optional[str] = os.getenv("GITHUB_APP_INSTALL_URL")
|
||
github_app_callback_url: Optional[str] = os.getenv("GITHUB_APP_CALLBACK_URL")
|
||
github_app_webhook_secret: Optional[str] = os.getenv("GITHUB_APP_WEBHOOK_SECRET")
|
||
github_app_sync_endpoint: Optional[str] = os.getenv("GITHUB_APP_SYNC_ENDPOINT")
|
||
github_app_sync_token: Optional[str] = os.getenv("GITHUB_APP_SYNC_TOKEN")
|
||
github_app_installations_fixture: Optional[str] = os.getenv("GITHUB_APP_INSTALLATIONS_FIXTURE")
|
||
|
||
|
||
# 全局配置实例
|
||
settings = Settings()
|