diff --git a/api/main.py b/api/main.py index b6b3fdc..738f8cf 100644 --- a/api/main.py +++ b/api/main.py @@ -1,12 +1,17 @@ from contextlib import asynccontextmanager import os +import uuid + +from fastapi import FastAPI, Request +from fastapi.middleware.cors import CORSMiddleware +from fastapi.responses import JSONResponse +from fastapi.exceptions import RequestValidationError +from starlette.exceptions import HTTPException as StarletteHTTPException +from starlette.middleware.base import BaseHTTPMiddleware -from fastapi import FastAPI from api.routes import templates, forms from api.db.init_db import init_db from api.errors.handlers import register_exception_handlers -from fastapi.middleware.cors import CORSMiddleware -from api.routes import forms, templates @asynccontextmanager async def lifespan(app: FastAPI): @@ -20,6 +25,58 @@ async def lifespan(app: FastAPI): register_exception_handlers(app) +class RequestIDMiddleware(BaseHTTPMiddleware): + async def dispatch(self, request: Request, call_next): + request_id = str(uuid.uuid4()) + request.state.request_id = request_id + response = await call_next(request) + response.headers["X-Request-ID"] = request_id + return response + + +app.add_middleware(RequestIDMiddleware) + +@app.exception_handler(StarletteHTTPException) +async def http_exception_handler(request: Request, exc: StarletteHTTPException): + return JSONResponse( + status_code=exc.status_code, + content={ + "error": { + "type": "HTTPException", + "message": exc.detail, + "details": {} + } + }, + ) + + +@app.exception_handler(RequestValidationError) +async def validation_exception_handler(request: Request, exc: RequestValidationError): + return JSONResponse( + status_code=422, + content={ + "error": { + "type": "ValidationError", + "message": "Invalid request data", + "details": exc.errors(), + } + }, + ) + + +@app.exception_handler(Exception) +async def general_exception_handler(request: Request, exc: Exception): + return JSONResponse( + status_code=500, + content={ + "error": { + "type": "InternalServerError", + "message": str(exc), + "details": {} + } + }, + ) + default_origins = "http://127.0.0.1:5173" allowed_origins = [ origin.strip() @@ -36,4 +93,4 @@ async def lifespan(app: FastAPI): ) app.include_router(templates.router) -app.include_router(forms.router) +app.include_router(forms.router) \ No newline at end of file diff --git a/api/schemas/forms.py b/api/schemas/forms.py index 3cce650..bf6957e 100644 --- a/api/schemas/forms.py +++ b/api/schemas/forms.py @@ -1,9 +1,15 @@ -from pydantic import BaseModel +from pydantic import BaseModel, field_validator class FormFill(BaseModel): template_id: int input_text: str + @field_validator("input_text") + def validate_input_text(cls, value): + if not value or not value.strip(): + raise ValueError("Input text cannot be empty") + return value + class FormFillResponse(BaseModel): id: int