feat(api): implement /extract and /validate endpoints with error handling

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
m3tm3re
2026-02-04 20:07:35 +01:00
parent 4791c91f06
commit 867b47efd0
5 changed files with 263 additions and 7 deletions

View File

@@ -1,5 +1,6 @@
"""FastAPI application for ZUGFeRD invoice processing."""
import base64
import json
import logging
from datetime import datetime
@@ -9,8 +10,15 @@ from fastapi import FastAPI, HTTPException, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from src.extractor import ExtractionError
from src.models import HealthResponse
from src.extractor import ExtractionError, extract_zugferd
from src.models import (
ExtractRequest,
ExtractResponse,
HealthResponse,
ValidateRequest,
ValidateResponse,
)
from src.validator import validate_invoice
class JSONFormatter(logging.Formatter):
@@ -60,6 +68,25 @@ async def extraction_error_handler(request: Request, exc: ExtractionError):
)
@app.exception_handler(HTTPException)
async def http_exception_handler(request: Request, exc: HTTPException):
if isinstance(exc.detail, dict) and "error" in exc.detail:
return JSONResponse(
status_code=exc.status_code,
content={
"error": exc.detail.get("error"),
"message": exc.detail.get("message"),
},
)
return JSONResponse(
status_code=exc.status_code,
content={
"error": "http_error",
"message": str(exc.detail),
},
)
@app.exception_handler(Exception)
async def generic_error_handler(request: Request, exc: Exception):
logger.error(f"Internal error: {exc}")
@@ -82,6 +109,41 @@ async def health_check() -> HealthResponse:
return HealthResponse(status="healthy", version="1.0.0")
@app.post("/extract", response_model=ExtractResponse)
async def extract_pdf(request: ExtractRequest) -> ExtractResponse:
"""Extract ZUGFeRD data from PDF.
Args:
request: ExtractRequest with pdf_base64 field
Returns:
ExtractResponse with extraction results
"""
try:
pdf_bytes = base64.b64decode(request.pdf_base64)
except Exception:
raise HTTPException(
status_code=400,
detail={"error": "invalid_base64", "message": "Invalid base64 encoding"},
)
return extract_zugferd(pdf_bytes)
@app.post("/validate", response_model=ValidateResponse)
async def validate_invoice_endpoint(request: ValidateRequest) -> ValidateResponse:
"""Validate ZUGFeRD invoice data.
Args:
request: ValidateRequest with xml_data, pdf_text, checks
Returns:
ValidateResponse with validation results
"""
result = validate_invoice(request)
return ValidateResponse(result=result)
def run(host: str = "0.0.0.0", port: int = 5000) -> None:
"""Run the FastAPI application.