- uv(workspace) + pnpm monorepo;docker-compose(pg+api+web) - SQLAlchemy 16 MVP 表 + Alembic 初版迁移(无漂移,users stub) - FastAPI 骨架:统一错误信封(带 request_id) + structlog + /jobs/:id + OpenAPI - Next.js 骨架:纸感主题 token + OpenAPI→TS 客户端代码生成(gen:api) - CI(ruff/mypy/pytest + pg service + alembic 漂移校验) - 四份设计规格(PRODUCT/UX/ARCHITECTURE/DEV_PLAN) + CLAUDE.md
56 lines
1.8 KiB
Python
56 lines
1.8 KiB
Python
"""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 health, jobs, 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(settings_providers.router)
|
||
return app
|
||
|
||
|
||
app = create_app()
|