### Security-Fixes (K1–K6, W1–W4, W7, N1, N5–N6, V1, V5–V7)
- K1: CSRF-Schutz via Double-Submit-Cookie (httpapi/csrf.go + csrf_helpers.go)
- K2: requireScreenAccess() in allen manage-Handlern (Tenant-Isolation)
- K3: Tenant-Check bei DELETE /api/v1/media/{id}
- K4: requirePlaylistAccess() + GetByItemID() für JSON-API Playlist-Routen
- K5: Admin-Passwort nur noch als [gesetzt] geloggt
- K6: POST /api/v1/screens/register mit Pre-Shared-Secret (MORZ_INFOBOARD_REGISTER_SECRET)
- W1: Race Condition bei order_index behoben (atomare Subquery in AddItem)
- W2: Graceful Shutdown mit 15s Timeout auf SIGTERM/SIGINT
- W3: http.MaxBytesReader (512 MB) in allen Upload-Handlern
- W4: err.Error() nicht mehr an den Client
- W7: Template-Execution via bytes.Buffer (kein partial write bei Fehler)
- N1: Rate-Limiting auf /login (5 Versuche/Minute pro IP, httpapi/ratelimit.go)
- N5: Directory-Listing auf /uploads/ deaktiviert (neuteredFileSystem)
- N6: Uploads nach Tenant getrennt (uploads/{tenantSlug}/)
- V1: Upload-Logik konsolidiert in internal/fileutil/fileutil.go
- V5: Cookie-Name als Konstante reqcontext.SessionCookieName
- V6: Strukturiertes Logging mit log/slog + JSON-Handler
- V7: DB-Pool wird im Graceful-Shutdown geschlossen
### Phase 6: Screenshot-Erzeugung
- player/agent/internal/screenshot/screenshot.go erstellt
- Integration in app.go mit MORZ_INFOBOARD_SCREENSHOT_EVERY Config
### UX: PDF.js Integration
- pdf.min.js + pdf.worker.min.js als lokale Assets eingebettet
- Automatisches Seitendurchblättern im Player
### Ansible: Neue Rollen
- signage_base, signage_server, signage_provision erstellt
- inventory.yml und site.yml erweitert
### Konzept-Docs
- GRUPPEN-KONZEPT.md, KAMPAGNEN-AKTIVIERUNG.md, MONITORING-KONZEPT.md
- PROVISION-KONZEPT.md, TEMPLATE-EDITOR.md, WATCHDOG-KONZEPT.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
12 KiB
12 KiB
Info-Board Neu - Umsetzungs-Todo
Phase 0 - Projektbasis
- Projektverzeichnisstruktur unter
/srv/docker/info-board-neufestlegen - Namenskonventionen fuer Server, Player, Rollen und Pakete definieren
- Dokumentationsstruktur fuer Architektur, Betrieb und Deployment anlegen
- Entscheidung fuer Server-Tech-Stack dokumentieren
- Entscheidung fuer Player-Implementierung dokumentieren
- Sprachentscheidung dokumentieren:
Goals bevorzugte Sprache fuer Agent und moeglichst viele Backend-Komponenten
Phase 1 - Fachliches Fundament
- Rollenmodell fuer
adminund monitorgebundene Nutzer final festschreiben - Datenmodell fuer
tenant,screen,user,media_asset,playlist,playlist_item,screen_status,screen_snapshotdefinieren - Playlist-Semantik mit
duration,valid_from,valid_until,load_timeout,cache_policy,on_errorspezifizieren - Fallback-Regel fuer ungeplante oder leere Inhalte verbindlich definieren
- Statusmodell fuer Online/Offline/Degraded/Error definieren
- Kommandokatalog fuer Admin-Aktionen finalisieren
- Template- und Kampagnenmodell fuer globale monitoruebergreifende Uebersteuerung finalisieren
- Prioritaetsregel
campaign > tenant_playlist > fallbackverbindlich festschreiben - Entscheidung dokumentieren, dass
playlist_items.screen_identfernt wird - Entscheidung dokumentieren, dass Gruppen bei Kampagnen serverseitig in Einzel-Assignments expandiert werden
Phase 2 - Technische Zielarchitektur
- Server-Komponentenliste finalisieren
- API-Schnittstellen grob definieren
- MQTT-Topic-Struktur finalisieren
- HTTPS- und MQTT-Aufgabentrennung dokumentieren
- Screenshot-/Vorschaustrategie spezifizieren
- Offline- und Cache-Strategie bis auf Dateiebene festlegen
- Sicherheitsmodell fuer Uploads, Login und Rechte pruefen
- API fuer Templates, Kampagnen, Aktivierung und Deaktivierung ausarbeiten
- Provisionierungs-Workflow fuer neue Screens technisch durchplanen
- Secret-Handling fuer initiale Root-Passwoerter oder Bootstrap-Zugaenge definieren
- API-Fehlermodell und gemeinsame Fehlerantworten festlegen
- ACK-Timeout-Strategie fuer
device_commandfestlegen message_wall-Rendering serverseitig verbindlich entscheiden- Netzwerktopologie fuer SSH-basierte Erstprovisionierung als Worker-/Jumphost-faehiges Modell festlegen
Phase 3 - Player-Design
- Minimalen Paketbedarf fuer den Player auf Raspberry Pi OS Debian 13 ermitteln
- X11-Minimalkonzept fuer Chromium-Kiosk dokumentieren
- Startmechanismus fuer Chromium ohne Desktop-Umgebung definieren
- Verzeichnislayout auf dem Player festlegen
player-agentfachlich zuschneidenplayer-uifachlich zuschneiden (lokale Kiosk-Seite mit Splash + Sysinfo-Overlay)- Watchdog-Konzept fuer Browser und Agent definieren
- Offline-Overlay-Verhalten spezifizieren
- Fehlerbehandlung fuer Web-Inhalte und Timeouts ausarbeiten
- Display-Steuerung fuer An/Aus, Rotation und Neustart planen
- Sysinfo-Overlay erweitern: load, freier RAM, IP-Adresse(n) anzeigen
Phase 4 - Server-Design
- API-Backend fachlich schneiden
- Admin-Oberflaeche in Hauptbereiche aufteilen
- Firmen-/Monitor-Oberflaeche in Hauptbereiche aufteilen
- Firmen-/Tenant-Oberfläche → siehe docs/TENANT-FEATURE-PLAN.md
- Storage-Konzept fuer Uploads, Cache-Dateien und Screenshots festlegen
- Authentifizierungskonzept festlegen
- Mandantentrennung im Datenmodell und in den APIs absichern
- Logging- und Monitoring-Konzept definieren
- Template-Editor fuer globale Kampagnen fachlich schneiden
- Aktivierungsoberflaeche fuer saisonale oder temporäre Kampagnen planen
- Gruppierung oder Slot-Modell fuer monitoruebergreifende Layouts planen
- Provisionierungs-UI fuer neue Screens fachlich und technisch schneiden
- Jobrunner-Konzept fuer Ansible-gestuetzte Erstinstallation planen
Phase 5 - Prototyping
- Minimalen Server-Prototyp bauen
- Minimalen Player-Agent-Prototyp bauen
- Minimale Player-UI bauen
- Lokale Test-Playlist mit Bild, Video, PDF und Webseite anlegen
- BUG: Datei
120papag.mpgist alstype: imagegespeichert, musstype: videosein – Player-UI versucht<img>-Laden, was fehlschlägt - Fallback-Verzeichnisbetrieb demonstrieren
valid_from/valid_untilim Prototyp pruefen- Offline-Sync mit lokalem Cache pruefen
- MQTT-Topic
signage/screen/{screenSlug}/playlist-changedspezifiziert und dokumentiert - MQTT-Kommandos
reload,restart_player,reboot,display_on,display_offtestweise durchspielen - globale Kampagne testen, die tenantbezogenen Content temporär ueberschreibt
- Rueckfall auf Normalbetrieb nach manueller Deaktivierung pruefen
Phase 6 - Betriebsfaehigkeit
- Docker-Compose-Setup fuer den Server anlegen
- systemd-Units fuer den Player erstellen
- Chromium-Kiosk-Startskript erstellen
- Screenshot-Erzeugung auf dem Player integrieren
- Heartbeat- und Statusmeldungen integrieren
- MQTT-Playlist-Change-Synchronisation mit Backend-Debounce (2s) und Agent-Debounce (3s) implementiert
- Fehler- und Wiederanlaufverhalten verifizieren
Phase 7 - Ansible-Automatisierung
- Rolle
signage_baseerstellen - Rolle
signage_playererstellen - Rolle
signage_displayerstellen - Rolle
signage_servererstellen - Rolle
signage_provisionerstellen - Inventar-/Variablenmodell fuer mehrere Monitore entwerfen
- Screen-spezifische Variablen wie
screen_id, Rotation und Aufloesung abbilden - Erstinstallation eines neuen Players automatisieren
- Update-Rollout eines bestehenden Players automatisieren
- Bootstrap ueber Root-Passwort auf SSH-Key und dauerhafte Verwaltung umstellen
Phase 8 - Pilotbetrieb
- Einen Referenzmonitor fuer den Pilotbetrieb auswaehlen
- Pilotmonitor neu aufsetzen
- Verbindung zum Zentralserver herstellen
- Upload- und Playlist-Pflege mit einem Testmandanten pruefen
- Admin-Funktionen am Pilotmonitor pruefen
- globale Template-Aktivierung fuer einen oder mehrere Monitore im Pilot testen
- Offline-Betrieb real testen
- Browser-/Renderer-Stabilitaet ueber laengeren Zeitraum beobachten
- komplette Neu-Provisionierung eines frischen Test-Screens aus dem Admin-Backend pruefen
Phase 9 - Migration der Bestandsmonitore
- Migrationsreihenfolge fuer
info01bisinfo09festlegen - Rueckfallstrategie pro Monitor definieren
- Altinhalte in das neue Medienmodell ueberfuehren
- Playlists uebernehmen oder neu modellieren
- Monitore nacheinander umstellen
- Nach jeder Umstellung Stabilitaet und Fernsteuerung pruefen
Phase 10 - Nacharbeiten
- Betriebsdokumentation schreiben
- Admin-Handbuch schreiben
- Kurzhandbuch fuer Firmen-Nutzer schreiben
- Backup- und Restore-Konzept dokumentieren
- Update- und Release-Prozess festlegen
- Langfristige Wayland-Neubewertung fuer spaetere Version vormerken
UX-Verbesserungen (Gestaltungsplan)
Hohe Prioritaet
- Flash-Messages nach Aktionen in Manage-UI (Upload, Loeschen, Speichern) — Feedback fuer den Nutzer
- Screen-Online/Offline-Status in Admin-Tabelle anzeigen (aus /status-Endpoint befuellen)
- Playlist-Tabelle in overflow-x Wrapper einwickeln (Responsive auf kleinen Screens)
- PDF-Darstellung: Sidebar und Toolbar im Chromium PDF-Viewer ausblenden (URL-Parameter navpanes=0, toolbar=0)
- PDF-Darstellung: PDF.js fuer automatisches Seitendurchblaettern integrieren
Mittlere Prioritaet
- Loesch-Bestaetigung: Bulma-Modal statt browser-nativer confirm()-Dialog
- Status-Page: Sprache von Englisch auf Deutsch vereinheitlichen
- Status-Page: Relative Zeitstempel statt RFC3339 ("vor 2 Minuten")
- Querlinks zwischen Admin-UI und Status-Page (Navigation)
- Bulma und SortableJS als lokale Assets einbetten statt CDN
- Player-UI: CSS-Transitions fuer sanfte Content-Wechsel (Fade statt abrupt)
- Player-UI: Erweitertes Sysinfo-Overlay (aktueller Titel, Playlist-Laenge)
- Aria-Labels fuer Loesch-Buttons und Drag-Handles (Accessibility)
Niedrige Prioritaet
- Upload-Fortschrittsbalken in Manage-UI
- vars.yml Download-Button in Provision-UI statt Copy-Paste
- Toggle-Switch statt Ja/Nein-Select fuer Enabled-Feld
UX-Bug-Fixes
- Fix: Protokoll-relative URLs (//cdn...) werden nicht mehr durch URL-Normalisierung kaputtgeschrieben
- Fix: PDF-Fragment-Parameter werden mit bestehenden Fragments gemerged statt blind angehängt
- Fix: /api/startup-token setzt Cache-Control: no-store Header (Server + Client)
- Fix: TestAssetsServed Nil-Dereferenz durch tote Goroutine behoben
Security & Code-Review (Opus, 2026-03-23)
Kritisch — Sicherheitslücken
- K2 Tenant-Isolation für
/manage/{screenSlug}/*:requireScreenAccess()in allen manage-Handlern - K3
DELETE /api/v1/media/{id}: Tenant-Check via reqcontext.UserFromContext - K4 JSON-API Playlist-Routen (
/items,/playlists/*/items,/order,/duration):requirePlaylistAccess()+GetByItemID()im Store - K1 CSRF-Schutz: Double-Submit-Cookie-Pattern (
httpapi/csrf.go); JS-Injection in alle Templates; Middleware in Router - K6
POST /api/v1/screens/register: Pre-Shared-Secret viaMORZ_INFOBOARD_REGISTER_SECRET(HeaderX-Register-Secret); Player-Agent sendet Secret mit - K5 Admin-Passwort aus Log entfernt — nur
[gesetzt]wird geloggt
Wichtig — Robustheit
- N5 Directory-Listing auf
/uploads/deaktiviert vianeuteredFileSystem(httpapi/uploads.go) - N6 Uploads nach Tenant getrennt:
fileutil.SaveUploadedFile()legt Dateien inuploads/{tenantSlug}/ab - W1 Race Condition bei
order_indexbehoben: atomare Subquery inAddItem() - W2 Graceful Shutdown implementiert:
http.Server.Shutdown()mit 15s Timeout auf SIGTERM/SIGINT - W3 Upload mit
http.MaxBytesReaderbegrenzt (512 MB) in allen drei Upload-Handlern - W4
err.Error()nicht mehr an den Client — generische Fehlermeldungen, Details serverseitig - W7 Template-Execution-Errors:
bytes.Buffer-Rendering, erst bei Erfolg an Client senden (renderTemplate())
Verbesserung — Wartbarkeit
- V3 Keine Tests für Auth, Middleware, Tenant-Handler (gesamter Phase-1-5-Code ohne Abdeckung)
- V1 Upload-Logik konsolidiert in
internal/fileutil/fileutil.go(SaveUploadedFile) - V5 Cookie-Name als Konstante
reqcontext.SessionCookieName— manage/auth.go und middleware.go nutzen sie - V6 Strukturiertes Logging:
log/slogmit JSON-Handler inmain.go;app.gonutztslog.Info/slog.Error - V7 DB-Pool wird im Graceful-Shutdown-Handler geschlossen (
a.dbPool.Close())
Nice-to-have — Features
- N1 Rate-Limiting auf
/login: In-Memory Sliding-Window (5 Versuche/Minute pro IP) viahttpapi/ratelimit.go - N2 Passwort-Änderung / Self-Service-Reset
- N3 Tenant-User-Management im Admin-UI
- N4 Session-TTL via Config-Variable steuerbar (aktuell hardcoded 8h)
Hinweis K6: MORZ_INFOBOARD_REGISTER_SECRET muss in server/.env / docker-compose.yml und in der Player-Config (MORZ_INFOBOARD_REGISTER_SECRET oder register_secret in config.json) identisch gesetzt werden. Wenn die Variable leer ist, bleibt der Endpoint offen (Rückwärtskompatibilität).
Querschnittsthemen
- Datensicherung fuer Datenbank und Medien einplanen
- TLS-/Reverse-Proxy-Konzept festlegen
- Ressourcenverbrauch auf Raspberry Pi beobachten
- Verhalten bei grossen Videos optimieren
- Verhalten bei defekten externen Webseiten absichern
- Datenschutz- und Rechtefragen fuer Screenshots/Vorschauen pruefen
Erste konkrete Abarbeitungsreihenfolge
- 1. Projektstruktur im neuen Verzeichnis vervollstaendigen
- 2. Datenmodell in eigener Datei ausformulieren
- 3. API- und MQTT-Vertrag definieren
- 4. Player-Minimalkonzept fuer Raspberry Pi OS Debian 13 festzurren
- 5. Server-Compose-Grundgeruest erstellen
- 6. Player-Prototyp mit lokalem Browser-Renderer bauen
- 7. Offline-Cache und Fallback robust machen
- 8. UIs fuer Admin und Firmen schrittweise aufbauen
- 9. Ansible-Rollen erstellen
- 10. Pilotmonitor migrieren