import logging import time from fastapi import FastAPI from fastapi import HTTPException from fastapi.exceptions import RequestValidationError from fastapi.encoders import jsonable_encoder from fastapi.responses import JSONResponse from starlette import status from starlette.requests import Request from app.api.auth import router as auth_router from app.api.audit import router as audit_router from app.api.backups import router as backups_router from app.api.customers import router as customers_router from app.api.dashboard import router as dashboard_router from app.api.inventory import router as inventory_router from app.api.knowledge import router as knowledge_router from app.api.lexware import router as lexware_router from app.api.permissions import router as permissions_router from app.api.repairs import router as repairs_router from app.api.repair_estimates import router as repair_estimates_router from app.api.roles import router as roles_router from app.api.system_settings import router as system_settings_router from app.api.users import router as users_router from app.db.database import SessionLocal from app.db.health import check_database from app.core.logging import configure_logging from app.knowledge_seed import seed_knowledge_manufacturers from app.rbac.seed import seed_rbac from app.services.initial_admin_bootstrap import bootstrap_initial_admin configure_logging() app = FastAPI( title="Hermes API", version="0.9.1", description="Backend von Olympus", ) app.include_router(auth_router) app.include_router(audit_router) app.include_router(backups_router) app.include_router(users_router) app.include_router(roles_router) app.include_router(permissions_router) app.include_router(customers_router) app.include_router(inventory_router) app.include_router(knowledge_router) app.include_router(repairs_router) app.include_router(repair_estimates_router) app.include_router(dashboard_router) app.include_router(system_settings_router) app.include_router(lexware_router) logger = logging.getLogger(__name__) @app.middleware("http") async def request_logging_middleware(request: Request, call_next): start = time.perf_counter() response = await call_next(request) duration_ms = round((time.perf_counter() - start) * 1000, 2) logger.info( "request.completed", extra={ "method": request.method, "path": request.url.path, "status_code": response.status_code, "duration_ms": duration_ms, }, ) return response def error_code_for_status(status_code: int) -> str: return { 400: "BAD_REQUEST", 401: "UNAUTHORIZED", 403: "FORBIDDEN", 404: "NOT_FOUND", 409: "CONFLICT", 422: "VALIDATION_ERROR", }.get(status_code, "HTTP_ERROR") @app.on_event("startup") def startup_seed_rbac(): db = SessionLocal() try: seed_rbac(db) seed_knowledge_manufacturers(db) bootstrap_initial_admin(db) finally: db.close() @app.exception_handler(HTTPException) async def http_exception_handler(request: Request, exc: HTTPException): logger.warning( "http_exception", extra={ "path": request.url.path, "method": request.method, "status_code": exc.status_code, }, ) return JSONResponse( status_code=exc.status_code, content={ "success": False, "message": str(exc.detail), "error_code": error_code_for_status(exc.status_code), "details": [], "detail": exc.detail, }, headers=exc.headers, ) @app.exception_handler(RequestValidationError) async def validation_exception_handler(request: Request, exc: RequestValidationError): logger.warning( "validation_exception", extra={"path": request.url.path, "method": request.method}, ) return JSONResponse( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, content={ "success": False, "message": "Validierung fehlgeschlagen", "error_code": "VALIDATION_ERROR", "details": jsonable_encoder(exc.errors()), "detail": jsonable_encoder(exc.errors()), }, ) @app.exception_handler(Exception) async def unhandled_exception_handler(request: Request, exc: Exception): logger.exception( "unhandled_exception", extra={"path": request.url.path, "method": request.method}, ) return JSONResponse( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content={ "success": False, "message": "Interner Serverfehler", "error_code": "INTERNAL_SERVER_ERROR", "details": [], "detail": "Interner Serverfehler", }, ) @app.get("/") def root(): return { "service": "Hermes", "status": "running", } @app.get("/health") def health(): return { "status": "healthy", "database": check_database(), }