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>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
99ba6a78ab
commit
75611be9a9
@@ -0,0 +1,53 @@
|
||||
"""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)
|
||||
Reference in New Issue
Block a user