Files
semapform_api/api_service.py
WorldTeacher e0988cf23b
Some checks failed
PR tests / build-image (pull_request) Successful in 2m44s
PR tests / smoke-tests (pull_request) Successful in 34s
/ build (pull_request) Failing after 42s
lint
2025-11-25 10:52:59 +01:00

105 lines
3.3 KiB
Python

"""Lightweight Python API service for signature validation
This can run independently to support the PHP application
"""
import os
import re
# Avoid importing heavy modules at top-level to keep `import api_service` lightweight
from fastapi import FastAPI, Query
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
app = FastAPI(title="Signature Validation API")
# Optional path prefix support: when behind a reverse-proxy that uses a
# URL prefix (eg. `https://api.example.tld/library/...`) set `API_PREFIX` to
# that prefix (example: `/library`) so incoming requests are rewritten to the
# application root. This keeps route definitions unchanged while supporting
# both proxied and direct deployments.
_api_prefix_raw = os.getenv("API_PREFIX", "").strip()
api_prefix = ""
if _api_prefix_raw:
if not _api_prefix_raw.startswith("/"):
_api_prefix_raw = "/" + _api_prefix_raw
api_prefix = _api_prefix_raw.rstrip("/")
@app.middleware("http")
async def _strip_api_prefix(request, call_next):
if api_prefix and request.url.path.startswith(api_prefix):
new_path = request.url.path[len(api_prefix) :]
request.scope["path"] = new_path or "/"
request.scope["root_path"] = api_prefix
return await call_next(request)
# Allow PHP application to call this API
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # In production, restrict to your PHP server domain
allow_credentials=True,
allow_methods=["GET"],
allow_headers=["*"],
)
# Catalogue is expensive to initialize at import time; instantiate lazily
cat = None
def _get_catalogue():
global cat
if cat is None:
# import inside function to avoid expensive work during module import
from bibapi import catalogue as _catalogue
cat = _catalogue.Catalogue()
return cat
@app.get("/api/validate-signature")
async def validate_signature(signature: str = Query(...)):
"""Validate a book signature and return total pages"""
try:
book_result = _get_catalogue().get_book_with_data(signature)
if book_result and hasattr(book_result, "pages") and book_result.pages:
# Try to extract numeric page count
pages_str = str(book_result.pages)
# Extract first number from pages string (e.g., "245 S." -> 245)
match = re.search(r"(\d+)", pages_str)
if match:
total_pages = int(match.group(1))
return JSONResponse(
{"valid": True, "total_pages": total_pages, "signature": signature},
)
return JSONResponse(
{
"valid": False,
"error": "Signatur nicht gefunden oder keine Seitenzahl verfügbar",
"signature": signature,
},
)
except Exception as e:
return JSONResponse(
{
"valid": False,
"error": f"Fehler bei der Validierung: {e!s}",
"signature": signature,
},
status_code=500,
)
@app.get("/health")
async def health_check():
"""Health check endpoint"""
return {"status": "ok", "service": "signature-validation"}
if __name__ == "__main__":
import uvicorn
port = int(os.getenv("API_PORT", "8001"))
uvicorn.run(app, host="0.0.0.0", port=port)