""" FastAPI 应用配置管理 支持环境变量配置,生产级配置管理 """ import os from pathlib import Path from typing import Optional import logging try: from dotenv import load_dotenv except Exception: # pragma: no cover load_dotenv = None # 项目根目录 BASE_DIR = Path(__file__).resolve().parent.parent.parent # 加载 .env(不覆盖已存在的系统环境变量) _dotenv_path = BASE_DIR / ".env" if load_dotenv is not None and _dotenv_path.exists(): load_dotenv(dotenv_path=_dotenv_path, override=False) # 环境变量 ENVIRONMENT = os.getenv('ENV', 'development') DEBUG = os.getenv('DEBUG', 'False').lower() == 'true' class Settings: """应用配置类""" # FastAPI 基础配置 APP_TITLE = "时间序列数据分析系统" APP_DESCRIPTION = "支持多格式数据上传、AI增强分析、多语言报告生成" APP_VERSION = "2.0.0" # API 暴露模式 # - full: 暴露 v1 + v2(默认) # - v2: 仅暴露 v2 分析接口 + 基础状态接口(禁用 v1 上传/文件/图片接口) API_MODE = os.getenv('API_MODE', 'full').strip().lower() # 服务器配置 HOST = os.getenv('HOST', '0.0.0.0') PORT = int(os.getenv('PORT', 60201)) RELOAD = DEBUG # CORS 配置 CORS_ORIGINS = os.getenv('CORS_ORIGINS', '*').split(',') CORS_ALLOW_CREDENTIALS = True CORS_ALLOW_METHODS = ['*'] CORS_ALLOW_HEADERS = ['*'] # 文件上传配置 UPLOAD_DIR = Path(os.getenv('UPLOAD_DIR', BASE_DIR / 'uploads')) UPLOAD_DIR.mkdir(exist_ok=True) MAX_UPLOAD_SIZE = int(os.getenv('MAX_UPLOAD_SIZE', 16 * 1024 * 1024)) # 16MB ALLOWED_EXTENSIONS = {'csv'} # 临时文件配置 TEMP_DIR = Path(os.getenv('TEMP_DIR', BASE_DIR / 'temp')) TEMP_DIR.mkdir(exist_ok=True) # 字体配置 FONTS_DIR = Path(os.getenv('FONTS_DIR', BASE_DIR / 'resource' / 'fonts')) FONTS_DIR.mkdir(parents=True, exist_ok=True) # API 配置 (阿里云千问) API_KEY = os.getenv('MY_API_KEY', '') API_BASE = os.getenv('MY_API_BASE', 'https://dashscope.aliyuncs.com/compatible-mode/v1') API_MODEL = os.getenv('MY_MODEL', 'qwen-turbo') API_TIMEOUT = int(os.getenv('API_TIMEOUT', 30)) # 分析配置 LANGUAGE_DEFAULT = os.getenv('LANGUAGE_DEFAULT', 'zh') ANALYSIS_TIMEOUT = int(os.getenv('ANALYSIS_TIMEOUT', 300)) # 5分钟 # 日志配置 LOG_LEVEL = os.getenv('LOG_LEVEL', 'INFO' if not DEBUG else 'DEBUG') LOG_DIR = Path(os.getenv('LOG_DIR', BASE_DIR / 'logs')) LOG_DIR.mkdir(exist_ok=True) # 内存管理 MAX_MEMORY_MB = int(os.getenv('MAX_MEMORY_MB', 500)) # v2 (OSS URL) 配置 # 允许的域名白名单(逗号分隔)。为空时表示不启用域名白名单(仍会做私网/环回 IP 拦截)。 V2_ALLOWED_HOSTS = [h.strip() for h in os.getenv('V2_ALLOWED_HOSTS', '').split(',') if h.strip()] # 是否允许 http(默认仅 https) V2_ALLOW_HTTP = os.getenv('V2_ALLOW_HTTP', 'False').lower() == 'true' # 是否允许私网/环回地址(仅用于本地开发/冒烟;生产建议保持 False) V2_ALLOW_PRIVATE_NETWORKS = os.getenv('V2_ALLOW_PRIVATE_NETWORKS', 'False').lower() == 'true' # 下载超时(秒)。requests 支持 (connect, read),这里统一使用 read 超时。 V2_DOWNLOAD_TIMEOUT_SECONDS = float(os.getenv('V2_DOWNLOAD_TIMEOUT_SECONDS', 30)) V2_CONNECT_TIMEOUT_SECONDS = float(os.getenv('V2_CONNECT_TIMEOUT_SECONDS', 5)) @classmethod def get_upload_path(cls, filename: str) -> Path: """获取上传文件的完整路径""" return cls.UPLOAD_DIR / filename @classmethod def get_temp_path(cls, filename: str) -> Path: """获取临时文件的完整路径""" return cls.TEMP_DIR / filename # 日志配置 def setup_logging(): """设置日志系统""" logging.basicConfig( level=Settings.LOG_LEVEL, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler(Settings.LOG_DIR / 'app.log'), logging.StreamHandler() ] ) # 创建全局配置实例 settings = Settings() # 启用日志 setup_logging()