Files
mission-control-v2/backend/routers/partner.py
T
HitonabiandClaude Opus 5.5 75611be9a9 phase2a: Kern fuer zwei Instanzen (Rolle, Partner, Telegram-Zweitweg)
- kern/einstellungen.py: MC_ROLLE (box|homelab), Instanzname, Datenordner, Partner-URL
- kern/zeit.py: eine Zeitzone statt sechs Kopien
- app.py/steward.py: Box-Router und Warm-Set nur in der Rolle box; der Homelab-Teil
  laedt nichts von der Box
- /api/health nennt Rolle und Instanz; Engine/Gateway/Hirn nur auf der Box
- /api/partner (Lebenszeichen) und /api/partner/<pfad> (Durchreiche, ohne Schleifen und SSE)
- Waechter prueft die andere Instanz (rot erst nach 5 Takten, ein Neustart ist kein Alarm)
- notify.sh: Zweitweg direkt an die Telegram-Bot-API, wenn hermes send scheitert oder fehlt;
  Token nicht in der Prozessliste, Tests greifen nie auf die echte .env
- update_verlauf erkennt "OK telegram direkt"

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 15:41:08 +02:00

44 lines
2.0 KiB
Python

"""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/<pfad> ab; hier wird
daraus <partner>/api/<pfad>. 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("/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"))