xtc-backend / app /config.py
a3216's picture
sync from GitHub fdeda92: feat: 添加/admin页面访问门禁配置,增强安全性
302c7e7 verified
Raw
History Blame Contribute Delete
4.92 kB
"""全局配置:从环境变量加载(pydantic-settings)。
简化点(相比原 Netlify 版):
- 不再支持 XTC_PROVIDER_N_* 多槽位环境变量,仅用 GEMINI/OPENAI 做种子
- 不再支持配置加密(XTC_CONFIG_CRYPT_KEY)
- 不再支持 XTC_CONFIG_ENV_ONLY / XTC_ADMIN_REQUIRE_KEY_ON_PAGE / XTC_ACCESS_SHORT_KEY / XTC_ACCESS_PIN
- 不再支持 Edge 路径与三段式 env 读取
- 新增 XTC_JWT_SECRET 用于签发临时令牌
- 存储主体为持久卷(/data/xtc.db);配置文件额外通过 HF Hub dataset 仓库做备份
"""
from __future__ import annotations
import os
from functools import lru_cache
from typing import List, Optional
from pydantic import Field, field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
def _split_key_list(value: Optional[str]) -> List[str]:
if not value:
return []
out = []
for item in str(value).replace("\n", ",").replace(";", ",").split(","):
s = item.strip()
if s:
out.append(s)
return list(dict.fromkeys(out)) # 去重保序
class Settings(BaseSettings):
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
extra="ignore",
case_sensitive=False,
)
# ===== 鉴权 =====
xtc_admin_key: str = Field(default="", alias="XTC_ADMIN_KEY")
xtc_access_key: str = Field(default="", alias="XTC_ACCESS_KEY")
xtc_jwt_secret: str = Field(default="", alias="XTC_JWT_SECRET")
# ===== 紧急开关 =====
xtc_disable_admin: bool = Field(default=False, alias="XTC_DISABLE_ADMIN")
# ===== /admin 页面访问门禁(可选)=====
# 配置后访问 /admin 必须携带 ?k=<token> 才能进入登录页,
# 否则返回 404 伪装该路径不存在。未配置时 /admin 行为不变。
# 注意:此为页面门禁,/admin/api/* 仍由 XTC_ADMIN_KEY 鉴权。
xtc_admin_access_token: str = Field(default="", alias="XTC_ADMIN_ACCESS_TOKEN")
# ===== 种子厂商 =====
gemini_api_key: str = Field(default="", alias="GEMINI_API_KEY")
openai_api_key: str = Field(default="", alias="OPENAI_API_KEY")
openai_base_url: str = Field(default="https://api.openai.com", alias="OPENAI_BASE_URL")
# ===== 超时 =====
upstream_timeout_ms: int = Field(default=120000, alias="UPSTREAM_TIMEOUT_MS")
upstream_stream_timeout_ms: int = Field(default=120000, alias="UPSTREAM_STREAM_TIMEOUT_MS")
# ===== HF Hub 配置备份(可选)=====
# 配置文件通过 HF Hub dataset 仓库做持久化备份,解决免费档磁盘 ephemeral 问题
# 未配置时降级为纯本地存储(功能正常,只是 restart/stop 后配置会丢失)
hf_token: str = Field(default="", alias="HF_TOKEN")
hf_config_repo: str = Field(default="", alias="HF_CONFIG_REPO")
# ===== 临时令牌 =====
xtc_access_token_ttl_sec: int = Field(default=1800, alias="XTC_ACCESS_TOKEN_TTL_SEC")
xtc_access_token_length: int = Field(default=8, alias="XTC_ACCESS_TOKEN_LENGTH")
# ===== 运行 =====
port: int = Field(default=7860, alias="PORT")
# ===== 数据库路径 =====
db_path: str = Field(default="/data/xtc.db", alias="XTC_DB_PATH")
@field_validator("xtc_access_token_length")
@classmethod
def _clamp_token_length(cls, v: int) -> int:
return max(6, min(24, v))
@field_validator("xtc_access_token_ttl_sec")
@classmethod
def _clamp_token_ttl(cls, v: int) -> int:
return max(60, min(86400, v))
@property
def gemini_api_keys(self) -> List[str]:
# 兼容 GEMINI_API_KEYS(逗号分隔)
return _split_key_list(os.environ.get("GEMINI_API_KEYS")) or _split_key_list(self.gemini_api_key)
@property
def openai_api_keys(self) -> List[str]:
return _split_key_list(os.environ.get("OPENAI_API_KEYS")) or _split_key_list(self.openai_api_key)
@property
def is_admin_enabled(self) -> bool:
return not self.xtc_disable_admin
@property
def has_admin_key(self) -> bool:
return bool(self.xtc_admin_key)
@property
def has_admin_access_token(self) -> bool:
return bool(self.xtc_admin_access_token)
@property
def has_access_key(self) -> bool:
return bool(self.xtc_access_key)
@property
def has_jwt_secret(self) -> bool:
return bool(self.xtc_jwt_secret)
@lru_cache(maxsize=1)
def get_settings() -> Settings:
return Settings()
# Gemini 上游地址(固定)
GEMINI_BASE_URL = "https://generativelanguage.googleapis.com"
GEMINI_API_VERSION = "v1beta"
GEMINI_API_CLIENT = "google-genai-sdk/1.34.0"
GEMINI_DEFAULT_MODEL = "gemini-flash-latest"
GEMINI_DEFAULT_EMBEDDINGS_MODEL = "gemini-embedding-001"
# RR 计数器数据库 key 前缀
RR_KEY_PREFIX = "rr:provider:"
# 伪流式会话 TTL
PSEUDO_SESSION_TTL_SEC = 10 * 60
# 模型列表缓存 TTL
MODELS_CACHE_TTL_SEC = 30