- SCHEMA.md: Index idx_media_assets_created_by_user_id hinzugefügt - API-ENDPOINTS.md: GET/POST Media-Endpoints erweitert um owner_is_restricted und owner_username - API-ENDPOINTS.md: DELETE Media-Endpoint mit Berechtigungslogik dokumentiert * admin_user: immer erlaubt * screen_user: erlaubt wenn asset.tenant_id == user.tenant_id * restricted: erlaubt nur wenn asset.created_by_user_id == user.id Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1361 lines
34 KiB
Markdown
1361 lines
34 KiB
Markdown
# 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" }
|
|
```
|
|
|
|
Wenn auf dem Server ein MQTT-Broker konfiguriert ist (`MORZ_INFOBOARD_MQTT_BROKER`), enthält die Response zusätzlich ein `mqtt`-Objekt mit den Verbindungsdaten. Der Agent soll diese Konfiguration übernehmen und seine MQTT-Verbindung bei Bedarf neu aufbauen.
|
|
|
|
```json
|
|
{
|
|
"status": "accepted",
|
|
"mqtt": {
|
|
"broker": "tcp://mqtt.example.com:1883",
|
|
"username": "agent",
|
|
"password": "secret"
|
|
}
|
|
}
|
|
```
|
|
|
|
- `mqtt` — nur vorhanden, wenn ein Broker konfiguriert ist (omitempty); fehlt das Feld, bleibt die bestehende MQTT-Konfiguration des Agents unverändert
|
|
- `mqtt.broker` — MQTT-Broker-URL (immer gesetzt, wenn `mqtt` vorhanden)
|
|
- `mqtt.username` — Benutzername (nur wenn konfiguriert, omitempty)
|
|
- `mqtt.password` — Passwort (nur wenn konfiguriert, omitempty)
|
|
|
|
---
|
|
|
|
## 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.
|
|
|
|
**Rolle `restricted`:** Gibt nur Medien zurück, die der eingeloggte User selbst hochgeladen hat (`created_by_user_id = user.id`).
|
|
|
|
**Alle anderen Rollen (`admin_user`, `screen_user`):** Alle Medien des Tenants. Response-Felder `owner_is_restricted` und `owner_username` sind befüllt, wenn das Medium von einem Restricted-User hochgeladen wurde.
|
|
|
|
**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",
|
|
"owner_is_restricted": false,
|
|
"owner_username": "admin"
|
|
},
|
|
{
|
|
"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",
|
|
"owner_is_restricted": true,
|
|
"owner_username": "teacher01"
|
|
}
|
|
]
|
|
```
|
|
|
|
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). Setzt `created_by_user_id` auf die ID des eingeloggten Users.
|
|
|
|
**Request-Typ A: Datei-Upload (Multipart)**
|
|
```
|
|
Content-Type: multipart/form-data
|
|
|
|
type: image (oder video, pdf)
|
|
title: Mein Bild
|
|
file: <binary data>
|
|
```
|
|
|
|
**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",
|
|
"owner_is_restricted": false,
|
|
"owner_username": "teacher01"
|
|
}
|
|
```
|
|
|
|
**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).
|
|
|
|
**Berechtigungen:**
|
|
- **admin_user:** Immer erlaubt
|
|
- **screen_user:** Erlaubt, wenn `asset.tenant_id == user.tenant_id`
|
|
- **restricted:** Erlaubt nur wenn `asset.created_by_user_id == user.id` (eigenes Medium)
|
|
|
|
**Status:**
|
|
- `204 No Content` — Erfolgreich gelöscht
|
|
- `403 Forbidden` — User hat nicht die erforderliche Berechtigung
|
|
- `404 Not Found` — Asset nicht vorhanden
|
|
- `500 Internal Server Error` — DB-Fehler
|
|
|
|
---
|
|
|
|
## Display-Steuerung (JSON API)
|
|
|
|
Beide Endpunkte erfordern `RequireAuth` + `RequireScreenAccess` (`authScreen`-Middleware).
|
|
|
|
### POST /api/v1/screens/{screenSlug}/display
|
|
|
|
Sendet einen MQTT-Befehl zum Ein- oder Ausschalten des physischen Displays.
|
|
|
|
**Auth:** Erforderlich (Bearer-Token oder Session-Cookie). Screen-Zugriff erforderlich.
|
|
**Rollen:** Nur `admin_user` und `screen_user`. Benutzer mit Rolle `restricted` erhalten `403 Forbidden`.
|
|
|
|
**Path-Parameter:**
|
|
- `screenSlug` — Slug des Screens
|
|
|
|
**Request-Body:**
|
|
```json
|
|
{"state": "on"}
|
|
```
|
|
oder
|
|
```json
|
|
{"state": "off"}
|
|
```
|
|
|
|
**Response:** `204 No Content`
|
|
|
|
**Fehler:**
|
|
- `400 Bad Request` — `state` ist nicht `"on"` oder `"off"`, oder ungültiges JSON
|
|
- `403 Forbidden` — Benutzer hat nicht die erforderliche Rolle (restricted-Benutzer dürfen Display nicht steuern)
|
|
- `502 Bad Gateway` — MQTT-Publish fehlgeschlagen
|
|
|
|
---
|
|
|
|
### POST /api/v1/screens/{screenSlug}/schedule
|
|
|
|
Speichert den Zeitplan für das automatische Ein-/Ausschalten eines Displays.
|
|
|
|
**Auth:** Erforderlich. Screen-Zugriff erforderlich.
|
|
**Rollen:** Nur `admin_user` und `screen_user`. Benutzer mit Rolle `restricted` erhalten `403 Forbidden`.
|
|
|
|
**Path-Parameter:**
|
|
- `screenSlug` — Slug des Screens
|
|
|
|
**Request-Body:**
|
|
```json
|
|
{
|
|
"schedule_enabled": true,
|
|
"power_on_time": "06:00",
|
|
"power_off_time": "22:00"
|
|
}
|
|
```
|
|
|
|
Zeitangaben müssen im Format `HH:MM` (24h) oder als leerer String `""` übergeben werden.
|
|
Der Scheduler prüft jede Minute, ob die aktuelle Uhrzeit mit `power_on_time` oder `power_off_time`
|
|
übereinstimmt, und sendet dann den entsprechenden MQTT-Befehl.
|
|
|
|
**Response:** `204 No Content`
|
|
|
|
**Fehler:**
|
|
- `400 Bad Request` — Zeitformat ungültig (nicht `HH:MM`), oder ungültiges JSON
|
|
- `403 Forbidden` — Benutzer hat nicht die erforderliche Rolle (restricted-Benutzer dürfen Zeitplan nicht ändern)
|
|
- `404 Not Found` — Screen nicht vorhanden
|
|
- `500 Internal Server Error` — DB-Fehler
|
|
|
|
---
|
|
|
|
### Globaler Override
|
|
|
|
| Methode | Pfad | Auth | Beschreibung |
|
|
|---------|------|------|--------------|
|
|
| GET | `/api/v1/global-override` | authUser | Aktiven Override abrufen (204 wenn keiner aktiv) |
|
|
| POST | `/api/v1/global-override` | authUser | Override setzen + sofort MQTT an alle Screens |
|
|
| DELETE | `/api/v1/global-override` | authUser | Override aufheben |
|
|
|
|
**GET /api/v1/global-override**
|
|
|
|
Ruft den aktuell aktiven globalen Override ab.
|
|
|
|
**Response:** `200 OK` (wenn aktiv)
|
|
```json
|
|
{"type":"off","until":"2026-04-05T18:00:00+02:00","set_at":"2026-03-27T15:30:00+02:00"}
|
|
```
|
|
|
|
**Response:** `204 No Content` (wenn kein Override aktiv)
|
|
|
|
---
|
|
|
|
**POST /api/v1/global-override**
|
|
|
|
Setzt einen globalen Override und sendet sofort MQTT-Befehle an alle Screens.
|
|
|
|
**Rollen:** Nur `admin_user` und `screen_user`. Benutzer mit Rolle `restricted` erhalten `403 Forbidden`.
|
|
|
|
**Request-Body:**
|
|
```json
|
|
{"type":"off","until":"2026-04-05T18:00:00+02:00"}
|
|
```
|
|
|
|
**Response:** `200 OK`
|
|
```json
|
|
{"type":"off","until":"2026-04-05T18:00:00+02:00","set_at":"2026-03-27T15:30:00+02:00"}
|
|
```
|
|
|
|
**Fehler:**
|
|
- `400 Bad Request` — `type` nicht "on"/"off", oder ungültiges Zeitformat
|
|
- `403 Forbidden` — Benutzer hat nicht die erforderliche Rolle (restricted-Benutzer dürfen Override nicht setzen)
|
|
- `500 Internal Server Error` — DB-Fehler
|
|
|
|
---
|
|
|
|
**DELETE /api/v1/global-override**
|
|
|
|
Hebt den aktuellen globalen Override auf.
|
|
|
|
**Rollen:** Nur `admin_user` und `screen_user`. Benutzer mit Rolle `restricted` erhalten `403 Forbidden`.
|
|
|
|
**Response:** `204 No Content`
|
|
|
|
**Fehler:**
|
|
- `403 Forbidden` — Benutzer hat nicht die erforderliche Rolle (restricted-Benutzer dürfen Override nicht löschen)
|
|
- `500 Internal Server Error` — DB-Fehler
|
|
|
|
---
|
|
|
|
### Per-Screen Override
|
|
|
|
| Methode | Pfad | Auth | Beschreibung |
|
|
|---------|------|------|--------------|
|
|
| POST | `/api/v1/screens/{screenSlug}/override` | authScreen | Per-Screen "Einschalten bis" setzen oder löschen |
|
|
|
|
**POST /api/v1/screens/{screenSlug}/override**
|
|
|
|
Setzt oder löscht den per-Screen "Einschalten bis"-Override. Mit diesem Override bleibt ein Monitor bis zu
|
|
dem angegebenen Zeitpunkt eingeschaltet, selbst wenn der globale Schedule "aus" vorsieht.
|
|
|
|
**Auth:** Erforderlich. Screen-Zugriff erforderlich.
|
|
**Rollen:** Nur `admin_user` und `screen_user`. Benutzer mit Rolle `restricted` erhalten `403 Forbidden`.
|
|
|
|
**Path-Parameter:**
|
|
- `screenSlug` — Slug des Screens
|
|
|
|
**Request-Body:**
|
|
```json
|
|
{"on_until":"2026-04-05T18:00:00+02:00"}
|
|
```
|
|
|
|
Um den Override zu löschen, `on_until` auf `null` setzen:
|
|
```json
|
|
{"on_until":null}
|
|
```
|
|
|
|
**Response:** `204 No Content`
|
|
|
|
**Fehler:**
|
|
- `400 Bad Request` — Ungültiges Zeitformat oder ungültiges JSON
|
|
- `403 Forbidden` — Benutzer hat nicht die erforderliche Rolle (restricted-Benutzer dürfen Override nicht setzen)
|
|
- `404 Not Found` — Screen 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.
|
|
|
|
### Benutzerrollen
|
|
|
|
Das System unterscheidet folgende Rollen:
|
|
|
|
- `admin_user` — Volller Zugriff auf alle Funktionen, inkl. Benutzerverwaltung und Display-Steuerung
|
|
- `screen_user` — Darf Medien hochladen, Playlists bearbeiten und Displays steuern (An/Aus, Zeitplan, Override)
|
|
- `restricted` — Darf Medien hochladen und Playlist bearbeiten. Keine Display-Steuerung (An/Aus, Zeitplan, Override). Betroffene Endpunkte antworten mit 403 Forbidden.
|
|
- `tenant_user` — Tenant-Operator für Self-Service-Dashboard (Medienupload, Tenantmenü)
|
|
|
|
---
|
|
|
|
### 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=<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=<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: <binary data>
|
|
```
|
|
|
|
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=<userID>
|
|
```
|
|
|
|
**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: <binary data>
|
|
```
|
|
|
|
**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):** MQTT-Konfiguration in POST /api/v1/player/status Response dokumentiert (Doris / Doku-Review)
|
|
- Response enthält jetzt optionales `mqtt`-Objekt mit `broker`, `username`, `password` (alle omitempty wenn leer)
|
|
- Feld wird nur gesendet wenn `MORZ_INFOBOARD_MQTT_BROKER` konfiguriert ist
|
|
- Agent übernimmt die Konfiguration und reconnectet MQTT bei Änderung
|
|
- **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
|