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 jwtauth import JWTAuth from jwtauth.asgi.middleware import JWTAuthenticationMiddleware from owslogger.asgi import RequestLoggingMiddleware from starlette.middleware import Middleware from email_campaigns.api import error_handlers from email_campaigns.api.auth import bearer_auth from email_campaigns.api.routers.automated_emails import ( router as automated_emails_router, ) from email_campaigns.api.routers.email_campaigns import router as email_campaigns_router from email_campaigns.api.routers.email_domains import router as email_domains_router from email_campaigns.api.routers.email_reporting import ( router as email_reporting_router, ) from email_campaigns.api.routers.fonts import router as fonts_router from email_campaigns.api.routers.infra import router as infra_router from email_campaigns.api.routers.source_campaigns import ( router as source_campaigns_router, ) from email_campaigns.config import settings from email_campaigns.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="Email Campaigns", servers=settings.openapi_servers, 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(email_domains_router) app.include_router(email_campaigns_router) app.include_router(automated_emails_router) app.include_router(source_campaigns_router) app.include_router(fonts_router) app.include_router(email_reporting_router) # Extend OpenAPI schema openapi.extend( app, jwt_auth_enabled=settings.jwt_auth_enabled, jwt_auth_exclude_paths=settings.jwt_auth_exclude_paths, bearer_auth_scheme_name=bearer_auth.scheme_name, ) # Install Container extension anydi.ext.fastapi.install(app, container=container) return app