# Info-Board Neu - API-Endpoints Vollständig ## Überblick Die Backend-API unterteilt sich in mehrere Bereiche: - **Health & Meta**: System-Status und API-Informationen - **Player Status**: Status-Ingest und Diagnose vom Player - **Screen Management**: CRUD und Registrierung von Screens - **Playlists**: Abruf und Verwaltung von Wiedergabelisten - **Media**: Upload und Verwaltung von Medien-Assets - **Message Wall**: Auflösung von Nachrichten-Wand-Anfragen - **Admin & UI**: Web-Formulare und Provisionierung - **Provisioning**: Erstinstallation neuer Screens (geplant) --- ## Health & Meta ### GET /healthz Health-Check für Monitoring. **Response:** ```json { "status": "ok", "service": "morz-infoboard-backend" } ``` ### GET /api/v1 API-Entrypoint mit Tools-Übersicht. **Response:** ```json { "name": "morz-infoboard-backend", "version": "dev", "tools": [ "message-wall-resolve", "screen-status-list", "screen-status-detail", "player-status-ingest", "screen-status-delete" ] } ``` ### GET /api/v1/meta Zusätzliche Metainformationen (noch nicht spezifiziert). --- ## Player Status (Diagnose) Siehe separate Dokumentation in `PLAYER-STATUS-HTTP.md`. Endpoints: - `POST /api/v1/player/status` — Status-Ingest vom Player-Agent - `GET /api/v1/screens/status` — Übersicht aller Screen-Status - `GET /api/v1/screens/{screenId}/status` — Einzelner Screen-Status - `DELETE /api/v1/screens/{screenId}/status` — Status löschen ### POST /api/v1/player/status Der Player-Agent sendet seinen aktuellen Status an den Server. **Request-Body:** ```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" } ``` **Response:** ```json { "status": "accepted" } ``` --- ## Screen Management (JSON API) ### POST /api/v1/screens/register Agent-Selbstregistrierung — wird vom Player-Agent beim Hochfahren aufgerufen. Der Agent upsert den Screen automatisch im Default-Tenant ("morz"), so dass alle deployt Screen automatisch im Admin-UI erscheinen. **Request-Body:** ```json { "slug": "info10", "name": "Info10 Bildschirm", "orientation": "landscape" } ``` **Response:** ```json { "id": "uuid...", "tenant_id": "uuid...", "slug": "info10", "name": "Info10 Bildschirm", "orientation": "landscape", "created_at": "2026-03-22T16:00:00Z", "updated_at": "2026-03-22T16:00:00Z" } ``` **Status:** - `200 OK` — Screen wurde erzeugt oder aktualisiert (upsert) - `400 Bad Request` — Slug fehlt oder ungültig - `500 Internal Server Error` — DB-Fehler oder Default-Tenant nicht vorhanden --- ### GET /api/v1/tenants/{tenantSlug}/screens Listet alle Screens eines Tenants auf. **Response:** ```json [ { "id": "uuid...", "tenant_id": "uuid...", "slug": "info10", "name": "Info10 Bildschirm", "orientation": "landscape", "created_at": "2026-03-22T16:00:00Z", "updated_at": "2026-03-22T16:00:00Z" }, ... ] ``` **Status:** - `200 OK` — Liste erfolgreich abrufen - `404 Not Found` — Tenant nicht vorhanden - `500 Internal Server Error` — DB-Fehler --- ### POST /api/v1/tenants/{tenantSlug}/screens Erstellt einen neuen Screen für einen Tenant (Admin-API). **Request-Body:** ```json { "slug": "new-screen", "name": "New Display", "orientation": "portrait" } ``` **Response:** ```json { "id": "uuid...", "tenant_id": "uuid...", "slug": "new-screen", "name": "New Display", "orientation": "portrait", "created_at": "2026-03-22T16:00:00Z", "updated_at": "2026-03-22T16:00:00Z" } ``` **Status:** - `201 Created` — Screen erstellt - `400 Bad Request` — Slug oder Name fehlt - `404 Not Found` — Tenant nicht vorhanden - `500 Internal Server Error` — DB-Fehler --- ## Playlist Management (JSON API) ### GET /api/v1/screens/{screenId}/playlist Abruf der aktiven Playlist für einen Screen (Player-Sync). Der Player ruft diesen Endpoint auf, um die aktuellen Inhalte zu laden. **Response:** ```json { "playlist_id": "uuid...", "default_duration_seconds": 20, "items": [ { "id": "uuid...", "type": "web", "src": "http://example.com/page1", "title": "Startseite", "duration_seconds": 30, "enabled": true, "valid_from": null, "valid_until": null }, { "id": "uuid...", "type": "image", "src": "/uploads/banner.jpg", "title": "Werbebanner", "duration_seconds": 20, "enabled": true, "valid_from": null, "valid_until": null } ] } ``` Wenn keine Playlist vorhanden ist, wird eine leere Liste zurückgegeben: ```json { "items": [] } ``` **Status:** - `200 OK` — Playlist abrufen - `404 Not Found` — Screen nicht vorhanden --- ### GET /api/v1/playlists/{screenId} Abrufen einer kompletten Playlist mit Metadaten. **Response:** ```json { "playlist": { "id": "uuid...", "screen_id": "uuid...", "tenant_id": "uuid...", "name": "Hauptplaylist", "is_active": true, "default_duration_seconds": 20, "fallback_enabled": true, "fallback_dir": "/fallback", "shuffle_enabled": false, "created_at": "2026-03-22T16:00:00Z", "updated_at": "2026-03-22T16:00:00Z" }, "items": [ { "id": "uuid...", "playlist_id": "uuid...", "type": "web", "src": "http://example.com", "title": "Example", "duration_seconds": 20, "enabled": true, "created_at": "2026-03-22T16:00:00Z" } ] } ``` **Status:** - `200 OK` — Erfolgreich abrufen - `500 Internal Server Error` — DB-Fehler --- ### POST /api/v1/playlists/{playlistId}/items Fügt ein Item zu einer Playlist hinzu. **Request-Body (Optionen A: Aus Media-Library):** ```json { "media_asset_id": "uuid...", "title": "Optional überschriebener Titel" } ``` **Request-Body (Optionen B: Direkte URL):** ```json { "type": "web", "src": "http://example.com/page", "title": "Example Page", "duration_seconds": 30, "valid_from": "2026-03-22T09:00:00Z", "valid_until": "2026-03-22T17:00:00Z" } ``` **Response:** ```json { "id": "uuid...", "playlist_id": "uuid...", "type": "web", "src": "http://example.com/page", "title": "Example Page", "duration_seconds": 30, "enabled": true, "valid_from": "2026-03-22T09:00:00Z", "valid_until": "2026-03-22T17:00:00Z", "created_at": "2026-03-22T16:00:00Z" } ``` **Status:** - `201 Created` — Item erstellt - `400 Bad Request` — Type oder Src fehlt; Media-Asset nicht vorhanden - `500 Internal Server Error` — DB-Fehler --- ### PATCH /api/v1/items/{itemId} Aktualisiert ein Playlist-Item (Titel, Dauer, Zeitfenster, aktiviert). **Request-Body:** ```json { "title": "Neuer Titel", "duration_seconds": 25, "enabled": true, "valid_from": "2026-03-22T09:00:00Z", "valid_until": "2026-03-22T17:00:00Z" } ``` **Status:** - `204 No Content` — Erfolgreich aktualisiert - `400 Bad Request` — Ungültige Dauer - `500 Internal Server Error` — DB-Fehler --- ### DELETE /api/v1/items/{itemId} Löscht ein Playlist-Item. **Status:** - `204 No Content` — Erfolgreich gelöscht - `500 Internal Server Error` — DB-Fehler --- ### PUT /api/v1/playlists/{playlistId}/order Reordnet die Items einer Playlist anhand einer geordneten Liste von Item-IDs. **Request-Body:** ```json [ "item-id-1", "item-id-2", "item-id-3" ] ``` **Status:** - `204 No Content` — Erfolgreich reordert - `400 Bad Request` — JSON-Array erwartet - `500 Internal Server Error` — DB-Fehler --- ### PATCH /api/v1/playlists/{playlistId}/duration Setzt die Standard-Dauer für neue Items einer Playlist. **Request-Body (Form-Encoded):** ``` default_duration_seconds=25 ``` **Status:** - `204 No Content` — Erfolgreich aktualisiert - `400 Bad Request` — Ungültige oder fehlende Dauer - `500 Internal Server Error` — DB-Fehler --- ## Media Management (JSON API) ### GET /api/v1/tenants/{tenantSlug}/media Listet alle Medien-Assets eines Tenants auf. **Response:** ```json [ { "id": "uuid...", "tenant_id": "uuid...", "title": "Banner Image", "description": null, "type": "image", "source_kind": "upload", "storage_path": "/uploads/1234567890_banner.jpg", "original_url": null, "mime_type": "image/jpeg", "size_bytes": 102400, "enabled": true, "created_at": "2026-03-22T16:00:00Z", "updated_at": "2026-03-22T16:00:00Z" }, { "id": "uuid...", "tenant_id": "uuid...", "title": "External Website", "type": "web", "source_kind": "remote_url", "original_url": "http://example.com", "storage_path": null, "enabled": true, "created_at": "2026-03-22T16:00:00Z" } ] ``` Wenn keine Assets vorhanden sind, wird eine leere Liste zurückgegeben. **Status:** - `200 OK` — Liste erfolgreich abrufen - `404 Not Found` — Tenant nicht vorhanden - `500 Internal Server Error` — DB-Fehler --- ### POST /api/v1/tenants/{tenantSlug}/media Registriert ein neues Medien-Asset (Datei-Upload oder externe URL). **Request-Typ A: Datei-Upload (Multipart)** ``` Content-Type: multipart/form-data type: image (oder video, pdf) title: Mein Bild file: ``` **Request-Typ B: Externe URL (Multipart)** ``` Content-Type: multipart/form-data type: web title: Externe Website url: http://example.com ``` **Response:** ```json { "id": "uuid...", "tenant_id": "uuid...", "title": "Mein Bild", "type": "image", "source_kind": "upload", "storage_path": "/uploads/1234567890_mein_bild.jpg", "mime_type": "image/jpeg", "size_bytes": 102400, "enabled": true, "created_at": "2026-03-22T16:00:00Z" } ``` **Status:** - `201 Created` — Asset erstellt - `400 Bad Request` — Ungültiger Type, fehlende Datei/URL, oder Request zu groß (>512 MB) - `404 Not Found` — Tenant nicht vorhanden - `500 Internal Server Error` — Datei-/DB-Fehler --- ### DELETE /api/v1/media/{id} Löscht ein Medien-Asset (und physische Datei falls lokal gespeichert). **Status:** - `204 No Content` — Erfolgreich gelöscht - `404 Not Found` — Asset nicht vorhanden - `500 Internal Server Error` — DB-Fehler --- ## Message Wall ### POST /api/v1/tools/message-wall/resolve Spezialendpoint zur Auflösung von Nachrichten-Wand-Anfragen (noch in Entwicklung). --- ## Authentifizierung (Web-Formulare) Alle Auth-Routen erfordern keine vorherige Authentifizierung. ### GET /login Zeigt das Login-Formular. - Wenn ein gueltiges `morz_session`-Cookie vorhanden ist, wird direkt zum jeweiligen Dashboard weitergeleitet (`/admin` fuer Admins, `/tenant/{slug}/dashboard` fuer Tenant-User). **Response:** HTML-Seite mit Benutzername/Passwort-Formular und optionaler Flash-Message. --- ### POST /login Verarbeitet die Login-Eingabe. **Request (Form-Encoded):** ``` username=admin&password=geheim ``` **Verhalten:** - Passwort wird per `bcrypt.CompareHashAndPassword` geprueft - Bei Erfolg wird ein `morz_session`-Cookie gesetzt (HttpOnly, Secure, 24h TTL) - Weiterleitung je nach Rolle: `admin` → `/admin`, `tenant` → `/tenant/{slug}/dashboard` - Bei Fehler: Rueckkehr zur Login-Seite mit Flash-Message **Status:** - `303 See Other` — Erfolg, Weiterleitung - `303 See Other` — Fehler, Rueckkehr zur Login-Seite mit `?msg=` --- ### POST /logout Meldet den aktuellen Benutzer ab. **Verhalten:** - Session wird in der DB geloescht (`DeleteSession`) - Cookie wird mit `MaxAge=-1` geloescht - Weiterleitung zu `/login` **Status:** - `303 See Other` --- ## Tenant Self-Service Dashboard (Web-Formulare) Alle Tenant-Routen erfordern `RequireAuth` + `RequireTenantAccess`. Admins koennen auf jeden Tenant zugreifen; Tenant-User nur auf ihren eigenen. ### GET /tenant/{tenantSlug}/dashboard Zeigt das Tenant-Self-Service-Dashboard. **Tabs:** - Tab A "Meine Monitore" — Screen-Karten mit Live-Status (via JS-Fetch aus `/api/v1/screens/status`) - Tab B "Mediathek" — Upload-Formular und Dateiliste **Query-Parameter:** - `tab=media` — oeffnet direkt Tab B (z. B. nach Upload-Redirect) - `flash=uploaded` / `flash=deleted` — zeigt Erfolgs-Flash-Message **Response:** HTML-Seite. --- ### POST /tenant/{tenantSlug}/upload Laedt ein Medium fuer den Tenant hoch. **Request (Multipart Form):** ``` type: image (oder video, pdf) title: Mein Bild file: ``` oder fuer eine Web-URL: ``` type: web title: Externe Website url: http://example.com ``` **Verhalten:** - Datei wird in `MORZ_INFOBOARD_UPLOAD_DIR` gespeichert - MIME-Typ wird aus dem Upload-Header abgeleitet - Max. Upload-Groesse: 512 MB **Status:** - `303 See Other` → `/tenant/{slug}/dashboard?tab=media&flash=uploaded` - `400 Bad Request` — fehlender Typ oder Datei - `404 Not Found` — Tenant nicht vorhanden --- ### POST /tenant/{tenantSlug}/media/{mediaId}/delete Loescht ein Medien-Asset des Tenants. **Verhalten:** - Eigentuemer-Pruefung: `asset.TenantID` muss mit dem Tenant uebereinstimmen - Physische Datei wird geloescht sofern vorhanden **Status:** - `303 See Other` → `/tenant/{slug}/dashboard?tab=media&flash=deleted` - `403 Forbidden` — Asset gehoert nicht diesem Tenant - `404 Not Found` — Tenant oder Asset nicht vorhanden --- ## Admin UI (Web-Formulare) ### GET /admin Administrations-Dashboard mit Übersicht aller Screens und Tenants. Rückgabe: HTML-Seite mit: - Liste aller Screens - Status-Information - Provisioning-Formulare - Screen-Verwaltung --- ### POST /admin/screens/provision Startet einen Provisionierungs-Job für einen neuen oder bestehenden Screen. **Request-Body (Form-Encoded oder JSON):** ```json { "screen_id": "uuid...", "target_ip": "192.168.1.100", "target_port": 22, "remote_user": "root", "auth_mode": "password", "provided_secret_ref": "secret-key-123" } ``` **Status:** - `200 OK` — Job erfolgreich erstellt - `400 Bad Request` — Fehlende oder ungültige Parameter - `404 Not Found` — Screen nicht vorhanden - `500 Internal Server Error` — DB-Fehler **Hinweis:** Der eigentliche Provisionierungs-Job läuft asynchron über einen Worker ab. --- ### POST /admin/screens Erstellt einen neuen Screen über das Admin-Formular. **Request-Body (Form-Encoded):** ``` slug=new-screen&name=Neuer+Bildschirm&orientation=landscape ``` **Status:** - `200 OK` oder `201 Created` — Screen erstellt - `400 Bad Request` — Fehlende Parameter - `500 Internal Server Error` — DB-Fehler Rückleitung zur Admin-Seite. --- ### POST /admin/screens/{screenId}/delete Löscht einen Screen. **Status:** - `200 OK` — Screen gelöscht - `404 Not Found` — Screen nicht vorhanden - `500 Internal Server Error` — DB-Fehler Rückleitung zur Admin-Seite. --- ## Playlist Management UI (Web-Formulare) ### GET /manage/{screenSlug} Verwaltungs-UI für die Playlist eines Screens. Rückgabe: HTML-Seite mit: - Liste der Medien-Assets - Playlist-Editor - Upload-Formular - Item-Verwaltung --- ### POST /manage/{screenSlug}/upload Datei-Upload über das Manage-Formular. **Request (Multipart):** ``` type: image (oder video, pdf) title: Neues Bild file: ``` **Status:** - `201 Created` — Asset erfolgreich hochgeladen - `404 Not Found` — Screen nicht vorhanden - `500 Internal Server Error` — Fehler Rückleitung zum Manage-Formular. --- ### POST /manage/{screenSlug}/items Fügt ein Item zur Playlist hinzu (Formular). **Request (Form-Encoded):** ``` media_asset_id=uuid...&duration_seconds=25 oder type=web&src=http://example.com&duration_seconds=30 ``` **Status:** - `201 Created` — Item erstellt - `404 Not Found` — Screen nicht vorhanden - `500 Internal Server Error` — DB-Fehler Rückleitung zum Manage-Formular. --- ### POST /manage/{screenSlug}/items/{itemId} Aktualisiert ein Item (Formular). **Request (Form-Encoded):** ``` title=Neuer+Titel&duration_seconds=25&enabled=on ``` **Status:** - `200 OK` oder `204 No Content` — Erfolgreich aktualisiert - `404 Not Found` — Item nicht vorhanden - `500 Internal Server Error` — DB-Fehler Rückleitung zum Manage-Formular. --- ### POST /manage/{screenSlug}/items/{itemId}/delete Löscht ein Item (Formular). **Status:** - `204 No Content` — Erfolgreich gelöscht - `404 Not Found` — Item nicht vorhanden - `500 Internal Server Error` — DB-Fehler Rückleitung zum Manage-Formular. --- ### POST /manage/{screenSlug}/reorder Reordert Items (Formular). **Request (Form-Encoded mit Array-Syntax):** ``` items=item-id-1&items=item-id-2&items=item-id-3 ``` **Status:** - `204 No Content` — Erfolgreich reordert - `500 Internal Server Error` — DB-Fehler Rückleitung zum Manage-Formular. --- ### POST /manage/{screenSlug}/media/{mediaId}/delete Löscht ein Medien-Asset (Formular). **Status:** - `204 No Content` — Erfolgreich gelöscht - `404 Not Found` — Asset nicht vorhanden - `500 Internal Server Error` — DB-Fehler Rückleitung zum Manage-Formular. --- ## Datei-Serving ### GET /uploads/{filename} Stellt hochgeladene Medien-Dateien bereit. **Query-Parameter:** - Keine **Status:** - `200 OK` — Datei gefunden - `404 Not Found` — Datei nicht vorhanden --- ## Diagnostic Pages (HTML) ### GET /status HTML-Diagnoseseite für den Browser mit Übersicht aller Screen-Status. - Automatisches Refresh alle 15 Sekunden - Shortcut-Links für Filter - Filterformular (wie JSON-Read-Pfad) - Direkte JSON-Detail-Links pro Screen **Query-Parameter:** Dieselben wie `GET /api/v1/screens/status` - `q` — Screen-ID Substring-Suche - `derived_state` — `online`, `degraded`, `offline` - `server_connectivity` — `online`, `degraded`, `offline`, `unknown` - `stale` — `true`, `false` - `updated_since` — RFC3339-Zeitstempel - `limit` — positive Ganzzahl --- ### GET /status/{screenId} HTML-Detailseite für einen einzelnen Screen. Zeigt: - Screen-Information - Derived State - Player Status - Connectivity - Freshness und Timestamps - Endpunkte und Links --- ## Player Local UI (Agent) ### GET /player Zeigt die lokale Player-UI (HTML) auf dem Gerät. Rückgabe: HTML-Seite mit: - Splash-Screen - Systeminformationen-Overlay - Verbindungsstatus-Punkt - Basis für Playlist-Anzeige --- ### GET /api/now-playing JSON-API des Player-Agents (lokal). **Response:** ```json { "playlist": [ { "src": "http://backend.local/api/v1/screens/info10/playlist", "type": "web", "title": "Startseite", "duration_seconds": 30 } ], "status": "running", "connectivity": "online" } ``` --- ### GET /api/sysinfo Systeminformationen des Player-Agents (lokal). **Response:** ```json { "items": [ { "label": "Hostname", "value": "infoboard-01" }, { "label": "Uptime", "value": "5d 2h 30m" } ] } ``` --- ## Fehlerbehandlung Alle JSON-Responses folgen diesem Fehlermodell (falls implementiert): ```json { "error": { "code": "error_code_here", "message": "Human-readable error message", "details": null } } ``` Typische HTTP-Status: - `200 OK` — Erfolgreiche Anfrage (Read) - `201 Created` — Ressource erstellt - `204 No Content` — Erfolgreiche Anfrage ohne Response-Body - `400 Bad Request` — Eingabe-Validierung fehlgeschlagen - `404 Not Found` — Ressource nicht vorhanden - `500 Internal Server Error` — Server-Fehler --- ## Änderungshistorie - **2026-03-23 (Update):** Auth- und Tenant-Dashboard-Endpoints ergaenzt (Doris / Doku-Review) - `GET /login`, `POST /login`, `POST /logout` dokumentiert - `GET /tenant/{tenantSlug}/dashboard` dokumentiert - `POST /tenant/{tenantSlug}/upload` dokumentiert - `POST /tenant/{tenantSlug}/media/{mediaId}/delete` dokumentiert - **2026-03-23:** Initiale Dokumentation aller HTTP-Endpoints basierend auf Code-Review - Alle Screen-Management-Endpoints dokumentiert - Alle Playlist-Management-Endpoints dokumentiert - Alle Media-Management-Endpoints dokumentiert - Admin-UI und Manage-UI-Endpoints dokumentiert - Player-Status-Diagnostik dokumentiert - Player-Local-API dokumentiert