2025-09-26 17:54:05 +08:00
|
|
|
"""
|
|
|
|
|
应用配置管理
|
2025-09-27 21:51:58 +08:00
|
|
|
支持从环境变量和 .env.dev 文件加载配置
|
2025-09-26 17:54:05 +08:00
|
|
|
"""
|
|
|
|
|
from typing import List, Optional
|
|
|
|
|
from pydantic import Field, validator
|
|
|
|
|
from pydantic_settings import BaseSettings
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Settings(BaseSettings):
|
|
|
|
|
"""应用配置类"""
|
|
|
|
|
|
|
|
|
|
# === 应用基础配置 ===
|
|
|
|
|
PROJECT_NAME: str = "FastAPI Demo"
|
|
|
|
|
VERSION: str = "1.0.0"
|
|
|
|
|
DESCRIPTION: str = "A simple FastAPI learning project"
|
|
|
|
|
DEBUG: bool = False
|
|
|
|
|
ENVIRONMENT: str = Field(default="production", description="运行环境: development, testing, production")
|
|
|
|
|
|
|
|
|
|
# === 服务器配置 ===
|
|
|
|
|
HOST: str = "127.0.0.1"
|
|
|
|
|
PORT: int = 8000
|
|
|
|
|
WORKERS: int = 1
|
|
|
|
|
RELOAD: bool = False
|
|
|
|
|
LOG_LEVEL: str = "info"
|
|
|
|
|
|
|
|
|
|
# === API 配置 ===
|
|
|
|
|
API_V1_STR: str = "/api/v1"
|
|
|
|
|
DOCS_URL: Optional[str] = "/docs"
|
|
|
|
|
REDOC_URL: Optional[str] = "/redoc"
|
|
|
|
|
|
|
|
|
|
# === 数据库配置 ===
|
2025-09-28 08:40:27 +08:00
|
|
|
# MySQL 数据库配置(从环境变量或 .env 文件读取)
|
|
|
|
|
DB_HOST: str = Field(default="localhost", description="数据库主机地址")
|
|
|
|
|
DB_PORT: int = Field(default=3306, description="数据库端口")
|
|
|
|
|
DB_USER: str = Field(default="root", description="数据库用户名")
|
|
|
|
|
DB_PASSWORD: str = Field(default="", description="数据库密码")
|
|
|
|
|
DB_NAME: str = Field(default="fastapi_demo", description="数据库名称")
|
|
|
|
|
DB_CHARSET: str = Field(default="utf8mb4", description="数据库字符集")
|
2025-09-27 23:23:25 +08:00
|
|
|
|
|
|
|
|
# 数据库连接池配置
|
2025-09-26 17:54:05 +08:00
|
|
|
DB_ECHO: bool = False
|
|
|
|
|
DB_POOL_SIZE: int = 5
|
|
|
|
|
DB_MAX_OVERFLOW: int = 10
|
2025-09-27 23:23:25 +08:00
|
|
|
DB_POOL_TIMEOUT: int = 30
|
|
|
|
|
DB_POOL_RECYCLE: int = 3600
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def DATABASE_URL(self) -> str:
|
|
|
|
|
"""构建数据库连接URL"""
|
|
|
|
|
return f"mysql+pymysql://{self.DB_USER}:{self.DB_PASSWORD}@{self.DB_HOST}:{self.DB_PORT}/{self.DB_NAME}?charset={self.DB_CHARSET}"
|
2025-09-26 17:54:05 +08:00
|
|
|
|
|
|
|
|
# === Redis 配置 ===
|
|
|
|
|
REDIS_HOST: str = "localhost"
|
|
|
|
|
REDIS_PORT: int = 6379
|
|
|
|
|
REDIS_DB: int = 0
|
|
|
|
|
REDIS_PASSWORD: Optional[str] = None
|
|
|
|
|
|
|
|
|
|
# === CORS 配置 ===
|
|
|
|
|
CORS_ORIGINS: List[str] = ["*"]
|
|
|
|
|
CORS_ALLOW_CREDENTIALS: bool = True
|
|
|
|
|
CORS_ALLOW_METHODS: List[str] = ["*"]
|
|
|
|
|
CORS_ALLOW_HEADERS: List[str] = ["*"]
|
|
|
|
|
|
|
|
|
|
# === JWT 认证配置 ===
|
|
|
|
|
SECRET_KEY: str = Field(default="your-secret-key-change-in-production", min_length=32)
|
|
|
|
|
ALGORITHM: str = "HS256"
|
|
|
|
|
ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
|
|
|
|
|
REFRESH_TOKEN_EXPIRE_DAYS: int = 7
|
|
|
|
|
|
|
|
|
|
# === 邮件配置 ===
|
|
|
|
|
SMTP_HOST: Optional[str] = None
|
|
|
|
|
SMTP_PORT: int = 587
|
|
|
|
|
SMTP_USER: Optional[str] = None
|
|
|
|
|
SMTP_PASSWORD: Optional[str] = None
|
|
|
|
|
SMTP_FROM: Optional[str] = None
|
|
|
|
|
|
|
|
|
|
# === 文件上传配置 ===
|
|
|
|
|
UPLOAD_DIR: str = "./uploads"
|
|
|
|
|
MAX_UPLOAD_SIZE: int = 10485760 # 10MB
|
|
|
|
|
ALLOWED_EXTENSIONS: List[str] = ["jpg", "jpeg", "png", "pdf", "doc", "docx"]
|
|
|
|
|
|
|
|
|
|
# === 第三方 API 配置 ===
|
|
|
|
|
OPENAI_API_KEY: Optional[str] = None
|
|
|
|
|
STRIPE_API_KEY: Optional[str] = None
|
|
|
|
|
AWS_ACCESS_KEY_ID: Optional[str] = None
|
|
|
|
|
AWS_SECRET_ACCESS_KEY: Optional[str] = None
|
|
|
|
|
AWS_REGION: str = "us-east-1"
|
|
|
|
|
|
|
|
|
|
# === 监控配置 ===
|
|
|
|
|
SENTRY_DSN: Optional[str] = None
|
|
|
|
|
PROMETHEUS_ENABLED: bool = False
|
|
|
|
|
METRICS_PATH: str = "/metrics"
|
|
|
|
|
|
|
|
|
|
# === 限流配置 ===
|
|
|
|
|
RATE_LIMIT_ENABLED: bool = True
|
|
|
|
|
RATE_LIMIT_REQUESTS: int = 100
|
|
|
|
|
RATE_LIMIT_PERIOD: int = 60 # seconds
|
|
|
|
|
|
|
|
|
|
@validator("ENVIRONMENT")
|
|
|
|
|
def validate_environment(cls, v):
|
|
|
|
|
"""验证运行环境"""
|
|
|
|
|
allowed = ["development", "testing", "production"]
|
|
|
|
|
if v not in allowed:
|
|
|
|
|
raise ValueError(f"ENVIRONMENT must be one of {allowed}")
|
|
|
|
|
return v
|
|
|
|
|
|
|
|
|
|
@validator("CORS_ORIGINS", pre=True)
|
|
|
|
|
def parse_cors_origins(cls, v):
|
|
|
|
|
"""解析 CORS 来源配置"""
|
|
|
|
|
if isinstance(v, str):
|
|
|
|
|
return [origin.strip() for origin in v.split(",")]
|
|
|
|
|
return v
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def is_development(self) -> bool:
|
|
|
|
|
"""是否为开发环境"""
|
|
|
|
|
return self.ENVIRONMENT == "development"
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def is_production(self) -> bool:
|
|
|
|
|
"""是否为生产环境"""
|
|
|
|
|
return self.ENVIRONMENT == "production"
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def is_testing(self) -> bool:
|
|
|
|
|
"""是否为测试环境"""
|
|
|
|
|
return self.ENVIRONMENT == "testing"
|
|
|
|
|
|
|
|
|
|
class Config:
|
2025-09-28 08:40:27 +08:00
|
|
|
env_file = ".env"
|
2025-09-26 17:54:05 +08:00
|
|
|
env_file_encoding = "utf-8"
|
|
|
|
|
case_sensitive = True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 根据环境加载不同的配置文件
|
|
|
|
|
def get_settings() -> Settings:
|
|
|
|
|
"""获取配置实例"""
|
|
|
|
|
import os
|
|
|
|
|
env = os.getenv("ENVIRONMENT", "development")
|
|
|
|
|
|
2025-09-28 08:40:27 +08:00
|
|
|
# 优先级:环境特定配置 > .env 通用配置
|
|
|
|
|
env_files = [".env"]
|
2025-09-26 17:54:05 +08:00
|
|
|
if env == "development":
|
2025-09-28 08:40:27 +08:00
|
|
|
env_files.append(".env.dev")
|
2025-09-26 17:54:05 +08:00
|
|
|
elif env == "testing":
|
2025-09-28 08:40:27 +08:00
|
|
|
env_files.append(".env.test")
|
2025-09-26 17:54:05 +08:00
|
|
|
elif env == "production":
|
2025-09-28 08:40:27 +08:00
|
|
|
env_files.append(".env.prod")
|
2025-09-26 17:54:05 +08:00
|
|
|
|
|
|
|
|
# 尝试加载对应的环境配置文件
|
|
|
|
|
for env_file in reversed(env_files):
|
|
|
|
|
if os.path.exists(env_file):
|
|
|
|
|
return Settings(_env_file=env_file)
|
|
|
|
|
|
|
|
|
|
return Settings()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 全局配置实例
|
|
|
|
|
settings = get_settings()
|