refactor(config): centralize api settings source
This commit is contained in:
+15
-121
@@ -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"]
|
||||
|
||||
@@ -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。
|
||||
- 避免未来继续在旧文件增加新字段导致漂移。
|
||||
|
||||
## 三、已补充测试
|
||||
|
||||
### 新增
|
||||
|
||||
Reference in New Issue
Block a user