from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from app.api import ( auth, memories, profiles, push_configs, reading_events, stories, universes, ) from app.core.config import settings from app.core.logging import get_logger, setup_logging from app.db.database import init_db setup_logging() logger = get_logger(__name__) @asynccontextmanager async def lifespan(app: FastAPI): """App lifespan manager.""" logger.info("app_starting", app_name=settings.app_name) await init_db() logger.info("database_initialized") # 加载 provider 缓存 await _load_provider_cache() yield logger.info("app_shutdown") async def _load_provider_cache(): """启动时加载 provider 缓存。""" from app.db.database import _get_session_factory from app.services.provider_cache import reload_providers try: session_factory = _get_session_factory() async with session_factory() as session: cache = await reload_providers(session) provider_count = sum(len(v) for v in cache.values()) logger.info("provider_cache_loaded", provider_count=provider_count) except Exception as e: logger.warning("provider_cache_load_failed", error=str(e)) # 不阻止启动,使用 settings 中的默认配置 app = FastAPI( title=settings.app_name, description="AI-driven story generator for kids.", version="0.1.0", lifespan=lifespan, ) app.add_middleware( CORSMiddleware, allow_origins=settings.cors_origins, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) app.include_router(auth.router, prefix="/auth", tags=["auth"]) app.include_router(stories.router, prefix="/api", tags=["stories"]) app.include_router(profiles.router, prefix="/api", tags=["profiles"]) app.include_router(universes.router, prefix="/api", tags=["universes"]) app.include_router(push_configs.router, prefix="/api", tags=["push-configs"]) app.include_router(reading_events.router, prefix="/api", tags=["reading-events"]) app.include_router(memories.router, prefix="/api", tags=["memories"]) @app.get("/health") async def health_check(): """Simple liveness check.""" return {"status": "ok"}