# 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 - **Screenshot API**: On-Demand- und periodische Screenshots 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...", "playlist_id": "uuid...", "media_asset_id": null, "order_index": 0, "type": "web", "src": "http://example.com/page1", "title": "Startseite", "duration_seconds": 30, "enabled": true, "valid_from": null, "valid_until": null, "created_at": "2026-03-22T16:00:00Z" }, { "id": "uuid...", "playlist_id": "uuid...", "media_asset_id": "uuid...", "order_index": 1, "type": "image", "src": "/uploads/banner.jpg", "title": "Werbebanner", "duration_seconds": 20, "enabled": true, "valid_from": null, "valid_until": null, "created_at": "2026-03-22T16:00:00Z" } ] } ``` 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 / Root-Redirect auf `/login`. - Anfragen auf exakt `/` werden per `303 See Other` zu `/login` weitergeleitet. - Anfragen auf unbekannte Pfade (z. B. `/irgendwas`) geben `404 Not Found` zurück (404-Guard). **Status:** - `303 See Other` — Weiterleitung zu `/login` - `404 Not Found` — Pfad existiert nicht --- ### 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&csrf_token= ``` Das CSRF-Token muss als verstecktes Formularfeld `csrf_token` mitgesendet werden. Der Token wird beim `GET /login` als Cookie `morz_csrf` gesetzt und in den Template-Daten als `{{.CSRFToken}}` bereitgestellt. **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. **Request (Form-Encoded):** ``` csrf_token= ``` Das CSRF-Token muss als verstecktes Formularfeld `csrf_token` mitgesendet werden. Der aktuelle Token-Wert wird beim GET-Aufruf der aufrufenden Seite als `{{.CSRFToken}}` in die Template-Daten eingebettet und als Cookie `morz_csrf` gesetzt. **Verhalten:** - Session wird in der DB geloescht (`DeleteSession`) - Cookie wird mit `MaxAge=-1` geloescht - Weiterleitung zu `/login` **Status:** - `303 See Other` - `403 Forbidden` — CSRF-Token fehlt oder ungueltig --- ## 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. --- ## Screen-User Management (Admin) ### POST /admin/users Erstellt einen neuen Screen-User für einen Tenant (Admin-Formular). **Request-Body (Form-Encoded):** ``` username=screenuser1&password=geheim ``` **Verhalten:** - Neuer User mit `role = 'screen_user'` wird angelegt - Passwort wird per bcrypt gehasht - User wird dem aktuellen Tenant zugeordnet **Status:** - `200 OK` oder `201 Created` — Screen-User erstellt - `400 Bad Request` — Fehlende oder ungültige Parameter, Username bereits vorhanden - `500 Internal Server Error` — DB-Fehler Rückleitung zur Admin-Seite. --- ### POST /admin/users/{userID}/delete Löscht einen Screen-User und alle zugeordneten Screen-Permissions. **Verhalten:** - User mit Rolle `screen_user` wird gelöscht - Alle Einträge in `user_screen_permissions` für diesen User werden gelöscht **Status:** - `200 OK` — Screen-User gelöscht - `404 Not Found` — User nicht vorhanden oder falscher Typ - `500 Internal Server Error` — DB-Fehler Rückleitung zur Admin-Seite. --- ### POST /admin/screens/{screenID}/users Fügt einen Screen-User zu einem Screen hinzu. **Request-Body (Form-Encoded):** ``` user_id= ``` **Verhalten:** - Eintrag in `user_screen_permissions` wird erstellt - User muss vom Typ `screen_user` sein - Unique-Constraint verhindert Duplikate **Status:** - `200 OK` — User zu Screen hinzugefügt - `400 Bad Request` — Fehlende Parameter, User bereits hinzugefügt - `404 Not Found` — Screen oder User nicht vorhanden - `500 Internal Server Error` — DB-Fehler Rückleitung zur Admin-Seite oder zum Screen-Detail. --- ### POST /admin/screens/{screenID}/users/{userID}/remove Entfernt einen Screen-User von einem Screen. **Verhalten:** - Eintrag in `user_screen_permissions` wird gelöscht - User behält seine Existenz; nur die Permission wird entfernt **Status:** - `200 OK` — User von Screen entfernt - `404 Not Found` — Screen, User oder Permission nicht vorhanden - `500 Internal Server Error` — DB-Fehler Rückleitung zur Admin-Seite oder zum Screen-Detail. --- ## Playlist Management UI (Web-Formulare) ### GET /manage Übersichtsseite für eingeloggte Benutzer. **Auth:** `RequireAuth`. **Verhalten:** - Admins und Tenant-User werden direkt zu ihrer Standard-Ansicht weitergeleitet. - Screen-User mit genau einem zugeordneten Screen werden direkt zu `GET /manage/{screenSlug}` weitergeleitet. - Screen-User mit mehreren zugeordneten Screens erhalten eine Übersichtsseite mit Links zu den einzelnen Screens. **Response:** HTML-Seite oder Redirect (303 See Other). --- ### 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 --- ## Screenshot API ### POST /api/v1/player/screenshot Vom Player-Agent aufgerufener Endpoint zum Hochladen eines Screenshots. **Auth:** Keine. **Request:** `multipart/form-data`, max. 3 MB. | Feld | Typ | Pflicht | Beschreibung | |-------------|--------|---------|------------------------------------------------------| | `screen_id` | string | ja | Interne Screen-ID (entspricht dem Slug des Players) | | `screenshot`| Datei | ja | Screenshot-Datei (JPEG oder PNG) | Der MIME-Typ wird aus dem `Content-Type`-Header des Datei-Parts übernommen. Fehlt er, wird `image/png` angenommen. Der Screenshot wird im In-Memory-`ScreenshotStore` gespeichert (nicht persistiert, kein Filesystem-Zugriff). **Response:** - `200 OK` — Screenshot gespeichert (kein Body) - `400 Bad Request` — `screen_id` fehlt, `screenshot`-Feld fehlt, oder Multipart-Parsing fehlgeschlagen - `500 Internal Server Error` — Lesefehler --- ### GET /api/v1/screens/{screenId}/screenshot Ruft den zuletzt hochgeladenen Screenshot eines Screens ab. **Auth:** `RequireAuth` (eingeloggter Benutzer). **Path-Parameter:** - `screenId` — Screen-ID (wie beim Upload übergeben) **Response:** - `200 OK` — Raw-Image-Daten mit korrektem `Content-Type` (z. B. `image/jpeg`), `Cache-Control: no-store` - `404 Not Found` — kein Screenshot für diese Screen-ID vorhanden --- ## Änderungshistorie - **2026-03-24 (Update):** Screenshot-Endpoints implementiert und dokumentiert (Doris / Doku-Review) - `POST /api/v1/player/screenshot` — war als "In Vorbereitung" markiert, ist jetzt vollständig implementiert; Abschnitt komplett neu verfasst - `GET /api/v1/screens/{screenId}/screenshot` — neuer Endpoint, `authOnly`, liefert Raw-Image aus In-Memory-Store - `GET /manage` — neue Übersichtsseite für `screen_user` mit mehreren Screens, `authOnly` - **2026-03-24 (Update):** CSRF-Pflichtfelder in POST /login und POST /logout dokumentiert (Doris / Doku-Review) - `POST /login` und `POST /logout` erfordern `csrf_token` als Hidden-Field (Double-Submit-Cookie-Pattern) - Hinweis auf `morz_csrf`-Cookie und `{{.CSRFToken}}`-Template-Variable ergaenzt - **2026-03-24 (Update):** Root-Redirect dokumentiert (Doris / Doku-Review) - `GET /` — Redirect 303 auf `/login`, 404-Guard für unbekannte Pfade - **2026-03-23 (Update):** Screen-User Management Endpoints (Doris / Doku-Review) - `POST /admin/users` — Screen-User anlegen - `POST /admin/users/{userID}/delete` — Screen-User löschen - `POST /admin/screens/{screenID}/users` — User zu Screen hinzufügen - `POST /admin/screens/{screenID}/users/{userID}/remove` — User von Screen entfernen - **2026-03-23 (Update):** Security-Enhancements und Upload-Konsolidierung (Doris / Doku-Review) - CSRF-Schutz (Double-Submit-Cookie) in `internal/httpapi/csrf.go` - Rate-Limiting für `/login` in `internal/httpapi/ratelimit.go` - Upload-Logik konsolidiert in `internal/fileutil/fileutil.go` und `internal/httpapi/uploads.go` - Neue Env-Variable `MORZ_INFOBOARD_REGISTER_SECRET` dokumentiert - Screenshot-Modul im Agent vorbereitet mit `MORZ_INFOBOARD_SCREENSHOT_EVERY` - **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