# Info-Board Neu - Erster HTTP-Statuspfad fuer den Player-Agent

## Ziel

Dieses Dokument beschreibt den ersten bewusst kleinen Statuspfad zwischen `player/agent` und `server/backend`.

Er ist fuer die aktuelle Entwicklungsstufe gedacht und noch kein finales Authentifizierungs- oder MQTT-Modell.

## V1-Dev-Entscheidung

- der Agent sendet zunaechst Statusdaten per HTTP an das Backend
- das Backend validiert und bestaetigt den Request und haelt den letzten bekannten Status pro Screen vor
- der Pfad dient als erste echte Backend-Agent-Integration vor Registration, Sync und MQTT

## Endpoints im Ueberblick

| Methode  | Pfad                                  | Beschreibung                        |
|----------|---------------------------------------|-------------------------------------|
| POST     | /api/v1/player/status                 | Statusingest vom Player-Agent       |
| GET      | /api/v1/screens/status                | JSON-Uebersicht aller Screens       |
| GET      | /api/v1/screens/{screenId}/status     | JSON-Detail fuer einen Screen       |
| DELETE   | /api/v1/screens/{screenId}/status     | Screen-Eintrag loeschen             |
| GET      | /status                               | HTML-Diagnoseseite (Uebersicht)     |
| GET      | /status/{screenId}                    | HTML-Diagnoseseite (Einzelscreen)   |

## POST /api/v1/player/status

### Request-Felder

Pflichtfelder:

- `screen_id`
- `ts` (RFC3339-Zeitstempel)
- `status`: `starting`, `running`, `stopped`
- `heartbeat_every_seconds` (positive Ganzzahl)

Optionale Felder:

- `server_connectivity`: `unknown`, `online`, `degraded`, `offline`
- `server_url`
- `mqtt_broker`
- `started_at` (RFC3339-Zeitstempel)
- `last_heartbeat_at` (RFC3339-Zeitstempel)

### Beispiel

```json
{
  "screen_id": "info01-dev",
  "ts": "2026-03-22T16:00:00Z",
  "status": "running",
  "server_connectivity": "online",
  "server_url": "http://127.0.0.1:8080",
  "mqtt_broker": "tcp://127.0.0.1:1883",
  "heartbeat_every_seconds": 30,
  "started_at": "2026-03-22T15:59:30Z",
  "last_heartbeat_at": "2026-03-22T16:00:00Z"
}
```

### Antwort

```json
{ "status": "accepted" }
```

Bei ungueltigen Requests wird der gemeinsame Fehlerumschlag verwendet.

## GET /api/v1/screens/status

Liefert eine Uebersicht aller bisher berichtenden Screens mit ihrem jeweils letzten bekannten Datensatz.

Die Antwort enthaelt:

- `summary` mit kompakten Counts fuer `total`, `online`, `degraded`, `offline`, `stale`
- `screens` sortiert nach Prioritaet: zuerst `offline`, dann `degraded`, dann `online`; innerhalb derselben Gruppe nach `screen_id`

Die `summary` beschreibt immer den gesamten bekannten Statusbestand, unabhaengig von aktiven Filtern.
Die `screens`-Liste wird durch die Query-Parameter eingeschraenkt.

### Query-Parameter

| Parameter           | Erlaubte Werte                          | Fehlercode bei Verstoß              |
|---------------------|-----------------------------------------|-------------------------------------|
| `q`                 | beliebig (Substring-Suche, case-insen.) | –                                   |
| `derived_state`     | `online`, `degraded`, `offline`         | `invalid_derived_state`             |
| `server_connectivity` | `online`, `degraded`, `offline`, `unknown` | `invalid_server_connectivity`  |
| `stale`             | `true`, `false`                         | `invalid_stale`                     |
| `updated_since`     | RFC3339-Zeitstempel                     | `invalid_updated_since`             |
| `limit`             | positive Ganzzahl                       | `invalid_limit`                     |

`q` filtert nach Screen-ID-Substring (Gross-/Kleinschreibung wird ignoriert).

Dieselben Parameter koennen sowohl an `GET /api/v1/screens/status` als auch an `GET /status` uebergeben werden, damit Browser-Ansicht und JSON-Uebersicht dieselbe Diagnosesicht teilen.

## GET /api/v1/screens/{screenId}/status

Liefert den zuletzt akzeptierten Status fuer einen einzelnen Screen.
Wenn fuer den Screen noch kein Status vorliegt, liefert das Backend `404` mit dem gemeinsamen Fehlerumschlag.

