bdd30f0a88
- deps.py: _create_redis_client 增加同步 ping 校验,Redis 不可达时正确降级为内存模式 - main.py: lifespan 复用 deps 的 UserStore 单例,避免 admin 与 API 请求实例不一致 - 前端: 强制改密弹窗(must_change_password 用户)、兼容新旧登录返回格式、markPasswordChanged - 新增 NAS SSH 部署脚本与批处理测试;gitignore 前端构建产物
116 lines
3.9 KiB
Python
116 lines
3.9 KiB
Python
"""运行时配置 API:GET/PUT /api/v1/settings、GET /api/v1/settings/schema、POST /api/v1/settings/reset
|
||
|
||
GET 任何登录用户可读;PUT/reset 需 admin。PUT 后会清空 LLM 客户端 / 解析插件 /
|
||
去重策略三处进程级缓存,使新配置立即对后续请求生效。
|
||
"""
|
||
|
||
from typing import Any
|
||
|
||
import structlog
|
||
from fastapi import APIRouter, Depends
|
||
from pydantic import BaseModel
|
||
|
||
from app.api.response import ApiError, ok
|
||
from app.api.deps import get_current_user, require_admin
|
||
from app.core.users import UserRecord
|
||
from app.core.dedup import invalidate_dedup_strategy_cache
|
||
from app.core.file_parser import (
|
||
list_docx_plugins,
|
||
list_ocr_plugins,
|
||
list_pdf_plugins,
|
||
invalidate_parser_plugin_cache,
|
||
)
|
||
from app.core.runtime_settings import (
|
||
RuntimeSettings,
|
||
get_runtime_settings,
|
||
reset_runtime_settings,
|
||
update_runtime_settings,
|
||
)
|
||
from app.services.llm import invalidate_llm_client_cache
|
||
|
||
logger = structlog.get_logger()
|
||
|
||
router = APIRouter(prefix="/api/v1", tags=["settings"])
|
||
|
||
|
||
class SettingsUpdateRequest(BaseModel):
|
||
"""Settings PATCH body:任意子树可缺省,缺省字段保留原值
|
||
|
||
例:{"models": {"summarize": {"model": "qwen2.5:3b"}}}、
|
||
{"parsers": {"ocr": {"plugin": "tesseract"}}}、
|
||
{"dedup": {"strategy": "simhash", "simhash_threshold": 5}}
|
||
"""
|
||
|
||
models: dict[str, Any] | None = None
|
||
parsers: dict[str, Any] | None = None
|
||
dedup: dict[str, Any] | None = None
|
||
|
||
|
||
@router.get("/settings")
|
||
async def get_settings(user: UserRecord = Depends(get_current_user)) -> dict[str, Any]:
|
||
"""返回当前 RuntimeSettings(任何登录用户可读)"""
|
||
cfg = get_runtime_settings()
|
||
return ok(cfg.model_dump(mode="json"))
|
||
|
||
|
||
@router.put("/settings")
|
||
async def update_settings(
|
||
body: SettingsUpdateRequest,
|
||
user: UserRecord = Depends(require_admin),
|
||
) -> dict[str, Any]:
|
||
"""部分更新 RuntimeSettings(仅 admin)
|
||
|
||
更新成功后清空 LLM 客户端 / 解析插件 / 去重策略三处进程级缓存,
|
||
使新配置立即对后续请求生效。
|
||
"""
|
||
patch = body.model_dump(exclude_none=True)
|
||
if not patch:
|
||
raise ApiError(1001, "请求体为空,未提供任何待更新字段")
|
||
|
||
try:
|
||
cfg = update_runtime_settings(patch)
|
||
except Exception as exc:
|
||
logger.error("RuntimeSettings 更新失败", error=str(exc), exc_info=True)
|
||
raise ApiError(2000, f"配置更新失败: {exc}") from exc
|
||
|
||
# 清缓存:让后续读取拿到新配置
|
||
invalidate_llm_client_cache()
|
||
invalidate_parser_plugin_cache()
|
||
invalidate_dedup_strategy_cache()
|
||
logger.info("RuntimeSettings 已更新并清理缓存", operator=user.username)
|
||
return ok(cfg.model_dump(mode="json"))
|
||
|
||
|
||
@router.get("/settings/schema")
|
||
async def get_settings_schema(
|
||
user: UserRecord = Depends(get_current_user),
|
||
) -> dict[str, Any]:
|
||
"""返回可选插件与策略列表(前端 Settings 页渲染选项用)"""
|
||
return ok(
|
||
{
|
||
"llm_providers": ["ollama", "openai_compatible"],
|
||
"pdf_plugins": list_pdf_plugins(),
|
||
"docx_plugins": list_docx_plugins(),
|
||
"ocr_plugins": list_ocr_plugins(),
|
||
"dedup_strategies": ["none", "sha256", "simhash"],
|
||
}
|
||
)
|
||
|
||
|
||
@router.post("/settings/reset")
|
||
async def reset_settings(
|
||
user: UserRecord = Depends(require_admin),
|
||
) -> dict[str, Any]:
|
||
"""重置 RuntimeSettings 为默认值(仅 admin),同时清缓存"""
|
||
try:
|
||
cfg = reset_runtime_settings()
|
||
except Exception as exc:
|
||
logger.error("RuntimeSettings 重置失败", error=str(exc), exc_info=True)
|
||
raise ApiError(2000, f"配置重置失败: {exc}") from exc
|
||
|
||
invalidate_llm_client_cache()
|
||
invalidate_parser_plugin_cache()
|
||
invalidate_dedup_strategy_cache()
|
||
logger.info("RuntimeSettings 已重置为默认值", operator=user.username)
|
||
return ok(cfg.model_dump(mode="json"))
|