fix: 修复会话鉴权「登录后 token 无效」并补齐 NAS 部署流程
- deps.py: _create_redis_client 增加同步 ping 校验,Redis 不可达时正确降级为内存模式 - main.py: lifespan 复用 deps 的 UserStore 单例,避免 admin 与 API 请求实例不一致 - 前端: 强制改密弹窗(must_change_password 用户)、兼容新旧登录返回格式、markPasswordChanged - 新增 NAS SSH 部署脚本与批处理测试;gitignore 前端构建产物
This commit is contained in:
+9
-10
@@ -6,7 +6,6 @@ import structlog
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.exceptions import RequestValidationError
|
||||
from fastapi.responses import FileResponse, JSONResponse, RedirectResponse
|
||||
from redis import asyncio as redis_async
|
||||
|
||||
from app.api.response import ApiError, error
|
||||
from app.api.v1.auth import router as auth_router
|
||||
@@ -15,7 +14,7 @@ from app.api.v1.knowledge import router as knowledge_router
|
||||
from app.api.v1.search import router as search_router
|
||||
from app.api.v1.settings import router as settings_router
|
||||
from app.config import settings
|
||||
from app.core.users import UserStore, bootstrap_admin
|
||||
from app.core.users import bootstrap_admin
|
||||
from app.services.qdrant import QdrantService
|
||||
|
||||
logger = structlog.get_logger()
|
||||
@@ -33,13 +32,13 @@ async def lifespan(_: FastAPI) -> AsyncIterator[None]:
|
||||
except Exception:
|
||||
logger.error("Qdrant 集合初始化失败,跳过初始化继续启动")
|
||||
try:
|
||||
# Redis 客户端创建失败(如 URL 非法)时传 None,UserStore 降级为内存模式
|
||||
redis_client = redis_async.from_url(settings.redis_url, decode_responses=True)
|
||||
except Exception:
|
||||
redis_client = None
|
||||
try:
|
||||
# 复用 deps 的 UserStore 单例,确保 lifespan 创建的 admin 与 API 请求用的是同一实例
|
||||
# _get_user_store 内部会检测 Redis 可达性,不可达时降级为内存模式
|
||||
from app.api.deps import _get_user_store
|
||||
|
||||
user_store = _get_user_store()
|
||||
# 空库引导默认管理员;明文密码由 bootstrap_admin 内部 warning 打印一次
|
||||
await bootstrap_admin(UserStore(redis_client), logger)
|
||||
await bootstrap_admin(user_store, logger)
|
||||
except Exception:
|
||||
logger.warning("默认管理员初始化失败,跳过", exc_info=True)
|
||||
try:
|
||||
@@ -150,8 +149,8 @@ async def admin_spa(rest: str):
|
||||
_AGENT_SKILL_ZIP = _STATIC_DIR / "agent-skill" / "QMDSearch-Agent-Skill.zip"
|
||||
|
||||
|
||||
@app.get("/agent-skill", include_in_schema=False)
|
||||
async def agent_skill_download() -> FileResponse | JSONResponse:
|
||||
@app.get("/agent-skill", include_in_schema=False, response_model=None)
|
||||
async def agent_skill_download():
|
||||
"""下载 AI Agent Skill 压缩包(qmdsearch-agent skill,含 SKILL.md 与示例)"""
|
||||
if not _AGENT_SKILL_ZIP.is_file():
|
||||
return JSONResponse(
|
||||
|
||||
Reference in New Issue
Block a user