from __future__ import annotations import logging from contextlib import asynccontextmanager from fastapi import FastAPI, Request from fastapi import APIRouter from fastapi.responses import HTMLResponse, JSONResponse from fastcheck_api.app.api.v1.routers import auth, checks, reports, usage from fastcheck_api.app.core.logging import configure_logging from fastcheck_api.app.core.mongodb import lifespan_db from fastcheck_api.app.services.legacy_service import legacy_service from fastcheck_api.app.services.rabbitmq_service import rabbitmq_service from fastcheck_api.app.middleware.request_logging import RequestLoggingMiddleware from fastcheck_api.app.middleware.tenant_context import TenantContextMiddleware from fastcheck_api.app.core.config import settings logger = logging.getLogger("fastcheck") tags_metadata = [ {"name": "auth", "description": "Authentication and session management."}, {"name": "checks", "description": "Create checks (evaluations) and retrieve job/results."}, {"name": "reports", "description": "Fetch saved reports from existing persistence."}, {"name": "usage", "description": "Usage and credit balance information."}, ] @asynccontextmanager async def lifespan(app: FastAPI): configure_logging() async for _ in lifespan_db(): if settings.rabbitmq_enabled: try: await rabbitmq_service.connect() except Exception: logger.exception("RabbitMQ connection failed; continuing without async processing") try: yield finally: try: await rabbitmq_service.close() except Exception: logger.exception("RabbitMQ shutdown failed") await legacy_service.close() app = FastAPI( title="FastCheck API", version="0.1.0", description="FastCheck API for check evaluation and report retrieval.", openapi_url="/openapi.json", docs_url="/internal/docs", redoc_url="/internal/redoc", openapi_tags=tags_metadata, lifespan=lifespan, ) app.add_middleware(RequestLoggingMiddleware) app.add_middleware(TenantContextMiddleware) @app.get( "/", summary="API entrypoint", description="Convenience endpoint with links to the OpenAPI schema and internal Swagger/ReDoc.", ) async def root(): return { "openapi": "/openapi.json", "docs": "/docs", "swagger_ui": "/internal/docs", "redoc": "/internal/redoc", "health": "/health", } @app.get( "/docs", summary="Customer API docs", description="Customer-facing API reference with generated code examples. The OpenAPI schema remains the source of truth.", include_in_schema=False, ) async def customer_docs(): return HTMLResponse( """