"""Die Partner-Instanz (User-Entscheid 24.09.2026: zwei Instanzen, eine Oberfläche). Box-Wart läuft auf der KI-Box, der Homelab-Teil in einem Container auf dem Proxmox-PC. Jede Instanz kennt die andere über MC_PARTNER_URL. Sie fragt deren Lebenszeichen ab (für den Wächter und die Kopfzeile der Oberfläche) und reicht Anfragen der Oberfläche unter /api/partner/… durch, damit eine Seite beide Bereiche zeigt — egal, auf welcher Instanz man sie öffnet. """ import threading import time import httpx from kern.einstellungen import einstellungen ZEITLIMIT_S = 5.0 CACHE_S = 15.0 _lock = threading.Lock() _cache: dict = {"ts": 0.0, "daten": None} def _kurz(exc: Exception) -> str: if isinstance(exc, httpx.TimeoutException): return "antwortet nicht rechtzeitig" if isinstance(exc, httpx.ConnectError): return "nicht erreichbar" if isinstance(exc, httpx.HTTPStatusError): return f"antwortet mit HTTP {exc.response.status_code}" return f"{exc.__class__.__name__}: {exc}"[:160] def status(frisch: bool = False) -> dict: """Lebenszeichen der Partner-Instanz. Ohne MC_PARTNER_URL: {"eingerichtet": False}.""" e = einstellungen() if not e.partner_url: return {"eingerichtet": False} with _lock: if not frisch and _cache["daten"] and time.time() - _cache["ts"] < CACHE_S: return dict(_cache["daten"]) daten: dict = {"eingerichtet": True, "name": e.partner_name, "url": e.partner_url, "erreichbar": False, "rolle": None, "instanz": None, "version": None, "fehler": None} try: r = httpx.get(f"{e.partner_url}/api/health", timeout=ZEITLIMIT_S) r.raise_for_status() h = r.json() daten.update(erreichbar=True, rolle=h.get("rolle"), instanz=h.get("instanz"), version=h.get("version")) except Exception as exc: daten["fehler"] = _kurz(exc) daten["geprueft"] = time.time() with _lock: _cache.update(ts=time.time(), daten=daten) return dict(daten)