from fastapi import APIRouter, Depends from pydantic import BaseModel from typing import Optional import aiosqlite from database import get_db router = APIRouter(prefix="/api/settings", tags=["settings"]) EDITABLE_KEYS = [ "ai_base_url", "ai_api_key", "ai_model", "summary_model", "vision_model", "voice_model", "topic_analysis_prompt", ] def _mask_key(value: str) -> str: if not value or len(value) <= 8: return "*" * len(value) if value else "" return value[:3] + "*" * (len(value) - 7) + value[-4:] @router.get("") async def get_settings(db: aiosqlite.Connection = Depends(get_db)): result = {} placeholders = ",".join("?" for _ in EDITABLE_KEYS) async with db.execute( f"SELECT key, value FROM app_settings WHERE key IN ({placeholders})", EDITABLE_KEYS, ) as cur: rows = await cur.fetchall() for row in rows: k, v = row["key"], row["value"] result[k] = _mask_key(v) if k == "ai_api_key" else v for k in EDITABLE_KEYS: if k not in result: result[k] = "" return result class SettingsUpdate(BaseModel): ai_base_url: Optional[str] = None ai_api_key: Optional[str] = None ai_model: Optional[str] = None summary_model: Optional[str] = None vision_model: Optional[str] = None voice_model: Optional[str] = None topic_analysis_prompt: Optional[str] = None @router.put("") async def update_settings(body: SettingsUpdate, db: aiosqlite.Connection = Depends(get_db)): updates = body.model_dump(exclude_none=True) for k, v in updates.items(): if k not in EDITABLE_KEYS: continue if k == "ai_api_key" and "*" in v: continue await db.execute( "INSERT INTO app_settings (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = ?", (k, v, v), ) await db.commit() from services.runtime_settings import invalidate_cache invalidate_cache() return {"status": "ok"}