import contextlib import logging from collections.abc import AsyncIterator import anydi.ext.fastapi from anydi.ext.starlette.middleware import RequestScopedMiddleware from fansifter_common.api import openapi from fansifter_common.api.middleware.query_string import QueryStringFlatteningMiddleware from fansifter_common.context.asgi.middleware import ( CorrelationIdMiddleware, RequestContextMiddleware, ) from fastapi import FastAPI from fastapi.datastructures import Default from fastapi.responses import ORJSONResponse from jwtauth import JWTAuth from jwtauth.asgi.middleware import JWTAuthenticationMiddleware from owslogger.asgi import RequestLoggingMiddleware from starlette.middleware import Middleware from url_shortener.api import error_handlers from url_shortener.api.routers.infra import router as infra_router from url_shortener.api.routers.main import router as main_router from url_shortener.config import settings from url_shortener.container import container logger = logging.getLogger(__name__) def get_app() -> FastAPI: """Get FastAPI application instance.""" # Get JWTAuth instance jwt_auth = container.resolve(JWTAuth) @contextlib.asynccontextmanager async def lifespan(_app: FastAPI) -> AsyncIterator[None]: """Start and close DI container on application startup and shutdown.""" async with container: yield # Create FastAPI application app = FastAPI( debug=settings.debug, title=settings.service_name, version=settings.service_version, openapi_url=settings.openapi_url, description="URL Shortener", servers=settings.openapi_servers, default_response_class=Default(ORJSONResponse), middleware=[ Middleware(RequestScopedMiddleware, container=container), Middleware(CorrelationIdMiddleware), Middleware(RequestContextMiddleware), Middleware( RequestLoggingMiddleware, exclude_paths=["/hello/", "/openapi.json", "/docs", "/redoc"], logger=logger, ), Middleware(QueryStringFlatteningMiddleware), Middleware( JWTAuthenticationMiddleware, environment=settings.environment, enabled=settings.jwt_auth_enabled, exclude_paths=settings.jwt_auth_exclude_paths, auth=jwt_auth, ), ], lifespan=lifespan, ) # Register exception error handlers error_handlers.register(app) # Include API routers app.include_router(infra_router) app.include_router(main_router) # Extend OpenAPI schema openapi.extend( app, jwt_auth_enabled=settings.jwt_auth_enabled, jwt_auth_exclude_paths=settings.jwt_auth_exclude_paths, ) # Install Container extension anydi.ext.fastapi.install(app, container=container) return app