monitor/API.md
fds2610-bot 10ecad93d5 Phase 3-5 der Neuausrichtung: Board/Monitor-Wand live, SSE-Push, Agenten-Animation
Setzt Visualization.md Abschnitt 9, Phasen 3-5 vollstaendig um:

Phase 3 (Kanban-Board live): Board zeigt echte Ticket-Daten aus GET /api/v1/tickets
(office-api.js), Klick auf eine Spalte oeffnet ein Modal mit Titel/Beschreibung/Typ und
einem Link auf die Codeberg-Quelle bei per Sync angelegten Tickets (F4).

Phase 4 (Monitor-Wand live): Event-, System- und KPI-Monitor zeigen echte Daten,
Klick-Modals fuer alle drei. Neuer SSE-Endpoint GET /api/v1/stream (F13) meldet per
ChangeFeed-Fingerabdruck, welche Ressourcen sich geaendert haben; office-realtime.js nutzt
EventSource mit Polling-Fallback. KPI-Monitor zeichnet eine eigene SVG-Sparkline (F10, kein
Chart-Lib) sobald KPIs gemeldet wurden, sonst weiterhin der Phase-2-Platzhalter (F7 bleibt
inhaltlich offen). API-Antworten tragen jetzt CORS-Header, da Frontend (:8090) und API
(:8091) im Docker-Compose-Setup unterschiedliche Origins sind.

Phase 5 (Agenten & Animation): resolveAgentVisualState bildet gemeldete Agenten-Zustaende
auf Ort/Pose ab; Agenten-Avatare sind eine per CSS transition animierte, von Board/
Schreibtisch-Moebeln entkoppelte Schicht, die zwischen Schreibtisch, Board, Monitor-Wand und
einer neuen Kaffee-Ecke laufen. Idle-Varianten (Zeitung/Kaffee/Schwaetzchen) werden rein
kosmetisch client-seitig zufaellig ausgeloest (F8), eine AnimationQueue serialisiert
Update-Animationen bei vielen gleichzeitigen Ereignissen (F9).

29 Node-Tests (100% Line-Coverage office-scene.js) und neue PHPUnit-Tests fuer
Response::stream(), ChangeFeed, StreamController und die neuen Repository::fingerprint()-
Methoden. PHP/Docker-Ausfuehrung konnte in dieser Umgebung nicht verifiziert werden (kein
PHP verfuegbar) - vor dem Merge bitte `docker compose run --rm api composer test` pruefen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-28 17:50:44 +02:00

9.4 KiB
Raw Blame History

monitor REST-API (Phase 1)

Diese API ist die zentrale Schnittstelle, ueber die entfernte Agenten (Menschen oder automatisierte Systeme) ihre Aktivitaet melden: Tickets anlegen/verschieben, Events melden, Systemstatus berichten, KPIs eintragen und den eigenen Zustand aktualisieren. Sie ist Teil von Phase 1 der Neuausrichtung (siehe Visualization.md, Abschnitt 9).

Implementierung: Vanilla-PHP (kein Framework), Persistenz in SQLite (api/data/monitor.sqlite). Details zum lokalen Setup: api/README.md.

Inhaltsverzeichnis

Grundlagen

  • Basis-Pfad: /api/v1
  • Request-/Response-Body: application/json
  • Alle Zeitstempel: ISO-8601 UTC (YYYY-MM-DDTHH:mm:ssZ), serverseitig erzeugt via Database::now(), sofern nicht explizit im Request angegeben.
  • Lesende Endpunkte (GET) sind oeffentlich, da sie das Frontend bedienen.
  • Schreibende Endpunkte (POST/PATCH) erfordern einen Agenten- oder Admin-Key (siehe Authentifizierung).
  • Alle Antworten tragen Access-Control-Allow-Origin: * (CORS), da Frontend und API im Docker-Compose-Setup auf unterschiedlichen Ports laufen (siehe docker-compose.yml: Frontend :8090, API :8091).

Authentifizierung

Zwei Schluesseltypen, beide statisch (F14):

Header Verwendet fuer Herkunft
X-Admin-Key POST /api/v1/agents (Registrierung neuer Agenten) Umgebungsvariable MONITOR_ADMIN_API_KEY
X-Api-Key Alle anderen schreibenden Endpunkte Wird bei der Agenten-Registrierung einmalig zurueckgegeben

