""" main.py — FastAPI 应用入口 统一响应包络 / 全局异常处理 / 健康检查 / 路由注册 """ import logging import os from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from fastapi.staticfiles import StaticFiles from app.core.config import get_settings from app.core.response import CloverHTTPException, ErrorCode logger = logging.getLogger(__name__) settings = get_settings() def create_app() -> FastAPI: app = FastAPI( title="Clover API", version="0.1.0", docs_url="/docs" if settings.APP_ENV != "production" else None, redoc_url=None, ) # ── CORS ────────────────────────────────────────── app.add_middleware( CORSMiddleware, allow_origins=["*"], # 生产环境收窄到前端域名 allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # ── 全局异常处理(统一包络)────────────────────── @app.exception_handler(CloverHTTPException) async def clover_http_exception_handler(request: Request, exc: CloverHTTPException): return JSONResponse( status_code=exc.status_code, content={"code": exc.biz_code, "message": exc.biz_message}, ) @app.exception_handler(Exception) async def generic_exception_handler(request: Request, exc: Exception): logger.error("Unhandled exception: %s %s → %s", request.method, request.url, exc) return JSONResponse( status_code=500, content={"code": ErrorCode.SERVER_ERROR, "message": "服务器内部错误"}, ) # ── 健康检查(Docker 健康检查用)────────────────── @app.get("/health") async def health(): return {"status": "ok"} # ── 路由注册(每个模块路由独立文件,这里汇总注册)─ _register_routers(app) # ── 静态文件(G4坑修复:图片 /uploads 路由)────── uploads_dir = os.path.join(os.path.dirname(__file__), "uploads") os.makedirs(uploads_dir, exist_ok=True) app.mount("/uploads", StaticFiles(directory=uploads_dir), name="uploads") return app def _register_routers(app: FastAPI) -> None: from app.api.v1.auth import router as auth_router from app.api.v1.api_keys import router as api_keys_router from app.api.v1.products import router as products_router from app.api.v1.product_images import router as product_images_router from app.api.v1.benchmarks import router as benchmarks_router from app.api.v1.tasks import router as tasks_router from app.api.v1.task_actions import router as task_actions_router from app.api.v1.review import router as review_router from app.api.v1.delivery import router as delivery_router from app.api.v1.stream import router as stream_router from app.api.v1.workspaces import router as workspaces_router from app.api.v1.fission import router as fission_router from app.api.v1.exports import router as exports_router prefix = "/api/v1" app.include_router(auth_router, prefix=prefix) app.include_router(api_keys_router, prefix=prefix) # 导出 router 必须在 products_router 之前:否则 /products/export 被 /products/{product_id} 吞掉 app.include_router(exports_router, prefix=prefix) app.include_router(products_router, prefix=prefix) app.include_router(product_images_router, prefix=prefix) app.include_router(benchmarks_router, prefix=prefix) app.include_router(tasks_router, prefix=prefix) app.include_router(task_actions_router, prefix=prefix) app.include_router(review_router, prefix=prefix) app.include_router(delivery_router, prefix=prefix) app.include_router(stream_router, prefix=prefix) app.include_router(workspaces_router, prefix=prefix) app.include_router(fission_router, prefix=prefix) app = create_app()