"""FastAPI entrypoint for the DMS backend service.""" from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from app.api.router import api_router from app.core.config import get_settings from app.db.base import init_db from app.services.app_settings import ensure_app_settings from app.services.handwriting_style import ensure_handwriting_style_collection from app.services.storage import ensure_storage from app.services.typesense_index import ensure_typesense_collection settings = get_settings() def create_app() -> FastAPI: """Builds and configures the FastAPI application instance.""" app = FastAPI(title="DCM DMS API", version="0.1.0") app.add_middleware( CORSMiddleware, allow_origins=settings.cors_origins, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) app.include_router(api_router, prefix="/api/v1") @app.middleware("http") async def enforce_upload_request_size(request: Request, call_next): """Rejects upload requests without deterministic length or exceeding configured limits.""" if request.url.path.endswith("/api/v1/documents/upload"): content_length = request.headers.get("content-length", "").strip() if not content_length: return JSONResponse( status_code=411, content={"detail": "Content-Length header is required for document uploads"}, ) try: content_length_value = int(content_length) except ValueError: return JSONResponse(status_code=400, content={"detail": "Invalid Content-Length header"}) if content_length_value <= 0: return JSONResponse(status_code=400, content={"detail": "Content-Length must be a positive integer"}) if content_length_value > settings.max_upload_request_size_bytes: return JSONResponse( status_code=413, content={ "detail": ( "Upload request exceeds total size limit " f"({content_length_value} > {settings.max_upload_request_size_bytes} bytes)" ) }, ) return await call_next(request) @app.on_event("startup") def startup_event() -> None: """Initializes storage directories and database schema on service startup.""" ensure_storage() ensure_app_settings() init_db() try: ensure_typesense_collection() except Exception: pass try: ensure_handwriting_style_collection() except Exception: pass return app app = create_app()