einstellungen: Zeitfenster, Radar-Schalter, Telegram-Test, Software-Stand und Altreste
Einstellungen, die etwas einstellen (services/einstellungen.py, /api/einstellungen): - Update-Zeitfenster: Wochentag und Uhrzeit des Update-Laufs als Drop-in mc2-autoupdate.timer.d/zeitfenster.conf. Gegen die Nachhol-Falle von Persistent=true (24.09.): Timer vor dem daemon-reload stoppen, Stempel auf jetzt, dann starten. Bei Fehler zurück auf das alte Drop-in. Während eines Update-Laufs abgelehnt. - Neustart nur im eingestellten Fenster (deploy/wartungsfenster.sh, drei Stunden ab der vollen Stunde des Beginns; Standard So 04:00-06:59). - Radar an/aus: disable --now bzw. enable + Stempel + start (kein Nachholen). deploy.sh respektiert den Schalter und startet den Radar-Timer nur mit Stempel; das Zeitfenster-Drop-in fasst er nie an. - Wächter: schläft der Timer eines Timer-Dienstes, ist ein alter Fehlschlag kein Befund mehr (der Dienst selbst ist static und schläft nie). - Telegram-Test je Instanz (/api/einstellungen/telegram-test und /api/homelab/einstellungen/telegram-test) über notify.sh -d; Ergebnis und Grund aus dem Melde-Log. notify.sh schreibt jetzt auch den Grund des gescheiterten Zweitwegs in den FALLBACK-Eintrag. - Einstellungsdatei mc2-einstellungen.json im Datenordner. Software-Stand: deploy.sh und homelab/ausrollen.sh schreiben nach dem Umschalten mc2-stand.json (deploy/stand-schreiben.py); /api/instanz liefert den Stand, die Einstellungen zeigen beide Instanzen und warnen bei Abweichung. Ohne Datei aus git, sonst unbekannt. Aufräumen: Altreste neben dem Betrieb aus einer festen, auf der Box geprüften Liste (Größe, Hinweis, nur was da und nicht in Gebrauch ist). Löschen nur per Kennung und Klick mit Rückfrage; Worktrees mit git worktree prune, Alt-Units mit daemon-reload, /opt/llamacpp notfalls mit sudo -n. Tests: pytest mit einem systemd-Abbild samt Nachhol-Regel, echtem notify.sh (Ersatz-Hermes, Tmp-Zugang, lokale Bot-API), Shell-Funktionen des Wartungsfensters und von deploy.sh; vitest für die Rückfragen der Schublade. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
40d2840b16
commit
3d8df1c6fd
@@ -10,6 +10,8 @@ Box-Wart-Schnittstellen (Umbau 09/2026): alles, was der Cockpit-Startbildschirm
|
||||
GET /api/modelle/aufraeumen Was auf der Modell-Platte ungenutzt Platz belegt
|
||||
GET /api/ziele Die KI-Box als Ziel mit Bausteinen (gemeinsames Modell, Phase 2)
|
||||
POST /api/modelle/aufraeumen/loeschen Einen Kandidaten löschen (nur auf Knopfdruck)
|
||||
GET /api/aufraeumen/altreste Altreste neben dem Betrieb (feste Liste, nur was da ist)
|
||||
POST /api/aufraeumen/altreste/loeschen Einen Altrest löschen — per Kennung, nie per Pfad
|
||||
|
||||
Dünn: die Logik liegt in services/waechter.py, modell_nutzung.py, zeitplan.py, update_verlauf.py.
|
||||
"""
|
||||
@@ -197,6 +199,24 @@ def aufraeumen_loeschen(auftrag: LoeschAuftrag) -> dict:
|
||||
return ergebnis
|
||||
|
||||
|
||||
class AltrestAuftrag(BaseModel):
|
||||
id: str # Kennung aus der festen Liste (services/aufraeumen.ALTRESTE), nie ein Pfad
|
||||
|
||||
|
||||
@router.get("/aufraeumen/altreste")
|
||||
def altreste_liste() -> dict:
|
||||
"""Altreste neben dem Betrieb (Test-Ordner, alte Umgebungen, Worktrees …), nur was da und nicht in Gebrauch ist."""
|
||||
return {"altreste": aufraeumen.altreste()}
|
||||
|
||||
|
||||
@router.post("/aufraeumen/altreste/loeschen")
|
||||
def altrest_loeschen(auftrag: AltrestAuftrag) -> dict:
|
||||
ergebnis = aufraeumen.altrest_loeschen(auftrag.id)
|
||||
if not ergebnis.get("ok"):
|
||||
raise HTTPException(status_code=409, detail=ergebnis.get("detail") or "Löschen ging nicht.")
|
||||
return ergebnis
|
||||
|
||||
|
||||
@router.get("/ziele")
|
||||
def ziele() -> dict:
|
||||
"""Die KI-Box im gemeinsamen Ziel-Modell (kern/ziele.py). Der Homelab-Teil liefert seine Ziele ab
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
"""Einstellungen der Oberfläche (seit 24.09.2026).
|
||||
|
||||
KI-Box (Rolle box):
|
||||
GET /api/einstellungen Update-Zeitfenster und Radar-Schalter samt Zustand laut systemd
|
||||
POST /api/einstellungen/update-fenster {tag: 1–7, uhrzeit: "HH:MM"}
|
||||
POST /api/einstellungen/radar {an: true|false}
|
||||
POST /api/einstellungen/telegram-test Testmeldung über notify.sh, Ergebnis aus dem Melde-Log
|
||||
Homelab-Teil (Rolle homelab):
|
||||
POST /api/homelab/einstellungen/telegram-test dasselbe für den Meldeweg des Homelab-Teils
|
||||
|
||||
Die Oberfläche schickt alles unter /api/homelab/… an den Homelab-Teil und den Rest an die KI-Box (lib/instanz.ts) —
|
||||
so erreicht jeder Test-Knopf die richtige Instanz, egal welche die Seite ausliefert. Dünn: die Logik liegt in
|
||||
services/einstellungen.py.
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from pydantic import BaseModel
|
||||
from services import einstellungen
|
||||
|
||||
router = APIRouter(prefix="/api/einstellungen", tags=["einstellungen"])
|
||||
homelab_router = APIRouter(prefix="/api/homelab/einstellungen", tags=["einstellungen"])
|
||||
|
||||
|
||||
class Zeitfenster(BaseModel):
|
||||
tag: int
|
||||
uhrzeit: str
|
||||
|
||||
|
||||
class Schalter(BaseModel):
|
||||
an: bool
|
||||
|
||||
|
||||
def _antwort(ergebnis: dict) -> dict:
|
||||
if not ergebnis.get("ok"):
|
||||
raise HTTPException(status_code=409, detail=ergebnis.get("detail") or "Das ging gerade nicht.")
|
||||
return ergebnis
|
||||
|
||||
|
||||
@router.get("")
|
||||
def stand() -> dict:
|
||||
return einstellungen.stand()
|
||||
|
||||
|
||||
@router.post("/update-fenster")
|
||||
def update_fenster(fenster: Zeitfenster) -> dict:
|
||||
return _antwort(einstellungen.update_fenster_setzen(fenster.tag, fenster.uhrzeit))
|
||||
|
||||
|
||||
@router.post("/radar")
|
||||
def radar(schalter: Schalter) -> dict:
|
||||
return _antwort(einstellungen.radar_schalten(schalter.an))
|
||||
|
||||
|
||||
@router.post("/telegram-test")
|
||||
def telegram_test() -> dict:
|
||||
"""Gesendet oder nicht, beides ist eine Antwort (200) — der Grund steht im Ergebnis."""
|
||||
return einstellungen.telegram_test()
|
||||
|
||||
|
||||
@homelab_router.post("/telegram-test")
|
||||
def telegram_test_homelab() -> dict:
|
||||
return einstellungen.telegram_test()
|
||||
@@ -9,6 +9,7 @@ import httpx
|
||||
from fastapi import APIRouter, HTTPException, Request, Response
|
||||
from kern import partner
|
||||
from kern.einstellungen import einstellungen
|
||||
from services import software_stand
|
||||
|
||||
router = APIRouter(prefix="/api", tags=["partner"])
|
||||
|
||||
@@ -21,10 +22,12 @@ _ZEITLIMIT = httpx.Timeout(30.0, connect=5.0)
|
||||
@router.get("/instanz")
|
||||
def instanz() -> dict:
|
||||
"""Wer liefert die Seite aus? Ohne Netzabfragen, damit die Oberfläche sofort weiß, welche Anfragen
|
||||
sie selbst beantwortet und welche über /api/partner/… an die andere Instanz gehen."""
|
||||
sie selbst beantwortet und welche über /api/partner/… an die andere Instanz gehen. Seit 24.09.2026 mit dem
|
||||
Software-Stand (welcher Commit seit wann live ist); den der anderen Instanz liefert /api/partner/instanz."""
|
||||
e = einstellungen()
|
||||
return {"rolle": e.rolle, "instanz": e.instanz,
|
||||
"partner": {"eingerichtet": bool(e.partner_url), "name": e.partner_name}}
|
||||
"partner": {"eingerichtet": bool(e.partner_url), "name": e.partner_name},
|
||||
"stand": software_stand.stand()}
|
||||
|
||||
|
||||
@router.get("/partner")
|
||||
|
||||
Reference in New Issue
Block a user