- SCHEMA.md: screen_id in user_screen_permissions von uuid auf text korrigiert - SCHEMA.md: Phasen-Hinweis zur screens-Tabelle hinzugefügt (6 Spalten aktuell implementiert, erweiterte Phase 2-3) - SERVER-KONZEPT.md: RequireScreenAccess-Middleware dokumentiert inkl. Route-Gruppen und Verhaltens-Details - server/backend/README.md: Env-Variable DATABASE_URL → MORZ_INFOBOARD_DATABASE_URL korrigiert - DEVELOPMENT.md: Compose-Stack von "später" auf "existiert bereits" aktualisiert - API-ENDPOINTS.md: HandlePlayerPlaylist Response um fehlende Felder ergänzt (playlist_id, media_asset_id, order_index, created_at) - DEVELOPMENT.md: Architekturentscheidungen präzisiert (message_wall=implementiert, Kampagnen=geplant) Co-Authored-By: Klaus <noreply@example.com>
11 KiB
Info-Board Neu - Server-Konzept
Ziel
Der Server ist die zentrale Steuer- und Verwaltungsinstanz der Plattform.
Er soll:
- Benutzer, Firmen und Screens verwalten
- Medien und Playlists bereitstellen
- globale Templates und Kampagnen steuern
- Provisionierung neuer Screens ausloesen
- Status, Screenshots und Heartbeats sammeln
- MQTT und HTTPS sauber trennen
Grundprinzip
Der Server ist die Quelle der fachlichen Wahrheit.
Der Player bleibt trotzdem lauffaehig, wenn der Server temporaer nicht erreichbar ist.
Das bedeutet:
- Server verwaltet Konfiguration und Inhalte zentral
- Player fuehrt lokal und robust aus
- Server sendet Steuerimpulse, Player synchronisiert aktiv nach
Hauptkomponenten
Reverse Proxy
Aufgaben:
- TLS-Terminierung
- Routing fuer API und Web-UIs
- optionale Auth-/Header-Regeln
Backend-API
Bevorzugte Sprache:
Go
Aufgaben:
- Authentifizierung und Autorisierung
- CRUD fuer Tenants, Users, Screens, Medien, Playlists
- Verwaltung globaler Templates und Kampagnen
- Player-Sync-Endpunkte
- Speicherung von Status und Screenshots
- Start von Provisionierungsjobs
Admin-UI
Aufgaben:
- Gesamtübersicht aller Screens
- Vorschau und Status
- Template-/Kampagnenverwaltung
- Kommandos und Provisionierung
Tenant-UI
Aufgaben:
- Uploads und Medienverwaltung pro Firma
- Pflege der monitorbezogenen Playlist
- Vorschau des eigenen Screens
Datenbank
Empfehlung:
- PostgreSQL
Aufgaben:
- Speicherung fachlicher Daten
- Status, Jobs, Revisionen, Zuordnungen
MQTT-Broker
Empfehlung:
- Mosquitto
Aufgaben:
- Heartbeats
- Statusmeldungen
- Events
- Kommandos und ACKs
Dateispeicher
Aufgaben:
- Uploads
- Screenshots
- ggf. serverseitig vorbereitete Medien
V1:
- Dateisystem ausreichend
Fachliche Bereiche
1. Mandanten und Benutzer
Der Server trennt:
- globale Admins
- tenantgebundene Nutzer
Regel:
- Firmen sehen nur ihren Bereich
- Admins sehen alles
2. Screen-Verwaltung
Der Server kennt jeden Screen mit:
- ID
- Name
- Klasse
- Orientierung
- Rotation
- Tenant-Zuordnung
- technischem Registrierungsstatus
3. Medienverwaltung
Der Server verwaltet:
- Uploads
- externe Medienreferenzen
- Metadaten
- tenant- oder screenspezifische Zuordnung
4. Playlist-Verwaltung
Der Server verwaltet tenantbezogene Inhalte pro Screen.
Wichtige Felder:
- Reihenfolge
- Dauer
valid_fromvalid_until- Fehlerstrategie
- Cache-Politik
5. Template- und Kampagnenverwaltung
Der Server stellt den globalen Orchestrierungsbereich bereit.
Funktionen:
- Templates erstellen
- Szenen pflegen
- Zielgruppen waehlen
- Kampagnen aktivieren/deaktivieren
- Zeitfenster setzen
- Uebersteuerung sichtbar machen
6. Provisionierung
Der Server startet Provisionierungsjobs fuer neue Screens.
Aufgaben:
- Eingaben aus Admin-UI entgegennehmen
- Job anlegen
- Secret-Handling absichern
- Worker oder Jobrunner starten
- Fortschritt speichern
- Fehler sauber rueckmelden
API-Bereiche
Die API soll mindestens diese Domänen haben:
authtenantsusersscreensmediaplayliststemplatescampaignsplayercommandsprovisioning
Revisionsmodell
Der Server arbeitet mit Revisionen, damit Player effizient synchronisieren koennen.
Mindestens:
config_revisionplaylist_revisionmedia_revisioncampaign_revision
Status- und Vorschaukonzept
Der Server speichert:
- letzten bekannten Heartbeat
- letzten Status
- letzten Screenshot
- aktuelle Inhaltsquelle pro Screen
Die Admin-UI soll damit erkennen:
- online/offline
- normaler tenantbezogener Betrieb
- globale Kampagnen-Uebersteuerung
- Fallback-Betrieb
- Fehlerzustand
Provisionierungsjobrunner
Die Provisionierung soll nicht direkt in Web-Requests laufen.
Stattdessen:
- API legt Job an
- dedizierter Worker/Jobrunnner arbeitet ihn ab
- Fortschritt wird in DB gespeichert
Zusaetzlich fuer v1 festzulegen:
- ACK-Timeout-Handling fuer
device_commandsueber Worker - Secret-Handling fuer Provisionierungs-Bootstrap ueber kurzlebige Secret-Referenzen
- physische Netzposition des Workers fuer SSH-Erreichbarkeit als Betriebsparameter
Docker-Compose-Zielbild
Sinnvolle Komponenten in compose/:
reverse-proxybackendpostgresmosquitto- optional
worker
Authentifizierung
Der Server verwendet einen Session-basierten Login-Flow mit bcrypt-Passwort-Hashing.
Login-Flow
GET /loginrendert das Login-Formular (Bulma-Card zentriert).POST /loginprueft Username und Passwort:AuthStore.GetUserByUsernamelaedt den User inkl. Tenant-Slug.bcrypt.CompareHashAndPasswordprueft das Passwort (Cost-Faktor 12).- Bei Erfolg legt
AuthStore.CreateSessioneine Session an (TTL 24 Stunden). - Das Session-Token wird als
morz_session-Cookie gesetzt (HttpOnly=true,Secure=true). - Im
DevMode(MORZ_INFOBOARD_DEV_MODE=true) wirdSecure=falsegesetzt fuer lokalen HTTP-Betrieb. - Weiterleitung je nach Rolle:
admin→/admin,tenant→/tenant/{slug}/dashboard.
POST /logoutloescht die Session in der DB und entfernt den Cookie.
Cookie-Lebensdauer
- Standard-TTL: 24 Stunden
- Der Cookie verfaellt automatisch; die DB wird stuendlich durch
CleanExpiredSessionsbereinigt.
Admin-User-Bootstrap
Beim Server-Start wird EnsureAdminUser aufgerufen, wenn MORZ_INFOBOARD_ADMIN_PASSWORD gesetzt ist.
Der Admin-User wird dem Tenant mit Slug MORZ_INFOBOARD_DEFAULT_TENANT (Standard: morz) zugeordnet.
Ist der User bereits vorhanden, passiert nichts. Fehler sind nicht fatal — der Server startet trotzdem.
Middleware-Kette
Alle geschuetzten Routen werden durch Middleware-Funktionen in internal/httpapi/middleware.go abgesichert.
Eingehende Anfrage
│
▼
RequireAuth Liest morz_session-Cookie, prueft Session via DB,
speichert *store.User im Request-Context.
→ Fehler: Redirect zu /login?next=<Pfad>
│
├─► RequireAdmin Prueft user.Role == "admin"
│ → Fehler: 403 Forbidden
│
├─► RequireTenant Prueft user.TenantSlug == {tenantSlug} aus dem URL-Pfad.
│ Access Admins duerfen immer durch.
│ → Fehler: 403 Forbidden
│
└─► RequireScreen Enforces per-screen access control.
Access Admins duerfen auf alle Screens zugreifen.
Screen-User brauchen expliziten Eintrag in `user_screen_permissions`.
Tenant-User duerfen auf alle Screens ihres Tenants zugreifen.
→ Fehler: 404 Not Found (Screen) oder 403 Forbidden (kein Zugriff)
Route-Gruppen im Router
| Gruppe | Middleware | Beispielrouten |
|---|---|---|
| Oeffentlich | keine | /healthz, /login, /api/v1/screens/register |
| Auth-only | RequireAuth | /api/v1/items/{itemId}, /api/v1/media/{id} |
| Admin-only | RequireAuth + RequireAdmin | /admin, /admin/screens/... |
| Tenant-scoped | RequireAuth + RequireTenantAccess | /tenant/{tenantSlug}/..., /api/v1/tenants/{tenantSlug}/... |
| Screen-scoped | RequireAuth + RequireScreenAccess | /manage/{screenSlug}/..., /api/v1/screens/{screenId}/playlist |
Der Hilfsfunktion chain(middlewares...) in router.go wrappet Handler von aussen nach innen.
RequireScreenAccess Middleware
Die Middleware RequireScreenAccess erzwingt Zugriffskontrolle auf Screen-Ressourcen und wird ausschliesslich fuer Routen verwendet, deren Handler screen-spezifische Operationen durchfuehren.
Verhalten:
- Admin-User: duerfen auf alle Screens zugreifen (Bypass).
- Screen-User: duerfen nur auf Screens zugreifen, fuer die sie einen expliziten Eintrag in
user_screen_permissionshaben. - Tenant-User: duerfen auf alle Screens ihres Tenants zugreifen (noch nicht vollstaendig implementiert).
Implementierung:
Die Middleware extrahiert den screenSlug aus dem URL-Pfad-Parameter, schlaegt den Screen in der Datenbank auf und prueft via ScreenStore.HasUserScreenAccess(), ob der Nutzer Zugriff hat. Admins umgehen diese Pruefung.
Fehlerbehandlung:
- Screen nicht gefunden:
404 Not Found - Kein Zugriff auf Screen:
403 Forbidden
Verwendung in Router:
Die authScreen-Middleware-Kombination wird fuer folgende Routes verwendet:
GET /manage/{screenSlug}— Playlist-Management-UIPOST /manage/{screenSlug}/upload— Medium hochladenPOST /manage/{screenSlug}/items— Item hinzufuegenPOST /manage/{screenSlug}/items/{itemId}— Item aktualisierenPOST /manage/{screenSlug}/items/{itemId}/delete— Item loeschenPOST /manage/{screenSlug}/reorder— Items reordnenPOST /manage/{screenSlug}/media/{mediaId}/delete— Medium loeschen
Tenant-Dashboard
Das Tenant-Self-Service-Dashboard ist unter /tenant/{tenantSlug}/dashboard erreichbar.
URL-Schema
| Methode | Pfad | Beschreibung |
|---|---|---|
| GET | /tenant/{tenantSlug}/dashboard |
Dashboard rendern |
| POST | /tenant/{tenantSlug}/upload |
Medium hochladen |
| POST | /tenant/{tenantSlug}/media/{mediaId}/delete |
Medium loeschen |
Tabs
- Tab A – Meine Monitore: Zeigt Screen-Karten mit Live-Status. Der Status wird per JavaScript
aus
GET /api/v1/screens/statusgeladen und alle 30 Sekunden aktualisiert. Status-Badge:is-success(online),is-danger(offline),is-warning(unbekannt). - Tab B – Mediathek: Upload-Formular (Bild, Video, PDF oder Web-URL) und Dateiliste
mit Loeschen-Button. Nach Upload oder Loeschen Redirect mit
?tab=media&flash=uploaded/deleted.
Eigentuemer-Pruefung beim Loeschen
HandleTenantDeleteMedia prueft, dass asset.TenantID == tenant.ID, bevor es loescht.
Damit ist sichergestellt, dass ein Tenant keine Assets anderer Tenants loeschen kann,
selbst wenn er die mediaId erraten wuerde.
Sicherheitsgrundsaetze
- Root-Bootstrap-Geheimnisse nur kurzlebig oder referenziert speichern
- API- und MQTT-Zugaenge getrennt behandeln
- alle Admin-Kommandos auditieren
- Tenant-Trennung strikt serverseitig erzwingen
API-Fehlermodell
Vor Implementierungsbeginn gilt ein einheitlicher Fehlerumschlag.
Empfehlung:
{
"error": {
"code": "screen_not_found",
"message": "Screen existiert nicht",
"details": null
}
}
Zielstruktur im Repo
Empfohlene Unterstruktur fuer den Server:
server/backend/server/admin-ui/server/tenant-ui/server/worker/compose/
Testfaelle fuer den Server
- Tenant sieht nur eigene Daten
- Admin sieht alle Daten
- Kampagne ueberschreibt tenantbezogenen Content korrekt
- Screen-Provisionierung legt Job sauber an
- Player-Sync ueber Revisionen funktioniert
- MQTT-Kommandos werden protokolliert
- Screenshot-Upload erscheint im Admin-Dashboard