"""Partner-Instanz: Lebenszeichen und Durchreiche (zwei Instanzen, eine Oberfläche, 24.09.2026). Die Oberfläche fragt den Bereich der anderen Instanz über /api/partner/ ab; hier wird daraus /api/. Die Herkunftsprüfung (services/herkunft.py) greift wie bei jeder anderen schreibenden Anfrage schon hier, bevor etwas weitergereicht wird. """ import httpx from fastapi import APIRouter, HTTPException, Request, Response from kern import partner from kern.einstellungen import einstellungen router = APIRouter(prefix="/api", tags=["partner"]) # Nicht durchreichen: den Partner-Weg des Partners (sonst reichen sich zwei Instanzen eine Anfrage # endlos zu) und den Live-Strom (SSE hält die Verbindung offen; der Partner-Bereich fragt ab). _GESPERRT = ("partner", "stream") _ZEITLIMIT = httpx.Timeout(30.0, connect=5.0) @router.get("/instanz") def instanz() -> dict: """Wer liefert die Seite aus? Ohne Netzabfragen, damit die Oberfläche sofort weiß, welche Anfragen sie selbst beantwortet und welche über /api/partner/… an die andere Instanz gehen.""" e = einstellungen() return {"rolle": e.rolle, "instanz": e.instanz, "partner": {"eingerichtet": bool(e.partner_url), "name": e.partner_name}} @router.get("/partner") def partner_status() -> dict: return partner.status() @router.api_route("/partner/{pfad:path}", methods=["GET", "POST", "PUT", "PATCH", "DELETE"], include_in_schema=False) async def weiterreichen(pfad: str, request: Request) -> Response: e = einstellungen() if not e.partner_url: raise HTTPException(status_code=404, detail="Es ist keine zweite Instanz eingerichtet.") if pfad.split("/", 1)[0] in _GESPERRT: raise HTTPException(status_code=404, detail="Diese Schnittstelle wird nicht weitergereicht.") kopf = {"X-MC-Von": e.instanz} if request.headers.get("content-type"): kopf["Content-Type"] = request.headers["content-type"] try: async with httpx.AsyncClient(timeout=_ZEITLIMIT) as c: r = await c.request(request.method, f"{e.partner_url}/api/{pfad}", params=request.query_params, content=await request.body(), headers=kopf) except httpx.HTTPError: raise HTTPException(status_code=502, detail=f"{e.partner_name} ist gerade nicht erreichbar.") from None return Response(content=r.content, status_code=r.status_code, media_type=r.headers.get("content-type", "application/json"))