- 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>
54 lines
2.0 KiB
Python
54 lines
2.0 KiB
Python
"""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)
|