Files
my_bookmate/backend/app/main.py
T
gavindiazandClaude Code bbf4cebd90 fix: 优雅停机时主动 dispose 连接池,消除 SQLite 后台连接被 GC 丢弃的 SAWarning
Ctrl+C 停服时 aiosqlite 后台线程上的连接若未归还池会被 GC 直接丢弃,
表现为 shutdown 时的 non-checked-in connection SAWarning。在 lifespan 关闭
阶段调用 engine.dispose() 主动清理(数据不受影响,纯停机噪音修复)。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-25 03:28:49 +08:00

73 lines
2.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from contextlib import asynccontextmanager
from dotenv import load_dotenv
load_dotenv()
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from app.core.config import settings
from app.core.database import init_db, Base, engine
from app.core.security import verify_token
from app.api.books import router as books_router
from app.api.chat import router as chat_router
from app.api.models import router as models_router
from app.api.mcp import router as mcp_router
from app.api.auth import router as auth_router
from app.models.book import Book, Chapter, Segment
from app.models.chat import Conversation, Message
from app.models.ai_model import AiModel
from app.models.mcp_server import McpServer
@asynccontextmanager
async def lifespan(app: FastAPI):
await init_db()
try:
yield
finally:
# 优雅停机:主动销毁连接池,避免 SQLite 后台线程的连接被 GC 丢弃
# (表现为 shutdown 时 SAWarning:non-checked-in connection ... dropped)
await engine.dispose()
app = FastAPI(title="BookMate", version="0.1.0", lifespan=lifespan)
# CORS 先加,站点访问密码中间件后加 → 密码中间件在最外层先跑
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.middleware("http")
async def site_auth_middleware(request: Request, call_next):
"""站点访问密码门:设置 SITE_PASSWORD 后,除登录/健康检查/CORS 预检外全部拦截。"""
if not settings.site_password:
return await call_next(request)
path = request.url.path
method = request.method
# CORS 预检、健康检查、登录相关端点放行
if method == "OPTIONS" or path == "/api/health" or path.startswith("/api/auth/"):
return await call_next(request)
token = request.headers.get("x-access-token", "")
if not token or not verify_token(token, settings.site_password):
return JSONResponse(status_code=401, content={"detail": "需要访问密码"})
return await call_next(request)
app.include_router(books_router, prefix="/api")
app.include_router(chat_router, prefix="/api")
app.include_router(models_router, prefix="/api")
app.include_router(mcp_router, prefix="/api")
app.include_router(auth_router, prefix="/api")
@app.get("/api/health")
async def health():
return {"status": "ok"}