109 lines
3.3 KiB
Python
109 lines
3.3 KiB
Python
# settings.py
|
||
# 统一配置入口:所有可变参数都从环境变量 / .env 读取,代码里不再出现硬编码密码。
|
||
#
|
||
# 真实业务里"配置"和"代码"必须分离:
|
||
# 开发 / 测试 / 生产 用的是三套数据库,靠改代码切环境一定会出事故。
|
||
|
||
import os
|
||
from pathlib import Path
|
||
|
||
|
||
BASE_DIR = Path(__file__).resolve().parent
|
||
|
||
|
||
def _load_dotenv(path: Path) -> None:
|
||
"""极简 .env 加载器。
|
||
|
||
优先使用 python-dotenv;没装则用内置的简易解析,保证零依赖也能跑。
|
||
"""
|
||
if not path.exists():
|
||
return
|
||
try:
|
||
from dotenv import load_dotenv # type: ignore
|
||
|
||
load_dotenv(path, override=False)
|
||
return
|
||
except ImportError:
|
||
pass
|
||
|
||
for raw in path.read_text(encoding="utf-8").splitlines():
|
||
line = raw.strip()
|
||
if not line or line.startswith("#") or "=" not in line:
|
||
continue
|
||
key, _, value = line.partition("=")
|
||
key, value = key.strip(), value.strip().strip('"').strip("'")
|
||
# 已存在的真实环境变量优先,不被文件覆盖
|
||
os.environ.setdefault(key, value)
|
||
|
||
|
||
_load_dotenv(BASE_DIR / ".env")
|
||
|
||
|
||
def _env(key: str, default: str) -> str:
|
||
value = os.getenv(key)
|
||
return default if value is None or value == "" else value
|
||
|
||
|
||
def _env_bool(key: str, default: bool) -> bool:
|
||
return _env(key, str(default)).strip().lower() in ("1", "true", "yes", "on")
|
||
|
||
|
||
def _env_int(key: str, default: int) -> int:
|
||
try:
|
||
return int(_env(key, str(default)))
|
||
except ValueError:
|
||
return default
|
||
|
||
|
||
class Settings:
|
||
# ---------- 应用 ----------
|
||
APP_NAME: str = _env("APP_NAME", "学生管理系统")
|
||
APP_VERSION: str = _env("APP_VERSION", "1.1.0")
|
||
DEBUG: bool = _env_bool("DEBUG", True)
|
||
|
||
HOST: str = _env("HOST", "127.0.0.1")
|
||
PORT: int = _env_int("PORT", 8004)
|
||
|
||
# API 统一前缀。改这里就能整体换版本,不用一个个改路由。
|
||
API_PREFIX: str = _env("API_PREFIX", "/api/v1")
|
||
|
||
# ---------- 跨域 ----------
|
||
# 注意:allow_credentials=True 时浏览器【禁止】返回 "*",
|
||
# 必须显式列出前端域名,否则带 Cookie / 登录态一定失败。
|
||
CORS_ORIGINS: list[str] = [
|
||
o.strip()
|
||
for o in _env(
|
||
"CORS_ORIGINS",
|
||
"http://localhost:5500,http://127.0.0.1:5500,"
|
||
"http://localhost:8000,http://127.0.0.1:8000,"
|
||
"http://localhost:5173,http://127.0.0.1:5173",
|
||
).split(",")
|
||
if o.strip()
|
||
]
|
||
|
||
# ---------- 数据库 ----------
|
||
DB_HOST: str = _env("DB_HOST", "127.0.0.1")
|
||
DB_PORT: int = _env_int("DB_PORT", 3306)
|
||
DB_USER: str = _env("DB_USER", "root")
|
||
DB_PASSWORD: str = _env("DB_PASSWORD", "")
|
||
DB_NAME: str = _env("DB_NAME", "student_management_system")
|
||
DB_CHARSET: str = _env("DB_CHARSET", "utf8mb4")
|
||
|
||
DB_ECHO: bool = _env_bool("DB_ECHO", False)
|
||
DB_POOL_SIZE: int = _env_int("DB_POOL_SIZE", 5)
|
||
DB_MAX_OVERFLOW: int = _env_int("DB_MAX_OVERFLOW", 10)
|
||
DB_POOL_RECYCLE: int = _env_int("DB_POOL_RECYCLE", 3600)
|
||
|
||
@property
|
||
def database_url(self) -> str:
|
||
from urllib.parse import quote_plus
|
||
|
||
return (
|
||
f"mysql+pymysql://{self.DB_USER}:{quote_plus(self.DB_PASSWORD)}"
|
||
f"@{self.DB_HOST}:{self.DB_PORT}/{self.DB_NAME}"
|
||
f"?charset={self.DB_CHARSET}"
|
||
)
|
||
|
||
|
||
settings = Settings()
|