From ba0bb1c8b99ca9eddcad768929b222317c908b6e Mon Sep 17 00:00:00 2001 From: jp170na Date: Fri, 14 Aug 2026 23:45:48 +0200 Subject: [PATCH] routes --- app/routes.py | 111 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 96 insertions(+), 15 deletions(-) diff --git a/app/routes.py b/app/routes.py index b06a944..c9a2892 100644 --- a/app/routes.py +++ b/app/routes.py @@ -54,11 +54,20 @@ logger = logging.getLogger(__name__) router = APIRouter() +MAX_WEBHOOK_BODY_BYTES = ( + 2 * 1024 * 1024 +) + + RETRIEVAL_UNAVAILABLE_DETAIL = ( "Vyhľadávací index alebo embeddingový " "model momentálne nie je dostupný." ) +INVALID_QUERY_DETAIL = ( + "Neplatný vyhľadávací dotaz." +) + INTERNAL_ERROR_DETAIL = ( "Nastala interná chyba servera." ) @@ -166,21 +175,16 @@ class HealthResponse(BaseModel): ready: bool database_exists: bool - database_path: str search_engine: str rag_enabled: bool - zpwiki_root: str zpwiki_exists: bool security_configured: bool class RagSource(BaseModel): - # RAG source sa môže v budúcnosti - # rozšíriť o ďalšie retrieval metadata - # bez rozbitia response modelu. model_config = ConfigDict( extra="allow", ) @@ -302,12 +306,17 @@ def raise_retrieval_error( error, ValueError, ): + logger.warning( + "Neplatný retrieval dotaz: %s", + error, + ) + raise HTTPException( status_code=( status.HTTP_400_BAD_REQUEST ), - detail=str( - error + detail=( + INVALID_QUERY_DETAIL ), ) from error @@ -420,6 +429,79 @@ def configured_repository() -> str: ) from error +async def read_webhook_body( + request: Request, +) -> bytes: + content_length = request.headers.get( + "content-length" + ) + + if content_length: + try: + declared_length = int( + content_length + ) + + except ValueError as error: + raise HTTPException( + status_code=400, + detail=( + "Neplatná Content-Length " + "hlavička" + ), + ) from error + + if declared_length < 0: + raise HTTPException( + status_code=400, + detail=( + "Neplatná Content-Length " + "hlavička" + ), + ) + + if ( + declared_length + > MAX_WEBHOOK_BODY_BYTES + ): + raise HTTPException( + status_code=413, + detail=( + "Webhook payload je " + "príliš veľký" + ), + ) + + chunks: list[bytes] = [] + + total_size = 0 + + async for chunk in request.stream(): + total_size += len( + chunk + ) + + if ( + total_size + > MAX_WEBHOOK_BODY_BYTES + ): + raise HTTPException( + status_code=413, + detail=( + "Webhook payload je " + "príliš veľký" + ), + ) + + chunks.append( + chunk + ) + + return b"".join( + chunks + ) + + @router.get( "/health", include_in_schema=False, @@ -436,6 +518,7 @@ def health() -> dict[str, Any]: try: validate_security_configuration() + security_configured = True except RuntimeError: @@ -453,16 +536,10 @@ def health() -> dict[str, Any]: "database_exists": ( database_exists ), - "database_path": str( - DB_FILE - ), "search_engine": ( "hybrid_fts5_embeddings" ), "rag_enabled": True, - "zpwiki_root": str( - ZPWIKI_ROOT - ), "zpwiki_exists": ( zpwiki_exists ), @@ -665,10 +742,14 @@ async def gitea_webhook( dict[str, Any] | JSONResponse ): - raw_body = await request.body() - secret = webhook_secret() + raw_body = await read_webhook_body( + request + ) + + # Gitea podpis overujeme nad presným raw + # request body ešte pred JSON parsingom. if not verify_gitea_signature( raw_body, x_gitea_signature,