Files
item_bank/app/config.py
T
2026-08-01 16:50:55 +08:00

87 lines
3.1 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.
"""应用配置:从环境变量 / .env 读取。"""
from functools import lru_cache
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(
env_file=".env", env_file_encoding="utf-8", extra="ignore"
)
# 基础
app_name: str = "item-bank"
debug: bool = False
# 数据库:SQLiteaiosqlite 异步驱动)
# 生产环境(k3s)里指向 PVC 挂载路径,如 /data/item_bank.db
database_url: str = "sqlite+aiosqlite:///./data/item_bank.db"
# 认证
jwt_secret: str = "dev-only-change-me" # 生产由 k8s Secret 注入
jwt_algorithm: str = "HS256"
access_token_expire_minutes: int = 60 * 24 # 24h
# 用户 AI 配置加密密钥(Fernet),生产由 Secret 注入
# 生成:python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
fernet_key: str = ""
# 对象存储(S3 兼容),存上传图片
s3_endpoint_url: str | None = None # 如 https://s3.amazonaws.com 或自建 endpoint
s3_region: str | None = None
s3_access_key: str | None = None
s3_secret_key: str | None = None
s3_bucket: str | None = None
# 上传限制
max_upload_bytes: int = 10 * 1024 * 1024 # 10 MB
allowed_image_mimes: tuple[str, ...] = (
"image/jpeg",
"image/png",
"image/webp",
)
# OCR 服务(Pix2Text 独立微服务,可选;未配置则只走多模态 VLM)
ocr_service_url: str | None = None
# CORS:允许的前端来源(逗号分隔),生产填实际域名
cors_origins: str = "http://localhost:5173"
# 限流(slowapi 语法,如 "5/minute")。测试时可用 RATE_LIMIT_ENABLED=false 关掉
rate_limit_enabled: bool = True
rate_limit_auth: str = "10/minute" # 登录/注册:防爆破,按 IP
rate_limit_ai: str = "20/minute" # AI 端点:防刷额度,按用户
rate_limit_upload: str = "30/minute" # 图片上传
@property
def cors_origin_list(self) -> list[str]:
return [o.strip() for o in self.cors_origins.split(",") if o.strip()]
def check_production_secrets(self) -> list[str]:
"""返回生产环境下的配置问题清单(debug 模式不检查)。
这个服务会暴露到公网,弱密钥或忘改的默认值是实际风险,
启动时报出来比事后发现好。
"""
problems: list[str] = []
if self.debug:
return problems
if self.jwt_secret == "dev-only-change-me":
problems.append("JWT_SECRET 仍是默认值,必须改为强随机值")
elif len(self.jwt_secret.encode()) < 32:
# HS256 的密钥短于 32 字节会降低签名强度(RFC 7518 §3.2
problems.append("JWT_SECRET 少于 32 字节,请用更长的随机值")
if not self.fernet_key:
problems.append("FERNET_KEY 未配置,用户无法保存 AI api_key")
if "*" in self.cors_origins:
problems.append("CORS_ORIGINS 不应含通配符")
return problems
@lru_cache
def get_settings() -> Settings:
return Settings()
settings = get_settings()