Files
blog.pyaqa.ru/app/main.py
Sergey Vanyushkin b1878e470f feat(ui): add error handling, flash messages and SEO optimization
- Add custom error pages (404, 403, 500) with user-friendly messages
- Add flash message system with signed cookies for security
- Add toast notifications with auto-dismiss and manual close
- Add comprehensive SEO meta tags (description, keywords, OG, Twitter)
- Add canonical URLs for SEO
- Update routes to use slug-based URLs (/posts/{slug} instead of /posts/{id})
- Add Open Graph and Twitter Card meta tags for social sharing
- Add favicon SVG
- Update all templates with proper meta tags and URLs
- Add error handlers registration in main.py
- Add flash middleware for request handling
- Install itsdangerous dependency
2026-05-02 16:23:57 +03:00

140 lines
3.7 KiB
Python

"""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
from contextlib import asynccontextmanager
import uvicorn
from dishka import make_async_container
from dishka.integrations.fastapi import setup_dishka
from fastapi import FastAPI
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
@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, call_next):
"""Middleware to setup flash manager for each request."""
await setup_flash_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='<meta http-equiv="refresh" content="0;url=/web/">', 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()