diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 3dadfca..d8262b2 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -37,6 +37,6 @@ jobs: - name: Smoke test /health endpoint run: | - docker run -d --name smoke -e PORT=8080 -e OPENAI_API_KEY=test -p 8080:8080 signwriting-description + docker run -d --name smoke -e PORT=8080 -e OPENAI_API_KEY=test -e TURNSTILE_SECRET_KEY=1x0000000000000000000000000000000AA -p 8080:8080 signwriting-description sleep 5 # wait for uvicorn cold start curl -sf http://localhost:8080/health diff --git a/signwriting_description/server.py b/signwriting_description/server.py index 0a85429..dee23b1 100644 --- a/signwriting_description/server.py +++ b/signwriting_description/server.py @@ -1,9 +1,10 @@ import os from datetime import UTC, datetime +import httpx import uvicorn from dotenv import load_dotenv -from fastapi import FastAPI, HTTPException, Query +from fastapi import FastAPI, HTTPException, Query, Request from fastapi.responses import JSONResponse from signwriting.formats.fsw_to_sign import fsw_to_sign @@ -15,9 +16,35 @@ if not OPENAI_API_KEY: raise RuntimeError("Missing OPENAI_API_KEY environment variable") +TURNSTILE_SECRET_KEY = os.getenv("TURNSTILE_SECRET_KEY") +if not TURNSTILE_SECRET_KEY: + raise RuntimeError("Missing TURNSTILE_SECRET_KEY environment variable") + app = FastAPI(title="Signwriting Description API") + +@app.middleware("http") +async def turnstile_verification(request: Request, call_next): + if request.url.path == "/health": + return await call_next(request) + + token = request.headers.get("cf-turnstile-response") + if not token: + return JSONResponse(status_code=403, content={"error": "Missing Turnstile token"}) + + async with httpx.AsyncClient() as client: + result = await client.post( + "https://challenges.cloudflare.com/turnstile/v0/siteverify", + data={"secret": TURNSTILE_SECRET_KEY, "response": token}, + ) + + if not result.json().get("success"): + return JSONResponse(status_code=403, content={"error": "Invalid Turnstile token"}) + + return await call_next(request) + + @app.get("/health") def health(): return {