- 移除前端localStorage依赖,改用后端SQLite作为唯一数据源 - 新增getWanchuanConfig和saveWanchuanConfig函数用于配置读写 - 添加getBoundKnowledgeBase函数统一获取绑定知识库信息 - 支持桌面应用端口变化时正确读取配置 refactor(settings): 重构万川平台配置管理逻辑 - 移除localStorage配置存储,改为后端API调用 - 实现配置自动恢复和防抖保存机制 - 添加token过期自动重登功能 - 优化知识库选择和连接状态管理 fix(knowledge): 修复知识库上传异步问题 - 将getBoundKnowledgeBase调用改为await异步处理 - 统一各页面的知识库信息获取方式 - 修正上传接口datasetId使用逻辑 feat(electron): 添加chatlog.exe存在性检查 - 新增ensureChatlogExe函数验证执行文件存在 - 防止杀毒软件误删导致的ENONENT错误 - 提供用户友好的错误提示和解决方案
100 lines
3.4 KiB
Python
100 lines
3.4 KiB
Python
from fastapi import APIRouter, Depends
|
||
from pydantic import BaseModel
|
||
from typing import Optional, Any
|
||
import json
|
||
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",
|
||
]
|
||
|
||
# 万川 AI 平台对接配置整体作为一条 JSON 存储,独立于上面的 AI 模型配置。
|
||
# 存到后端 SQLite(app_settings 表)后,配置不再依赖前端 localStorage 的 origin,
|
||
# 桌面应用(exe)即便后端端口/origin 变化也能跨次启动恢复平台地址、账号、密码与已选知识库。
|
||
WANCHUAN_KEY = "wanchuan_config"
|
||
|
||
|
||
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"}
|
||
|
||
|
||
# ── 万川 AI 平台对接配置(整体 JSON 持久化) ──────────────────
|
||
# 前端把 { platformUrl, username, password, selectedKbId, selectedKbInfo } 整体存这里。
|
||
# 桌面应用 origin 变化不影响该配置(不依赖 localStorage)。
|
||
|
||
@router.get("/wanchuan")
|
||
async def get_wanchuan_config(db: aiosqlite.Connection = Depends(get_db)):
|
||
async with db.execute(
|
||
"SELECT value FROM app_settings WHERE key = ?", (WANCHUAN_KEY,)
|
||
) as cur:
|
||
row = await cur.fetchone()
|
||
if not row or not row["value"]:
|
||
return {}
|
||
try:
|
||
return json.loads(row["value"])
|
||
except (ValueError, TypeError):
|
||
return {}
|
||
|
||
|
||
@router.put("/wanchuan")
|
||
async def update_wanchuan_config(body: dict[str, Any], db: aiosqlite.Connection = Depends(get_db)):
|
||
value = json.dumps(body, ensure_ascii=False)
|
||
await db.execute(
|
||
"INSERT INTO app_settings (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = ?",
|
||
(WANCHUAN_KEY, value, value),
|
||
)
|
||
await db.commit()
|
||
return {"status": "ok"}
|