229 lines
6.2 KiB
Python
229 lines
6.2 KiB
Python
"""
|
|
FastAPI主应用
|
|
将原来的main.py重命名为app.py
|
|
"""
|
|
|
|
import time
|
|
from contextlib import asynccontextmanager
|
|
from fastapi import FastAPI, Request
|
|
from fastapi.exceptions import RequestValidationError
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from fastapi.middleware.trustedhost import TrustedHostMiddleware
|
|
from fastapi.responses import JSONResponse
|
|
from fastapi.openapi.docs import (
|
|
get_swagger_ui_html,
|
|
get_swagger_ui_oauth2_redirect_html,
|
|
get_redoc_html,
|
|
)
|
|
from fastapi.staticfiles import StaticFiles
|
|
|
|
from api.routes import face_features
|
|
from api.routes.algorithm_router import router as algorithm_router, sync_videofacebiz_params, \
|
|
sync_videofacebiz_blacklist, sync_videofaceprisonbiz_params, sync_videofaceprisonbiz_blacklist
|
|
from api.errors import (
|
|
APIError,
|
|
validation_exception_handler,
|
|
api_error_handler,
|
|
generic_exception_handler
|
|
)
|
|
from config import settings
|
|
from database.connection import init_database
|
|
from database.connection import db_manager
|
|
from rtsp.service import rtsp_server
|
|
|
|
|
|
# 生命周期管理
|
|
@asynccontextmanager
|
|
async def lifespan(app: FastAPI):
|
|
"""
|
|
应用生命周期管理
|
|
|
|
- 启动时:初始化数据库
|
|
- 关闭时:清理资源
|
|
"""
|
|
# 启动时
|
|
print("🚀 start algorithm service...")
|
|
print(f"📊 db: {settings.DATABASE_NAME}")
|
|
print(f"🔧 debug mode: {settings.DEBUG}")
|
|
|
|
# 初始化数据库
|
|
init_database()
|
|
|
|
# 数据库健康检查
|
|
if db_manager.health_check():
|
|
print("✅ 数据库连接正常")
|
|
else:
|
|
print("❌ 数据库连接失败")
|
|
|
|
# 启动 RTSP 服务(如果启用)
|
|
if settings.RTSP_ENABLED:
|
|
print("📹 启动 RTSP 服务...")
|
|
rtsp_server.start()
|
|
# 将 RTSP 服务实例保存到应用状态
|
|
app.state.rtsp_server = rtsp_server
|
|
|
|
# 自动同步VideoFaceBiz参数和黑名单
|
|
print("🔄 自动同步VideoFaceBiz参数和黑名单...")
|
|
try:
|
|
params_updated = sync_videofacebiz_params()
|
|
blacklist_loaded = sync_videofacebiz_blacklist()
|
|
print(f"✅ 自动同步完成 - 参数更新: {params_updated}个, 黑名单加载: {blacklist_loaded}个")
|
|
except Exception as e:
|
|
print(f"⚠️ 自动同步失败: {e}")
|
|
else:
|
|
print("⚠️ RTSP 服务未启用")
|
|
|
|
# 自动同步VideoFacePrisonBiz参数和黑名单
|
|
print("🔄 自动同步VideoFacePrisonBiz参数和黑名单...")
|
|
try:
|
|
params_updated = sync_videofaceprisonbiz_params()
|
|
blacklist_loaded = sync_videofaceprisonbiz_blacklist()
|
|
print(f"✅ 自动同步完成 - 参数更新: {params_updated}个, 黑名单加载: {blacklist_loaded}个")
|
|
except Exception as e:
|
|
print(f"⚠️ 自动同步失败: {e}")
|
|
|
|
yield
|
|
|
|
# 关闭时
|
|
print("🛑 algorithm service stopped...")
|
|
|
|
# 停止 RTSP 服务
|
|
if settings.RTSP_ENABLED:
|
|
print("🛑 停止 RTSP 服务...")
|
|
rtsp_server.stop()
|
|
|
|
db_manager.close()
|
|
|
|
|
|
# 创建FastAPI应用
|
|
app = FastAPI(
|
|
title=settings.PROJECT_NAME,
|
|
version=settings.PROJECT_VERSION,
|
|
description=settings.PROJECT_DESCRIPTION,
|
|
openapi_url=f"{settings.API_V1_PREFIX}/openapi.json",
|
|
docs_url=None, # 自定义docs
|
|
redoc_url=None, # 自定义redoc
|
|
lifespan=lifespan
|
|
)
|
|
|
|
|
|
# 自定义API文档页面
|
|
@app.get("/docs", include_in_schema=False)
|
|
async def custom_swagger_ui_html():
|
|
return get_swagger_ui_html(
|
|
openapi_url=app.openapi_url,
|
|
title=f"{app.title} - Swagger UI",
|
|
oauth2_redirect_url=app.swagger_ui_oauth2_redirect_url,
|
|
swagger_js_url="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js",
|
|
swagger_css_url="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css",
|
|
)
|
|
|
|
|
|
@app.get(app.swagger_ui_oauth2_redirect_url, include_in_schema=False)
|
|
async def swagger_ui_redirect():
|
|
return get_swagger_ui_oauth2_redirect_html()
|
|
|
|
|
|
@app.get("/redoc", include_in_schema=False)
|
|
async def redoc_html():
|
|
return get_redoc_html(
|
|
openapi_url=app.openapi_url,
|
|
title=f"{app.title} - ReDoc",
|
|
redoc_js_url="https://unpkg.com/redoc@next/bundles/redoc.standalone.js",
|
|
)
|
|
|
|
|
|
# 中间件配置
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=settings.BACKEND_CORS_ORIGINS,
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
app.add_middleware(
|
|
TrustedHostMiddleware,
|
|
allowed_hosts=["*"] if settings.DEBUG else ["localhost", "127.0.0.1", "0.0.0.0"]
|
|
)
|
|
|
|
|
|
# 请求计时中间件
|
|
@app.middleware("http")
|
|
async def add_process_time_header(request: Request, call_next):
|
|
"""
|
|
添加请求处理时间头
|
|
"""
|
|
start_time = time.time()
|
|
response = await call_next(request)
|
|
process_time = time.time() - start_time
|
|
response.headers["X-Process-Time"] = str(process_time)
|
|
return response
|
|
|
|
|
|
# 异常处理器
|
|
app.add_exception_handler(RequestValidationError, validation_exception_handler)
|
|
app.add_exception_handler(APIError, api_error_handler)
|
|
app.add_exception_handler(Exception, generic_exception_handler)
|
|
|
|
|
|
# 根路由
|
|
@app.get("/")
|
|
async def root():
|
|
"""
|
|
根路径
|
|
"""
|
|
return {
|
|
"message": "algorithm service",
|
|
"version": settings.PROJECT_VERSION,
|
|
"docs": "/docs",
|
|
"api_prefix": settings.API_V1_PREFIX
|
|
}
|
|
|
|
|
|
@app.get("/health")
|
|
async def health_check():
|
|
"""
|
|
健康检查端点
|
|
"""
|
|
# 检查数据库连接
|
|
db_healthy = db_manager.health_check()
|
|
|
|
return {
|
|
"status": "healthy" if db_healthy else "unhealthy",
|
|
"database": "connected" if db_healthy else "disconnected",
|
|
"timestamp": time.time()
|
|
}
|
|
|
|
|
|
# 注册API路由
|
|
app.include_router(
|
|
face_features.router,
|
|
prefix=settings.API_V1_PREFIX
|
|
)
|
|
|
|
app.include_router(
|
|
algorithm_router,
|
|
prefix=settings.API_V1_PREFIX
|
|
)
|
|
|
|
|
|
# 自定义404处理器
|
|
@app.exception_handler(404)
|
|
async def not_found_handler(request: Request, exc):
|
|
"""
|
|
自定义404错误处理器
|
|
"""
|
|
return JSONResponse(
|
|
status_code=404,
|
|
content={
|
|
"error": {
|
|
"code": "NOT_FOUND",
|
|
"message": f"请求的资源不存在: {request.url.path}"
|
|
}
|
|
}
|
|
)
|
|
|
|
|
|
# 导出应用实例
|
|
__all__ = ["app"] |