Files
mindfulness/server/app/core/config.py
2026-02-03 17:43:58 +08:00

127 lines
4.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from __future__ import annotations
from functools import lru_cache
from pathlib import Path
from typing import Literal, Optional
from pydantic import ValidationError
from pydantic_settings import BaseSettings, SettingsConfigDict
def _guess_env_file(app_env: str) -> Optional[str]:
"""
根据 APP_ENV 选择 env 文件。
说明:
- `.env.dev` / `.env.prod` 不进入仓库,通常由运维在部署时放到 `server/` 目录
- 如果文件不存在,则返回 None让系统从“真实环境变量”读取
"""
base_dir = Path(__file__).resolve().parents[2] # .../server/app -> .../server
candidates = {
"dev": base_dir / ".env.dev",
"prod": base_dir / ".env.prod",
}
p = candidates.get(app_env)
if p and p.exists():
return str(p)
return None
class Settings(BaseSettings):
"""
应用配置(统一从环境变量读取)。
注意:请不要把真实账号密码写入仓库;使用 `.env.dev/.env.prod` 或部署系统注入。
"""
app_env: Literal["dev", "prod"] = "dev"
app_name: str = "mindfulness-server"
app_host: str = "0.0.0.0"
app_port: int = 8000
database_url: str
redis_url: str
celery_broker_url: str
celery_result_backend: Optional[str] = None
# 法务协议链接(由后端统一托管并下发给客户端)
# 说明:
# - 当前仅支持 EN / TC 两种语言(与客户端现状一致)
# - 若未显式配置 LEGAL_*,后端会使用“内置协议页面”(/v1/legal/privacy、/v1/legal/terms
# - 线上建议配置为你们的官网/托管页/静态站点 HTTPS 链接,避免与 API 域名强绑定
#
# 这里用一个内部哨兵值表示“走内置页面”,避免默认值误导为可用的公网链接。
legal_privacy_url_en: str = "__internal__"
legal_terms_url_en: str = "__internal__"
legal_privacy_url_tc: Optional[str] = None
legal_terms_url_tc: Optional[str] = None
# Expo Push可选
# 说明:
# - 不填也能调用 Expo Push API但会受更严格的速率限制
# - 建议生产环境配置 EXPO_ACCESS_TOKEN便于稳定性与配额
expo_access_token: Optional[str] = None
model_config = SettingsConfigDict(
env_prefix="",
case_sensitive=False,
extra="ignore",
)
@lru_cache
def get_settings() -> Settings:
"""
获取配置(带缓存)。
加载顺序:
- 优先使用 `.env.dev/.env.prod`(若存在)
- 否则使用系统环境变量
"""
import os
env = os.getenv("APP_ENV", "dev").strip() or "dev"
env_file = _guess_env_file(env)
try:
return Settings(_env_file=env_file)
except ValidationError as e:
# 给出更可执行的错误信息,避免只看到一串校验栈。
missing_fields: list[str] = []
for err in e.errors():
if err.get("type") == "missing":
loc = err.get("loc") or ()
if loc:
missing_fields.append(str(loc[0]))
# 将字段名映射为常见的环境变量名(默认规则:字段名大写)
missing_env_keys = [f.upper() for f in missing_fields] if missing_fields else []
env_file_hint = env_file or f".env.{env}(未找到,已回退到系统环境变量)"
required_hint = (
"".join(missing_env_keys)
if missing_env_keys
else "DATABASE_URL、REDIS_URL、CELERY_BROKER_URL"
)
msg = (
"应用启动失败:缺少必填配置。\n\n"
f"- 当前 APP_ENV{env}\n"
f"- 期望读取的 env 文件:{env_file_hint}\n"
f"- 缺少的环境变量:{required_hint}\n\n"
"修复方式(任选其一):\n"
"1) 直接注入环境变量(推荐):\n"
" - DATABASE_URL=...\n"
" - REDIS_URL=...\n"
" - CELERY_BROKER_URL=...\n"
"2) 使用 env 文件:在 `server/` 下准备 `.env.dev` 或 `.env.prod`KEY=value 格式),\n"
" 本地可通过 `server/run.sh --env dev|prod` 自动加载Docker 运行可用 `--env-file` 传入。\n"
)
# 不附带原始 ValidationError 的异常上下文,减少日志噪音;
# msg 已包含缺失项与修复方式,足够定位问题。
raise RuntimeError(msg) from None