164 lines
4.7 KiB
Python
164 lines
4.7 KiB
Python
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.customers import router as customers_router
|
|
from app.api.dashboard import router as dashboard_router
|
|
from app.api.knowledge import router as knowledge_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.1.0",
|
|
description="Backend von Olympus",
|
|
)
|
|
|
|
app.include_router(auth_router)
|
|
app.include_router(audit_router)
|
|
app.include_router(users_router)
|
|
app.include_router(roles_router)
|
|
app.include_router(permissions_router)
|
|
app.include_router(customers_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)
|
|
|
|
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(),
|
|
}
|