| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879 |
- import logging
- from contextlib import asynccontextmanager
- from fastapi import FastAPI, Request
- from fastapi.responses import FileResponse
- from fastapi.staticfiles import StaticFiles
- from app.api import api_router
- from app.api.camera import capture_pair_via_mqtt
- from app.services import CameraUnavailableError, get_camera_service
- from app.core.config import settings
- logging.basicConfig(level=logging.INFO)
- logger = logging.getLogger(__name__)
- settings.static.ensure_directories()
- @asynccontextmanager
- async def lifespan(_: FastAPI):
- """
- FastAPI 生命周期管理。
- 启动阶段会尝试预热相机,这样第一次请求拍照时更快。
- 但这里不会因为相机异常而阻止整个服务启动,
- 因为有时你可能还需要先看接口文档、做健康检查或排查环境。
- """
- camera_service = get_camera_service()
- try:
- camera_service.initialize()
- logger.info("树莓派相机预热完成。")
- except CameraUnavailableError as exc:
- logger.warning("相机预热失败,拍照接口暂时不可用:%s", exc)
- try:
- yield
- finally:
- camera_service.close()
- logger.info("相机资源已关闭。")
- app = FastAPI(
- title=settings.app.title,
- version=settings.app.version,
- description=settings.app.description,
- lifespan=lifespan,
- )
- app.include_router(api_router, prefix="/api")
- app.mount("/static", StaticFiles(directory=str(settings.static.root_dir)), name="static")
- @app.get("/", include_in_schema=False)
- def read_root() -> FileResponse:
- return FileResponse(settings.static.index_path)
- @app.api_route("/capture-pair", methods=["GET", "POST"], include_in_schema=False)
- def capture_pair_alias(request: Request):
- return capture_pair_via_mqtt(request)
- @app.get("/health", summary="健康检查")
- def health_check() -> dict[str, object]:
- """
- 健康检查接口。
- 这个接口不强依赖相机真实可用,只返回服务当前状态和相机是否已初始化。
- 在部署脚本、容器探针或反向代理检查中都比较实用。
- """
- camera_service = get_camera_service()
- return {
- "status": "ok",
- "camera_initialized": camera_service.is_initialized,
- "app_name": settings.app.title,
- "app_version": settings.app.version,
- }
|