Der Datensatz enthaelt neben den gespeicherten Feldern:

- `received_at` – serverseitiger Annahmezeitpunkt des letzten gueltigen Reports
- `stale` – ob der letzte Report bereits veraltet wirkt (mehr als zwei Heartbeat-Intervalle ohne neuen Report)
- `derived_state` – zusammengefasste Diagnoseeinschaetzung (s.u.)

## DELETE /api/v1/screens/{screenId}/status

Loescht den gespeicherten Statuseintrag fuer einen Screen endgueltig.
Wenn kein Eintrag vorhanden ist, liefert das Backend `404`.

### Antwort

```json
{ "status": "deleted" }
```

## GET /status

Serverseitig gerenderte HTML-Diagnoseseite fuer den Browser.
Nutzt dieselbe Statusuebersicht wie der JSON-Endpunkt.

- automatisches Refresh alle 15 Sekunden
- Shortcut-Links fuer Connectivity- und Freshness-Filter
- kleines Filterformular fuer dieselben Parameter wie beim JSON-Read-Pfad
- direkte JSON-Detail-Links pro Screen

Bei ungueltigen Query-Parametern liefert `/status` eine HTML-Fehlerseite (kein JSON), damit Browser-Aufrufe direkt lesbare Hinweise erhalten.

## GET /status/{screenId}

HTML-Detailseite fuer einen einzelnen Screen.
Zeigt denselben Datensatz wie `GET /api/v1/screens/{screenId}/status` (Derived State, Player-Status, Connectivity, Frische, Timestamps, Endpoints).

Bei unbekanntem Screen: 404 mit HTML-Fehlermeldung und Rueck-Link auf `/status`.

## Serverseitig abgeleitete Felder

### stale

Die Schwelle wird aus dem gemeldeten `heartbeat_every_seconds` abgeleitet: mehr als zwei Intervalle ohne neuen Report gelten als veraltet.

`stale` ist aktuell eine Diagnosehilfe fuer die Entwicklungsstufe und noch kein vollstaendiges Online-/Offline-Modell fuer Admin-Oberflaechen.

### derived_state

- `offline` bei `stale = true` oder `server_connectivity = offline`
- `degraded` bei `server_connectivity = degraded|unknown` oder wenn `status` nicht `running` ist
- `online` in den verbleibenden Faellen

## Persistenz

Der Status-Store kann dateibasiert betrieben werden.

Konfiguration ueber die Umgebungsvariable:

- `MORZ_INFOBOARD_STATUS_STORE_PATH` – Pfad zur JSON-Datei; leer lassen fuer reinen In-Memory-Betrieb

Beim Datei-Store gilt:

- beim Start wird die Datei eingelesen, falls sie vorhanden ist (fehlende Datei ist kein Fehler)
- bei jedem `Save` und `Delete` wird der Inhalt atomar in die Datei geschrieben (write + rename)
- nach einem Neustart des Backends bleibt der letzte bekannte Statusbestand erhalten

## Agentseitige Connectivity-Ableitung

Agent-seitig wird die Server-Erreichbarkeit lokal als `unknown`, `online`, `degraded` oder `offline` aus dem Erfolg der HTTP-Reports abgeleitet.

Fuer den transportierten Wert im erfolgreichen HTTP-Report gilt:

- wenn ein Report vom Backend akzeptiert wurde, wird dieser Report selbst als `server_connectivity = online` gespeichert
- anhaltende Ausfaelle werden primaer ueber lokale Agent-Zustaende und serverseitige `stale`-Ableitung sichtbar

## Abgrenzung

Noch nicht Teil dieser Stufe:

- Screen-Authentifizierung
- MQTT-Heartbeat oder MQTT-Status
- Admin-UI-Anzeige des letzten Status
- Retry-Queue oder lokale Zwischenspeicherung im Agent

## Verwandte Endpoints

Siehe auch die vollständige API-Dokumentation in `API-ENDPOINTS.md`:

- Screen-Registrierung: `POST /api/v1/screens/register`
- Playlist-Abruf: `GET /api/v1/screens/{screenId}/playlist`
- Medien-Verwaltung: `GET /api/v1/tenants/{tenantSlug}/media`, `POST /api/v1/tenants/{tenantSlug}/media`

## Folgeschritte

Auf diesem Pfad bauen spaeter auf:

- Screen-Identitaet und Authentifizierung
- Trennung zwischen HTTP-Snapshot und MQTT-Heartbeat
- Admin-Vorschau fuer Online-/Offline- und Degraded-Zustaende
