fdb664e546
对多个文件进行代码格式化调整,将长行参数拆分为多行书写,提升代码可读性,包括: - 调整函数定义、调用的多行换行格式 - 优化列表、元组、字典的多行排版 - 新增README.md项目说明文档
94 lines
3.2 KiB
Python
94 lines
3.2 KiB
Python
from collections.abc import AsyncIterator
|
|
from contextlib import asynccontextmanager
|
|
from pathlib import Path
|
|
|
|
import structlog
|
|
from fastapi import FastAPI, Request
|
|
from fastapi.exceptions import RequestValidationError
|
|
from fastapi.responses import FileResponse, JSONResponse
|
|
|
|
from app.api.response import ApiError, error
|
|
from app.api.v1.auth import router as auth_router
|
|
from app.api.v1.document import router as document_router
|
|
from app.api.v1.knowledge import router as knowledge_router
|
|
from app.api.v1.search import router as search_router
|
|
from app.config import settings
|
|
from app.core.auth import ensure_default_admin
|
|
from app.services.qdrant import QdrantService
|
|
|
|
logger = structlog.get_logger()
|
|
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(_: FastAPI) -> AsyncIterator[None]:
|
|
"""应用生命周期:启动时初始化 Qdrant 集合与默认管理员
|
|
|
|
初始化失败仅记录日志、不阻止启动(本地开发可能无 Qdrant/Redis)。
|
|
"""
|
|
try:
|
|
await QdrantService().ensure_collections()
|
|
logger.info("Qdrant 集合初始化完成")
|
|
except Exception:
|
|
logger.error("Qdrant 集合初始化失败,跳过初始化继续启动")
|
|
try:
|
|
await ensure_default_admin()
|
|
except Exception:
|
|
logger.warning("默认管理员初始化失败,跳过", exc_info=True)
|
|
try:
|
|
Path(settings.upload_dir).mkdir(parents=True, exist_ok=True)
|
|
logger.info("上传目录已就绪", upload_dir=settings.upload_dir)
|
|
except Exception:
|
|
logger.warning("上传目录初始化失败,文件落盘将按需创建", exc_info=True)
|
|
yield
|
|
|
|
|
|
app = FastAPI(
|
|
title=settings.app_name,
|
|
description="AI Agent 分层信息检索服务",
|
|
version="0.1.0",
|
|
lifespan=lifespan,
|
|
)
|
|
|
|
app.include_router(auth_router)
|
|
app.include_router(search_router)
|
|
app.include_router(document_router)
|
|
app.include_router(knowledge_router)
|
|
|
|
|
|
@app.exception_handler(ApiError)
|
|
async def api_error_handler(request: Request, exc: ApiError) -> JSONResponse:
|
|
"""业务异常 → 统一错误响应(code 取自异常)"""
|
|
return JSONResponse(content=error(exc.code, exc.message))
|
|
|
|
|
|
@app.exception_handler(RequestValidationError)
|
|
async def validation_error_handler(
|
|
request: Request, exc: RequestValidationError
|
|
) -> JSONResponse:
|
|
"""请求参数校验失败 → code 1001"""
|
|
errors = exc.errors()
|
|
message = f"请求参数校验失败: {errors[0]['msg']}" if errors else "请求参数校验失败"
|
|
return JSONResponse(content=error(1001, message))
|
|
|
|
|
|
@app.exception_handler(Exception)
|
|
async def unhandled_error_handler(request: Request, exc: Exception) -> JSONResponse:
|
|
"""未捕获异常 → code 2000"""
|
|
logger.exception("未捕获异常", path=request.url.path)
|
|
return JSONResponse(content=error(2000, "服务器内部错误"))
|
|
|
|
|
|
@app.get("/api/v1/health")
|
|
async def health() -> dict[str, str]:
|
|
"""健康检查"""
|
|
return {"status": "ok"}
|
|
|
|
|
|
_ADMIN_HTML = Path(__file__).resolve().parent / "static" / "admin.html"
|
|
|
|
|
|
@app.get("/admin", include_in_schema=False)
|
|
async def admin_page() -> FileResponse:
|
|
"""管理后台单页(单文件静态 HTML,零外部依赖)"""
|
|
return FileResponse(_ADMIN_HTML, media_type="text/html")
|