from pathlib import Path from fastapi import FastAPI, HTTPException, Request from fastapi.exceptions import RequestValidationError from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import HTMLResponse, JSONResponse from fastapi.staticfiles import StaticFiles from app.config import settings from app.routers import api_router from app.routers.activities import activity_share_landing def create_app() -> FastAPI: app = FastAPI(title=settings.app_name, debug=settings.app_debug) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) @app.get("/health", tags=["health"]) async def health_check() -> dict[str, str]: return {"status": "ok"} @app.exception_handler(HTTPException) async def http_exception_handler(request: Request, exc: HTTPException) -> JSONResponse: _ = request code_map = { 400: 1005, 401: 1001, 403: 1002, 404: 1003, } return JSONResponse( status_code=exc.status_code, content={ "code": code_map.get(exc.status_code, 1005), "message": exc.detail, "data": None, }, ) @app.exception_handler(RequestValidationError) async def validation_exception_handler( request: Request, exc: RequestValidationError ) -> JSONResponse: _ = request field_errors = [] for error in exc.errors(): location = error.get("loc", []) field_name = location[-1] if location else "unknown" field_errors.append( { "field": field_name, "message": error.get("msg", "参数校验失败"), "type": error.get("type"), } ) return JSONResponse( status_code=422, content={ "code": 1004, "message": "参数校验失败", "data": {"field_errors": field_errors}, }, ) @app.exception_handler(Exception) async def generic_exception_handler(request: Request, exc: Exception) -> JSONResponse: _ = request return JSONResponse( status_code=500, content={ "code": 1005, "message": str(exc) if settings.app_debug else "服务器内部错误", "data": None, }, ) static_dir = Path(__file__).resolve().parent / "static" static_dir.mkdir(parents=True, exist_ok=True) app.mount("/static", StaticFiles(directory=static_dir), name="static") app.mount(f"{settings.api_v1_prefix}/static", StaticFiles(directory=static_dir), name="api-static") app.add_api_route( "/share/activity/{share_token}", activity_share_landing, methods=["GET"], response_class=HTMLResponse, include_in_schema=False, ) app.include_router(api_router, prefix=settings.api_v1_prefix) return app app = create_app()