import time from contextlib import asynccontextmanager from fastapi import FastAPI, Request from fastapi.middleware.gzip import GZipMiddleware from fastapi.middleware.cors import CORSMiddleware from common.database.db_session_manager import DB_SESSION_MNG from common.logger import LOG from common.utils.gtime import GTime from router.middleware.tenant_middleware import TenantMiddleware import router.v1.health.health import router.v1.negotiation.negotiation import router.v1.chat.chat import router.v1.learning.learning import router.v1.card.card API_SERVER_START_TIME = GTime.UTCStr() @asynccontextmanager async def lifespan(app: FastAPI): # startup: 공유 베이스(_base) 없으면 자동 시드 (운영 자동화) from bootstrap.lifespan import ensure_base_seeded await ensure_base_seeded() yield # shutdown: DB 엔진 커넥션 풀 정리 await DB_SESSION_MNG.dispose_all() app = FastAPI(title="Negosium Agent Server", lifespan=lifespan) # Accept-Encoding: gzip 요청에 대해 1000 bytes 이상 응답을 압축. app.add_middleware(GZipMiddleware, minimum_size=1000) # 테넌트 라우팅 미들웨어 (헤더 X-Tenant-ID → request.state.tenant_id). app.add_middleware(TenantMiddleware) # 데모 HTML(file:// 또는 타 출처)에서 호출 가능하도록 CORS 허용 (개발용 — 운영은 출처 제한 권장). app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"], ) @app.middleware("http") async def log_time(request: Request, call_next): start_time = time.time() response = await call_next(request) elapsed = time.time() - start_time LOG.d(f"took: {elapsed:.4f} - {request.url.path}") return response @app.get(path="/healthz", responses={404: {"description": "Not found"}}) async def healthz(): return API_SERVER_START_TIME # 각 도메인 라우터 등록. 새 기능 추가 시 router.v1.. import 후 include. # (chat / card / learning / qtable 라우터는 P7 에서 Chat_server 14개 API 이식하며 추가) app.include_router(router.v1.health.health.router) app.include_router(router.v1.negotiation.negotiation.router) app.include_router(router.v1.chat.chat.router) app.include_router(router.v1.learning.learning.router) app.include_router(router.v1.card.card.router)