From f474b97a575c4582d902cd8553b8ae4ec1b84cda Mon Sep 17 00:00:00 2001 From: Xiaoxia AI Date: Sun, 21 Jun 2026 00:36:13 +0800 Subject: [PATCH] refactor(config): centralize api settings source --- apps/api/app/core/config.py | 136 +++------------------------- docs/全面代码审计报告-2026-06-21.md | 17 ++++ 2 files changed, 32 insertions(+), 121 deletions(-) diff --git a/apps/api/app/core/config.py b/apps/api/app/core/config.py index c0c31cc90..53b81f20f 100644 --- a/apps/api/app/core/config.py +++ b/apps/api/app/core/config.py @@ -1,125 +1,19 @@ -from pydantic_settings import BaseSettings, SettingsConfigDict -from typing import Optional -import os +"""Compatibility layer for the canonical API settings module. + +Use `app.config` as the single source of truth for API configuration. +This module remains only for older imports during migration. +""" + +from app.config import Settings as AppSettings +from app.config import get_settings, settings -class AppSettings(BaseSettings): - """应用配置""" - - # 基础配置 - app_name: str = "xiaoxia-saas" - app_env: str = "development" # development / staging / production - app_version: str = "0.1.0" - debug: bool = True - - # API 配置 - api_host: str = "0.0.0.0" - api_port: int = 8000 - api_prefix: str = "/api/v1" - - # 数据库配置 - database_url: str = "postgresql+psycopg://postgres:postgres@localhost:5432/xiaoxia_saas" - database_pool_size: int = 20 - database_max_overflow: int = 40 - database_pool_timeout: int = 30 - database_pool_recycle: int = 3600 - - # Redis 配置 - redis_url: str = "redis://localhost:6379/0" - redis_max_connections: int = 50 - - # Celery 配置 - celery_broker_url: str = "redis://localhost:6379/0" - celery_result_backend: str = "redis://localhost:6379/1" - celery_worker_concurrency: int = 4 - celery_worker_max_tasks_per_child: int = 1000 - - # MinIO 配置 - minio_endpoint: str = "localhost:9000" - minio_access_key: str = "admin" - minio_secret_key: str = "xiaoxia2026" - minio_bucket: str = "xiaoxia-assets" - minio_secure: bool = False - minio_public_url: str = "http://localhost:9000" - - # 日志配置 - log_level: str = "INFO" - log_format: str = "json" # json / text - log_file: Optional[str] = None - - # CORS 配置 - cors_origins: str = "http://localhost:3000,http://localhost:8000" - cors_allow_credentials: bool = True - - # 文件上传限制 - max_upload_size_mb: int = 1000 - allowed_file_types: str = "video/mp4,video/quicktime,video/x-msvideo,audio/mpeg,audio/wav,image/jpeg,image/png,image/gif" - - # 安全配置 - secret_key: str = "change-me-in-production" - access_token_expire_minutes: int = 60 - refresh_token_expire_days: int = 7 - - # 监控配置(可选) - sentry_dsn: Optional[str] = None - prometheus_port: Optional[int] = None - - model_config = SettingsConfigDict( - env_file=".env", - env_file_encoding="utf-8", - case_sensitive=False, - extra="ignore", - ) - - @property - def cors_origins_list(self) -> list[str]: - """解析 CORS origins 为列表""" - return [origin.strip() for origin in self.cors_origins.split(",")] - - @property - def allowed_file_types_list(self) -> list[str]: - """解析允许的文件类型为列表""" - return [ft.strip() for ft in self.allowed_file_types.split(",")] - - @property - def is_production(self) -> bool: - """是否为生产环境""" - return self.app_env == "production" - - @property - def is_staging(self) -> bool: - """是否为 staging 环境""" - return self.app_env == "staging" - - @property - def is_development(self) -> bool: - """是否为开发环境""" - return self.app_env == "development" +def reload_settings() -> AppSettings: + """Reload settings for tests and legacy callers.""" + import app.config as canonical_config + + canonical_config.settings = canonical_config.get_settings() + return canonical_config.settings -# 全局配置实例 -_settings: Optional[AppSettings] = None - - -def get_settings() -> AppSettings: - """获取配置实例(单例模式)""" - global _settings - if _settings is None: - # 根据环境加载不同的 .env 文件 - env = os.getenv("APP_ENV", "development") - env_file = f".env.{env}" if env != "development" else ".env" - - # 如果环境特定的配置文件存在,则使用它 - if os.path.exists(env_file): - _settings = AppSettings(_env_file=env_file) - else: - _settings = AppSettings() - - return _settings - - -def reload_settings(): - """重新加载配置(用于测试)""" - global _settings - _settings = None - return get_settings() +__all__ = ["AppSettings", "get_settings", "reload_settings", "settings"] diff --git a/docs/全面代码审计报告-2026-06-21.md b/docs/全面代码审计报告-2026-06-21.md index 2bde848d8..b3a8ffadd 100644 --- a/docs/全面代码审计报告-2026-06-21.md +++ b/docs/全面代码审计报告-2026-06-21.md @@ -84,6 +84,23 @@ - 未配置 OSS 时,下载 URL 对本地 `/generated-files/` 直接返回,对 OSS URL 回退为公开 URL。 - 上传/下载等 OSS 专属操作在未配置时返回明确错误。 +### 5. P1:API 配置存在双真源 + +**涉及文件**:`apps/api/app/config.py`、`apps/api/app/core/config.py` + +**问题**: +- `app/config.py` 与 `app/core/config.py` 同时定义 Settings。 +- 两套配置字段命名、OSS/MinIO、JWT、CORS 等含义不一致。 +- 新代码主要使用 `app.config`,旧模块可能误导入 `app.core.config`。 + +**根因**: +- 早期目录重构后没有清理旧配置入口。 + +**修复**: +- 明确 `app.config` 为 API 配置唯一真源。 +- `app/core/config.py` 改为兼容转发层,只 re-export canonical Settings/get_settings/settings。 +- 避免未来继续在旧文件增加新字段导致漂移。 + ## 三、已补充测试 ### 新增