"""FastAPI 应用入口(ARCH §7)。""" from __future__ import annotations from collections.abc import AsyncIterator from contextlib import asynccontextmanager from fastapi import FastAPI, Request from fastapi.responses import JSONResponse from ww_db import get_sessionmaker from ww_shared import AppError, ErrorBody, ErrorEnvelope from ww_api.logging_config import configure_logging, get_logger from ww_api.middleware import request_id_middleware from ww_api.routers import ( foreshadow, health, jobs, outline, projects, settings_providers, ) from ww_api.services.project_deps import seed_stub_user configure_logging() log = get_logger("ww.api") @asynccontextmanager async def _lifespan(app: FastAPI) -> AsyncIterator[None]: # 幂等 seed 单用户 stub——所有 owner_id FK 依赖它(见 memory/gotchas)。 async with get_sessionmaker()() as session: await seed_stub_user(session) yield def create_app() -> FastAPI: app = FastAPI(title="网文创作工作流 API", version="0.0.0", lifespan=_lifespan) app.middleware("http")(request_id_middleware) @app.exception_handler(AppError) async def _app_error_handler(request: Request, exc: AppError) -> JSONResponse: request_id = getattr(request.state, "request_id", None) log.warning("app_error", code=exc.code, message=exc.message) envelope = ErrorEnvelope( error=ErrorBody( code=exc.code, message=exc.message, details=exc.details, request_id=request_id, ) ) return JSONResponse(status_code=exc.http_status, content=envelope.model_dump()) app.include_router(health.router) app.include_router(jobs.router) app.include_router(projects.router) app.include_router(foreshadow.router) app.include_router(outline.router) app.include_router(settings_providers.router) return app app = create_app()