"""Application entry point with DDD architecture. This module is the main entry point for the FastAPI application. Configures DI container, middleware, and routes following DDD principles. """ from collections.abc import AsyncGenerator, Awaitable, Callable from contextlib import asynccontextmanager import uvicorn from dishka import make_async_container from dishka.integrations.fastapi import setup_dishka from fastapi import FastAPI, Request, Response from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import HTMLResponse from fastapi.staticfiles import StaticFiles from app.infrastructure import close_db, init_db, register_exception_handlers, settings from app.infrastructure.di.providers import ( DatabaseProvider, KeycloakProvider, RepositoryProvider, TransactionManagerProvider, UseCaseProvider, ) from app.presentation import router from app.presentation.web import auth_router from app.presentation.web import router as web_router from app.presentation.web.error_handlers import register_error_handlers from app.presentation.web.flash import setup_flash_manager from app.presentation.web.locale import setup_locale_manager @asynccontextmanager async def lifespan(app: FastAPI) -> AsyncGenerator[None]: """Application lifespan manager. Handles startup and shutdown tasks for the application. Args: app: FastAPI application instance. Yields: None during application runtime. """ await init_db() yield await close_db() def app_factory() -> FastAPI: """Create and configure FastAPI application. Sets up DI container, exception handlers, middleware, and routes. Returns: Configured FastAPI application instance. """ app = FastAPI( title=settings.app.name, debug=settings.app.debug, lifespan=lifespan, docs_url="/docs" if settings.is_dev else None, redoc_url="/redoc" if settings.is_dev else None, ) container = make_async_container( DatabaseProvider(), RepositoryProvider(), TransactionManagerProvider(), UseCaseProvider(), KeycloakProvider(), ) setup_dishka(container, app) register_exception_handlers(app) register_error_handlers(app) @app.middleware("http") async def flash_middleware( request: Request, call_next: Callable[[Request], Awaitable[Response]] ) -> Response: """Middleware to setup flash manager for each request.""" await setup_flash_manager(request) response = await call_next(request) if hasattr(request.state, "flash_manager"): request.state.flash_manager.set_cookie(response) return response @app.middleware("http") async def locale_middleware( request: Request, call_next: Callable[[Request], Awaitable[Response]] ) -> Response: """Middleware to detect and set locale for each request.""" await setup_locale_manager(request) response = await call_next(request) return response app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) app.include_router(router, prefix="/api") app.include_router(web_router) app.include_router(auth_router) app.mount("/static", StaticFiles(directory="static"), name="static") @app.get("/", response_class=HTMLResponse) async def root_redirect() -> HTMLResponse: """Redirect root URL to web UI. Returns: HTMLResponse with redirect to web interface. """ return HTMLResponse( content='', status_code=200 ) @app.get("/health", tags=["health"]) async def health_check() -> dict[str, str]: """Health check endpoint. Returns: Status information dictionary. """ return { "status": "ok", "app": settings.app.name, "env": settings.environment.value, } return app def main() -> None: """Run the application. Starts uvicorn server with application factory. """ uvicorn.run( app_factory, factory=True, host=settings.app.host, port=settings.app.port, ) if __name__ == "__main__": main()