# API Keys 数据库存储设计 ## 背景 将 AI 服务的 API Keys 从环境变量迁移到数据库存储,实现: - 运维便利:无需重启服务即可更新配置 - 安全集中管理:敏感信息统一管理 - Web 界面配置:管理员可通过设置页面直接管理 ## 设计决策 | 决策点 | 选择 | 原因 | |--------|------|------| | 管理范围 | 仅 AI API Keys | JWT_SECRET 启动时需要,保持环境变量 | | 存储方式 | 明文存储 | 简单直接,依赖数据库访问控制 | | 优先级 | 数据库优先 | 方便运行时覆盖环境变量 | | 访问权限 | 仅管理员 | Web 界面配置入口仅 admin 可见 | ## 数据模型 复用现有 `config` 表,使用 `api_key.` 前缀: ``` key | value ---------------------------|------------------ api_key.dashscope | sk-xxx... api_key.deepseek | sk-xxx... api_key.anthropic | sk-xxx... ``` ## API 接口 | 方法 | 路径 | 功能 | |------|------|------| | GET | `/api/admin/api-keys` | 获取所有 API Keys(掩码显示) | | GET | `/api/admin/api-keys/{provider}` | 获取指定 API Key 完整值 | | PUT | `/api/admin/api-keys/{provider}` | 设置/更新 API Key | | DELETE | `/api/admin/api-keys/{provider}` | 删除 API Key(回退到环境变量) | ## 需修改的文件 ### 1. src/vitals/core/database.py 新增函数: - `get_api_key(provider: str) -> str | None` - 读取 API Key - `set_api_key(provider: str, value: str)` - 保存 API Key - `delete_api_key(provider: str)` - 删除 API Key - `get_all_api_keys(masked: bool = True) -> dict` - 获取所有 API Keys ```python API_KEY_ENV_MAP = { "dashscope": "DASHSCOPE_API_KEY", "deepseek": "DEEPSEEK_API_KEY", "anthropic": "ANTHROPIC_API_KEY", } def get_api_key(provider: str) -> str | None: """获取 API Key,数据库优先,环境变量备用""" with get_connection() as (conn, cursor): cursor.execute( "SELECT value FROM config WHERE `key` = %s", (f"api_key.{provider}",) ) row = cursor.fetchone() if row and row["value"]: return row["value"] # 回退到环境变量 env_var = API_KEY_ENV_MAP.get(provider) return os.environ.get(env_var) if env_var else None ``` ### 2. src/vitals/vision/providers/qwen.py ```python # 修改前 api_key = os.environ.get("DASHSCOPE_API_KEY") # 修改后 from vitals.core import database as db api_key = db.get_api_key("dashscope") ``` ### 3. src/vitals/vision/providers/deepseek.py ```python # 修改前 api_key = os.environ.get("DEEPSEEK_API_KEY") # 修改后 from vitals.core import database as db api_key = db.get_api_key("deepseek") ``` ### 4. src/vitals/vision/analyzer.py ```python # 修改前 api_key = os.environ.get("ANTHROPIC_API_KEY") # 修改后 from vitals.core import database as db api_key = db.get_api_key("anthropic") ``` ### 5. src/vitals/web/app.py 新增管理员 API 接口: ```python @app.get("/api/admin/api-keys") async def get_api_keys(request: Request): """获取所有 API Keys(掩码显示)""" user = await get_current_user(request) if not user or not user.get("is_admin"): raise HTTPException(status_code=403) return db.get_all_api_keys(masked=True) @app.get("/api/admin/api-keys/{provider}") async def get_api_key(provider: str, request: Request): """获取指定 API Key 完整值""" # 权限检查... return {"provider": provider, "value": db.get_api_key(provider)} @app.put("/api/admin/api-keys/{provider}") async def set_api_key(provider: str, request: Request): """设置 API Key""" # 权限检查... body = await request.json() db.set_api_key(provider, body["value"]) return {"success": True} @app.delete("/api/admin/api-keys/{provider}") async def delete_api_key(provider: str, request: Request): """删除 API Key""" # 权限检查... db.delete_api_key(provider) return {"success": True} ``` ### 6. src/vitals/web/templates/settings.html 在设置页面增加 API 密钥管理卡片(仅管理员可见): ```html {% if user.is_admin %}

API 密钥配置

{% endif %} ``` ## 数据流 ``` 管理员 → Web界面 → /api/admin/api-keys → database.set_api_key() → config表 ↓ 食物识别 → qwen.py → database.get_api_key() → 查config表 → 有值返回 / 无值读环境变量 ``` ## 向后兼容 - 现有环境变量配置继续有效 - 数据库未配置时自动回退到环境变量 - 无需数据迁移,新功能即插即用