Der Klartext-API-Key eines Agenten wird nur einmal bei der Registrierung ausgegeben. Gespeichert wird ausschliesslich ein SHA-256-Hash in SQLite (Tabelle agents, Spalte api_key_hash) - der Klartext ist danach nicht mehr rekonstruierbar und muss vom Agenten sicher verwahrt werden.

Fehlt oder passt ein Key nicht, antwortet die API mit 401 Unauthorized.

Agenten

Rollen (F1/F2): entwickler, product_owner, qa, user. Ein Agent bekommt bei der Registrierung fest einen von MONITOR_DESK_COUNT (Standard 8) Schreibtischen zugewiesen (niedrigster freier Index). Die Rolle user bekommt nie einen Schreibtisch (kein fester Sitzplatz). Sind alle Schreibtische belegt, wird trotzdem registriert, aber ohne Schreibtisch (desk_index: null).

POST /api/v1/agents — Agent registrieren

Auth: X-Admin-Key.

// Request
{ "display_name": "Alice", "role": "entwickler" }
// Response 201
{
  "agent": {
    "id": 1, "display_name": "Alice", "role": "entwickler",
    "desk_index": 0, "state": "idle", "current_ticket_id": null,
    "created_at": "2026-07-27T10:00:00Z", "last_seen_at": "2026-07-27T10:00:00Z"
  },
  "api_key": "‹64 Hex-Zeichen, nur jetzt sichtbar›"
}

GET /api/v1/agents — Alle Agenten

Response: { "agents": [ ‹Agent-Objekte wie oben, ohne api_key› ] }

POST /api/v1/agents/{id}/activity — Eigenen Zustand melden

Auth: X-Api-Key des betroffenen Agenten (ein Agent darf nur seinen eigenen Zustand melden, sonst 403).

// Request
{ "state": "working", "current_ticket": 42 }

Response 200: aktualisiertes Agent-Objekt.

Tickets

Spalten (F3): backlog, bereit, in_arbeit, qa, testing, fertig, done.

  • backlog und done werden zusaetzlich regelmaessig aus Codeberg-Issues befuellt (siehe Codeberg-Sync), alle anderen Bewegungen ergeben sich ausschliesslich aus Agenten-Meldungen (F4).
  • Ticket-Typ und Farbe (statt Prioritaet) werden bei Codeberg-Tickets aus dem ersten Issue-Label uebernommen (F4).
  • Stapel-Darstellung ("aelteste 4 + Rest als Stapel", F4) ist reine Frontend-Logik; die API liefert Tickets dafuer sortiert nach Spalte und Alter.

GET /api/v1/tickets — Board-Zustand

Query-Parameter: column (optional, filtert auf eine Spalte).

Response: { "tickets": [ { "id", "external_ref", "title", "description", "column_name", "type", "color", "assignee_agent_id", "source", "created_at", "updated_at" }, ... ] }

POST /api/v1/tickets — Ticket anlegen

Auth: X-Api-Key.

// Request (nur "title" ist Pflicht)
{ "title": "Neues Bug-Ticket", "description": "...", "column": "backlog", "type": "bug", "color": "#ff0000" }

Response 201 mit dem angelegten Ticket, 422 bei fehlendem title oder ungueltiger column.

PATCH /api/v1/tickets/{id} — Ticket verschieben/aendern

Auth: X-Api-Key. Erlaubte Felder im Body: column, title, description, type, color, assignee_agent_id (alle optional, nur uebergebene Felder werden geaendert).

Response 200 mit dem aktualisierten Ticket, 404 falls unbekannt, 422 bei ungueltiger column.

Events

severity: warning (gelb) oder error (rot). Neuestes Event zuerst (F5).

POST /api/v1/events — Event melden

Auth: X-Api-Key.

{ "system_id": "srv1", "name": "Ping", "message": "Host nicht erreichbar", "severity": "error" }

Pflichtfelder: name, message, severity. occurred_at optional (Default: jetzt). Response 201 mit dem gespeicherten Event.

GET /api/v1/events — Event-Liste

Query-Parameter: limit (Default 5, entspricht der im Plan festgelegten Zeilenzahl des Event-Monitors, F5; Maximum intern 200).

Response: { "events": [ { "id", "system_id", "name", "message", "severity", "occurred_at", "created_at" }, ... ] }

Systeme

