Files
breakpilot-lehrer/backend-lehrer/api/klausur_proxy.py
Benjamin Admin cba877c65a
Some checks failed
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 37s
CI / test-go-edu-search (push) Successful in 35s
CI / test-python-klausur (push) Failing after 2m41s
CI / test-python-agent-core (push) Successful in 30s
CI / test-nodejs-website (push) Successful in 38s
Restructure: Move final 16 root files into packages (backend-lehrer)
classroom/ (+2): state_engine_api, state_engine_models
vocabulary/ (2): api, db
worksheets/ (2): api, models
services/  (+6): audio, email, translation, claude_vision, ai_processor, story_generator
api/        (4): school, klausur_proxy, progress, user_language

Only main.py + config.py remain at root. 16 shims added.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 22:50:37 +02:00

136 lines
4.7 KiB
Python

"""
Klausur-Service API Proxy
Routes API requests from /api/klausur/* to the klausur-service microservice
"""
import os
import jwt
import datetime
import httpx
from fastapi import APIRouter, Request, HTTPException, Response
# Klausur Service URL
KLAUSUR_SERVICE_URL = os.getenv("KLAUSUR_SERVICE_URL", "http://klausur-service:8086")
JWT_SECRET = os.getenv("JWT_SECRET", "your-super-secret-jwt-key-change-in-production")
ENVIRONMENT = os.getenv("ENVIRONMENT", "development")
# Demo teacher UUID for development mode
DEMO_TEACHER_ID = "e9484ad9-32ee-4f2b-a4e1-d182e02ccf20"
router = APIRouter(prefix="/klausur", tags=["klausur"])
def get_demo_token() -> str:
"""Generate a demo JWT token for development mode"""
payload = {
"user_id": DEMO_TEACHER_ID,
"email": "demo@breakpilot.app",
"role": "admin",
"exp": datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(hours=24),
"iat": datetime.datetime.now(datetime.timezone.utc)
}
return jwt.encode(payload, JWT_SECRET, algorithm="HS256")
async def proxy_request(request: Request, path: str) -> Response:
"""Forward a request to the klausur service"""
url = f"{KLAUSUR_SERVICE_URL}/api/v1{path}"
# Forward headers, especially Authorization
headers = {}
if "authorization" in request.headers:
headers["Authorization"] = request.headers["authorization"]
elif ENVIRONMENT == "development":
# In development mode, use demo token if no auth provided
demo_token = get_demo_token()
headers["Authorization"] = f"Bearer {demo_token}"
if "content-type" in request.headers:
headers["Content-Type"] = request.headers["content-type"]
# Get request body for POST/PUT/PATCH/DELETE
body = None
if request.method in ("POST", "PUT", "PATCH", "DELETE"):
body = await request.body()
async with httpx.AsyncClient(timeout=60.0) as client:
try:
response = await client.request(
method=request.method,
url=url,
headers=headers,
content=body,
params=request.query_params
)
return Response(
content=response.content,
status_code=response.status_code,
headers=dict(response.headers),
media_type=response.headers.get("content-type", "application/json")
)
except httpx.ConnectError:
raise HTTPException(
status_code=503,
detail="Klausur service unavailable"
)
except httpx.TimeoutException:
raise HTTPException(
status_code=504,
detail="Klausur service timeout"
)
# Health check
@router.get("/health")
async def health():
"""Health check for klausur service connection"""
async with httpx.AsyncClient(timeout=5.0) as client:
try:
response = await client.get(f"{KLAUSUR_SERVICE_URL}/health")
return {"klausur_service": "healthy", "connected": response.status_code == 200}
except Exception:
return {"klausur_service": "unhealthy", "connected": False}
# Klausuren
@router.api_route("/klausuren", methods=["GET", "POST"])
async def klausuren(request: Request):
return await proxy_request(request, "/klausuren")
@router.api_route("/klausuren/{klausur_id}", methods=["GET", "PUT", "DELETE"])
async def klausur_by_id(klausur_id: str, request: Request):
return await proxy_request(request, f"/klausuren/{klausur_id}")
# Students
@router.api_route("/klausuren/{klausur_id}/students", methods=["GET", "POST"])
async def klausur_students(klausur_id: str, request: Request):
return await proxy_request(request, f"/klausuren/{klausur_id}/students")
@router.api_route("/students/{student_id}", methods=["GET", "DELETE"])
async def student_by_id(student_id: str, request: Request):
return await proxy_request(request, f"/students/{student_id}")
# Grading
@router.api_route("/students/{student_id}/criteria", methods=["PUT"])
async def update_criteria(student_id: str, request: Request):
return await proxy_request(request, f"/students/{student_id}/criteria")
@router.api_route("/students/{student_id}/gutachten", methods=["PUT"])
async def update_gutachten(student_id: str, request: Request):
return await proxy_request(request, f"/students/{student_id}/gutachten")
@router.api_route("/students/{student_id}/finalize", methods=["POST"])
async def finalize_student(student_id: str, request: Request):
return await proxy_request(request, f"/students/{student_id}/finalize")
# Grade info
@router.get("/grade-info")
async def grade_info(request: Request):
return await proxy_request(request, "/grade-info")