"""FastAPI 入口。 通过 `uvicorn app.main:app --reload` 启动。 """ from __future__ import annotations import logging from collections.abc import AsyncIterator from contextlib import asynccontextmanager from pathlib import Path from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from app.api.v1.ad import router as ad_router from app.api.v1.auth import router as auth_router from app.api.v1.compare import router as compare_router from app.api.v1.coupon import router as coupon_router from app.api.v1.feedback import router as feedback_router from app.api.v1.meituan import router as meituan_router from app.api.v1.savings import router as savings_router from app.api.v1.signin import router as signin_router from app.api.v1.tasks import router as tasks_router from app.api.v1.user import router as user_router from app.api.v1.wallet import router as wallet_router from app.core.config import settings from app.core.logging import setup_logging setup_logging(debug=settings.APP_DEBUG) logger = logging.getLogger("shagua.main") @asynccontextmanager async def lifespan(_: FastAPI) -> AsyncIterator[None]: # 提示而非强制建表:生产用 alembic upgrade head,本地 dev 也建议先跑一次 migration。 # 这里只 log,不自动 create_all,避免生产环境意外建出过时 schema。 logger.info( "started env=%s debug=%s db=%s", settings.APP_ENV, settings.APP_DEBUG, settings.DATABASE_URL.split("://", 1)[0], ) yield logger.info("shutting down") app = FastAPI( title=settings.APP_NAME, version="0.1.0", docs_url="/docs" if not settings.is_prod else None, redoc_url="/redoc" if not settings.is_prod else None, lifespan=lifespan, ) if settings.cors_origins_list: app.add_middleware( CORSMiddleware, allow_origins=settings.cors_origins_list, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) @app.get("/health", tags=["meta"]) def health() -> dict[str, str]: return {"status": "ok"} app.include_router(auth_router) app.include_router(user_router) app.include_router(feedback_router) app.include_router(coupon_router) app.include_router(compare_router) app.include_router(meituan_router) app.include_router(wallet_router) app.include_router(signin_router) app.include_router(tasks_router) app.include_router(savings_router) app.include_router(ad_router) # 用户上传文件(头像)静态服务。生产可改由 nginx 直接 serve MEDIA_ROOT。 _media_root = Path(settings.MEDIA_ROOT) _media_root.mkdir(parents=True, exist_ok=True) app.mount( settings.MEDIA_URL_PREFIX, StaticFiles(directory=str(_media_root)), name="media", )