Bis zu 9 Kacheln (F6, SystemRepository::MAX_TILES), Fehler zuerst sortiert. Aktuell werden Systeme zusaetzlich aus der bestehenden status.txt (geschrieben von check_fds.sh) importiert; per API gemeldete Systeme (source: "api") haben Vorrang und werden vom Legacy-Import nicht ueberschrieben.

POST /api/v1/systems/{id}/status — Systemstatus melden

Auth: X-Api-Key.

{ "status": "ok", "name": "Webserver", "type": "host", "detail": "optional" }

status: ok oder error. Response 200 mit dem gespeicherten System, 422 bei ungueltigem status.

GET /api/v1/systems — Systemuebersicht

Response: { "systems": [ { "id", "name", "type", "status", "detail", "source", "last_check_at" }, ... ] } (maximal 9 Eintraege).

KPIs

F7: Konkrete Kennzahlen sind noch offen ("kommt spaeter"). Phase 1 stellt nur die generische Speicher-/Abfragestruktur bereit.

POST /api/v1/kpis/{key} — KPI-Datenpunkt melden

Auth: X-Api-Key.

{ "value": 12.5, "label": "Offene Tickets", "unit": "count", "timestamp": "2026-07-27T10:00:00Z" }

Pflichtfeld: value (numerisch). label/unit/timestamp optional. Response 201 mit dem KPI inkl. Verlauf.

GET /api/v1/kpis — Alle KPIs

Response: { "kpis": [ { "key", "label", "unit", "current_value", "history": [ { "value", "recorded_at" }, ... ] }, ... ] }

Live-Updates (SSE)

F13: Das Frontend haelt sich per Server-Sent Events ueber Aenderungen auf dem Laufenden, statt staendig zu pollen.

GET /api/v1/stream — Aenderungs-Feed

Auth: keine (oeffentlich wie die anderen GET-Endpunkte). Antwort: Content-Type: text/event-stream.

Die Verbindung bleibt fuer MONITOR_SSE_MAX_ITERATIONS * MONITOR_SSE_POLL_INTERVAL Sekunden offen (Default: 30 * 2s = ca. 1 Minute) und prueft alle MONITOR_SSE_POLL_INTERVAL Sekunden serverseitig, ob sich Tickets, Events, Systeme, KPIs oder Agenten veraendert haben (billiger "Fingerabdruck" aus Anzahl + juengstem Zeitstempel je Ressource, keine vollstaendige Änderungshistorie). Danach schliesst der Server die Verbindung; EventSource im Browser verbindet automatisch neu (Standardverhalten).

Bei einer Aenderung wird ein update-Event gesendet, das nur benennt, welche Ressourcen sich geaendert haben - das Frontend laedt die betroffenen Endpunkte anschliessend regulaer per GET neu:

event: update
data: {"changed": ["tickets", "events"]}

Ohne Aenderung sendet der Server stattdessen alle MONITOR_SSE_POLL_INTERVAL Sekunden einen Kommentar (: heartbeat) als Keep-Alive, damit Proxys die Verbindung nicht vorzeitig schliessen.

Codeberg-Sync (Backlog/Done)

api/bin/sync-codeberg.php (per Cron oder manuell auszufuehren) liest offene und geschlossene Issues aus dem konfigurierten Codeberg-Repo (CODEBERG_OWNER/CODEBERG_REPO) und gleicht sie ueber external_ref (= Issue-Nummer) mit den Tickets ab:

  • Offene Issues → Spalte backlog (nur falls das Ticket noch in backlog/done ist - manuell auf dem Board bewegte Tickets werden nicht zurueckgesetzt).
  • Geschlossene Issues → Spalte done.
  • Ticket-Typ/-Farbe kommen vom ersten Issue-Label (Name/Farbe).

Aufruf via Docker: docker compose run --rm api php bin/sync-codeberg.php.

Fehlerformat

Alle Fehlerantworten haben die Form { "error": "‹Meldung›" } mit passendem HTTP-Status:

Status Bedeutung
400 Ungueltige Anfrage (z. B. unbekannter Endpunkt-Parameter)
401 Fehlender/ungueltiger X-Api-Key/X-Admin-Key
403 Aktion nicht erlaubt (z. B. fremde Agent-Aktivitaet melden)
404 Ressource nicht gefunden
405 HTTP-Methode fuer diesen Pfad nicht erlaubt
422 Validierungsfehler (Pflichtfeld fehlt, ungueltiger Enum-Wert)