"""FastAPI 入口(B7 集成):路由挂载、trace 中间件、lifespan、统一错误体。 lifespan(B7 挂账⑤⑥): - 启动期校验:非 development 环境且鉴权工厂仍为 debug 头实现 → 拒绝启动 (T-01 接入 JWT 后置 deps.AUTH_FACTORY_IS_DEBUG=False 放行); - Redis 网关单例注册(惰性连接,publish/DEL 失败降级不阻塞业务); - shutdown 统一 dispose 数据库引擎(utils/db 工厂,B6 复审 P3 泄漏收口)。 trace:X-Trace-Id 透传/生成 + 响应头回写(trace.py 约束:call_next 前 set, 同步路由线程池由 anyio 传播,一致性 B8 断言兜底)。 """ from __future__ import annotations import re from contextlib import asynccontextmanager from fastapi import FastAPI, Request from app.api import deps from app.api.risk import router as risk_router from app.api.simulate import router as simulate_router from app.config.settings import settings from app.service.risk import redis_gateway from app.utils.db import dispose_engines from app.utils.response import register_error_handlers from app.utils.trace import new_trace, reset_trace, set_trace # 透传外部 X-Trace-Id 的格式白名单(防响应头注入;不合规一律新生成) _TRACE_ID_PATTERN = re.compile(r"^[A-Za-z0-9._-]{1,64}$") @asynccontextmanager async def lifespan(_: FastAPI): if settings.app_env != "development" and deps.AUTH_FACTORY_IS_DEBUG: raise RuntimeError( "debug auth factory is wired but app_env is not 'development'; " "deploy T-01 JWT auth first or set app_env=development" ) redis_gateway.set_gateway(redis_gateway.RedisGateway()) try: yield finally: redis_gateway.set_gateway(None) dispose_engines() app = FastAPI(title="JinRong Agent Platform", version="0.2.0", lifespan=lifespan) register_error_handlers(app) app.include_router(risk_router) app.include_router(simulate_router) @app.middleware("http") async def trace_middleware(request: Request, call_next): """trace_id 贯通:透传合法 X-Trace-Id,否则生成;响应头回写。""" incoming = request.headers.get("X-Trace-Id", "") trace_id = incoming if _TRACE_ID_PATTERN.fullmatch(incoming) else new_trace() token = set_trace(trace_id) try: response = await call_next(request) finally: reset_trace(token) response.headers["X-Trace-Id"] = trace_id return response @app.get("/health") def health(): return {"status": "ok", "env": settings.app_env}