homelab: eigener Live-Strom (keine Getrennt-Anzeige mehr), Hinweise des Homelab-Waechters auf der Seite, Verbindungsanzeige je Instanz
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
83c33584e4
commit
160381d8b3
@@ -7,21 +7,29 @@
|
||||
POST /api/homelab/ziele/{id}/docker Docker über Arcane (zuerst Probelauf)
|
||||
POST /api/homelab/ziele/pve/neustart Proxmox-Host neu starten (eigener Knopf)
|
||||
GET /api/homelab/laeufe Die letzten Update-Läufe mit ihren Schritten
|
||||
GET /api/homelab/hinweise Hinweise des Wächters dieser Instanz
|
||||
GET /api/homelab/ausfuehrer Lebenszeichen des Ausführers
|
||||
GET /api/homelab/ausfuehrer/auftrag ┐
|
||||
POST /api/homelab/ausfuehrer/bericht ├ nur für den Ausführer (Kopfzeile X-MC2-Ausfuehrer)
|
||||
POST /api/homelab/ausfuehrer/ergebnis/{id} ┘
|
||||
|
||||
GET /api/stream Live-Strom dieser Instanz (Anstöße, kein Messpunkt)
|
||||
|
||||
Dünn: die Logik liegt in services/homelab/.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import time
|
||||
|
||||
from fastapi import APIRouter, Depends, Header, HTTPException
|
||||
from fastapi import APIRouter, Depends, Header, HTTPException, Request
|
||||
from fastapi.responses import StreamingResponse
|
||||
from kern.einstellungen import einstellungen
|
||||
from pydantic import BaseModel
|
||||
from services.homelab import inventar, kanal, updates
|
||||
|
||||
router = APIRouter(prefix="/api/homelab", tags=["homelab"])
|
||||
strom_router = APIRouter(prefix="/api", tags=["homelab"])
|
||||
|
||||
|
||||
def _nur_ausfuehrer(x_mc2_ausfuehrer: str | None = Header(default=None)) -> None:
|
||||
@@ -70,6 +78,13 @@ def suchen(ziel_id: str) -> dict:
|
||||
return {"ok": True, "auftrag": kanal.anlegen("suchen", {"vmid": gast["vmid"]})}
|
||||
|
||||
|
||||
@router.get("/hinweise")
|
||||
def hinweise() -> dict:
|
||||
"""Was der Wächter dieser Instanz gerade sieht (Ausführer schweigt, Gast antwortet nicht …)."""
|
||||
from services import waechter
|
||||
return waechter.lese_stand()
|
||||
|
||||
|
||||
@router.get("/laeufe")
|
||||
def laeufe() -> dict:
|
||||
return {"laeufe": updates.laeufe()}
|
||||
@@ -101,3 +116,42 @@ class Ergebnis(BaseModel):
|
||||
@router.post("/ausfuehrer/ergebnis/{auftrag_id}", dependencies=[Depends(_nur_ausfuehrer)])
|
||||
def ergebnis(auftrag_id: str, e: Ergebnis) -> dict:
|
||||
return {"ok": kanal.ergebnis(auftrag_id, e.code, e.text)}
|
||||
|
||||
|
||||
# --- Live-Strom der Homelab-Instanz ---------------------------------------------------------------
|
||||
# Die Oberfläche hört auf /api/stream der Instanz, die sie ausliefert. Im Homelab gibt es keine
|
||||
# Messpunkte der KI-Box; der Strom stößt nur neu laden an, wenn sich Bericht, Läufe oder Hinweise ändern,
|
||||
# und meldet sich alle 10 s mit einem leeren Anstoß (sonst hielte die Oberfläche die Leitung für tot).
|
||||
_DATEIEN = (("homelab", "pve-bericht.json"), ("homelab-laeufe", "homelab-laeufe.json"),
|
||||
("homelab-hinweise", "mc2-waechter.json"))
|
||||
|
||||
|
||||
def _abdruecke() -> dict[str, float]:
|
||||
ordner = einstellungen().daten_dir
|
||||
abdruck = {}
|
||||
for schluessel, name in _DATEIEN:
|
||||
try:
|
||||
abdruck[schluessel] = (ordner / name).stat().st_mtime
|
||||
except OSError:
|
||||
abdruck[schluessel] = 0.0
|
||||
return abdruck
|
||||
|
||||
|
||||
async def _strom(request: Request, takt_s: float = 2.0, lebenszeichen_takte: int = 5):
|
||||
alt = _abdruecke()
|
||||
yield ": verbunden\n\n"
|
||||
takte = 0
|
||||
while not await request.is_disconnected():
|
||||
await asyncio.sleep(takt_s)
|
||||
takte += 1
|
||||
neu = _abdruecke()
|
||||
schluessel = [k for k, v in neu.items() if v != alt.get(k)]
|
||||
alt = neu
|
||||
if schluessel or takte % lebenszeichen_takte == 0:
|
||||
yield f"event: invalidate\ndata: {json.dumps({'keys': schluessel})}\n\n"
|
||||
|
||||
|
||||
@strom_router.get("/stream")
|
||||
async def stream(request: Request) -> StreamingResponse:
|
||||
return StreamingResponse(_strom(request), media_type="text/event-stream",
|
||||
headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"})
|
||||
|
||||
Reference in New Issue
Block a user