| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120 |
- import os
- from functools import lru_cache
- from pathlib import Path
- from pydantic_settings import BaseSettings, SettingsConfigDict
- _ROOT = Path(__file__).resolve().parents[2]
- # 根据环境变量或系统环境确定要加载的 .env 文件
- # 优先级:
- # 1. 系统环境变量 ENV 指定的 .env.{env} 文件
- # 2. 存在的 .env.production/.env.test/.env.dev 文件(按优先级)
- # 3. .env 文件
- # 4. env.example 文件
- def _get_env_file() -> Path:
- # 首先检查系统环境变量 ENV
- env = os.getenv("ENV", "").lower()
-
- # 如果系统环境变量已设置,优先使用对应的环境文件
- if env:
- if env == "prod" or env == "production":
- env_file = _ROOT / ".env.production"
- if env_file.exists():
- return env_file
- elif env == "test" or env == "testing":
- env_file = _ROOT / ".env.test"
- if env_file.exists():
- return env_file
- elif env == "dev" or env == "development":
- env_file = _ROOT / ".env.dev"
- if env_file.exists():
- return env_file
-
- # 如果系统环境变量未设置,按优先级检查环境文件是否存在
- # 优先检查生产环境文件(常见于生产服务器)
- env_file = _ROOT / ".env.production"
- if env_file.exists():
- return env_file
-
- env_file = _ROOT / ".env.test"
- if env_file.exists():
- return env_file
-
- env_file = _ROOT / ".env.dev"
- if env_file.exists():
- return env_file
-
- # 回退到 .env
- env_file = _ROOT / ".env"
- if env_file.exists():
- return env_file
-
- # 最后回退到 env.example
- return _ROOT / "env.example"
- _ENV_FILE = _get_env_file()
- class Settings(BaseSettings):
- model_config = SettingsConfigDict(
- env_file=_ENV_FILE,
- env_file_encoding="utf-8",
- extra="ignore",
- )
- # 环境配置
- env: str = "dev" # dev, test, prod
- # 数据库配置
- database_url: str = "mysql+asyncmy://root:password@localhost:3306/dibang?charset=utf8mb4" # MySQL 数据库连接字符串,格式: mysql+asyncmy://user:password@host:port/database?charset=utf8mb4
- # JWT 配置
- jwt_secret: str = "dev-secret-key-change-in-production" # JWT 密钥,生产环境必须设置强密钥
- access_token_expires_minutes: int = 60 # 访问令牌过期时间(分钟)
- refresh_token_expires_days: int = 14 # 刷新令牌过期时间(天)
- # 服务器配置
- host: str = "0.0.0.0" # 服务器监听地址
- port: int = 8000 # 服务器端口
- # CORS 配置
- cors_origins: str = "*" # CORS 允许的源,多个用逗号分隔,如: http://localhost:3000,http://localhost:8080
- cors_allow_credentials: bool = True # 是否允许携带凭证
- cors_allow_methods: str = "*" # 允许的 HTTP 方法,多个用逗号分隔
- cors_allow_headers: str = "*" # 允许的 HTTP 头,多个用逗号分隔
- # 数据库连接池配置
- db_pool_size: int = 10 # 连接池中保持的连接数
- db_max_overflow: int = 20 # 连接池外可以额外创建的连接数
- db_pool_timeout: int = 30 # 从连接池获取连接的超时时间(秒)
- db_pool_recycle: int = 3600 # 连接回收时间(秒),避免 MySQL wait_timeout 导致的连接过期
- db_pool_pre_ping: bool = True # 每次使用连接前先 ping,确保连接有效
- @property
- def cors_origins_list(self) -> list[str]:
- """将 CORS origins 字符串转换为列表"""
- if self.cors_origins == "*":
- return ["*"]
- return [origin.strip() for origin in self.cors_origins.split(",")]
- @property
- def cors_allow_methods_list(self) -> list[str]:
- """将 CORS methods 字符串转换为列表"""
- if self.cors_allow_methods == "*":
- return ["*"]
- return [method.strip() for method in self.cors_allow_methods.split(",")]
- @property
- def cors_allow_headers_list(self) -> list[str]:
- """将 CORS headers 字符串转换为列表"""
- if self.cors_allow_headers == "*":
- return ["*"]
- return [header.strip() for header in self.cors_allow_headers.split(",")]
- @lru_cache(maxsize=1)
- def get_settings() -> Settings:
- return Settings()
|