- GET /manage: neue Übersichtsseite mit Bulma-Karten für screen_user mit ≥2 Screens
- handleScreenUserRedirect leitet bei ≥2 Screens auf /manage statt auf ersten Screen
- On-Demand-Screenshot-Flow via MQTT:
- Backend publiziert signage/screen/{slug}/screenshot-request beim Seitenaufruf
- Player-Agent empfängt Topic, ruft TakeAndSendOnce() auf
- Player POST /api/v1/player/screenshot → Backend speichert in ScreenshotStore (RAM)
- GET /api/v1/screens/{screenId}/screenshot liefert gespeichertes Bild (authOnly)
- ScreenshotStore: In-Memory, thread-safe, kein Persistenz-Overhead
- JS-Retry nach 4s in Templates (Screenshot braucht 1-3s für MQTT-Roundtrip)
- manageTmpl zeigt Screenshot-Thumbnail beim Einzelscreen-Aufruf
- Doku: neue Endpoints, MQTT-Topics, Screenshot-Flow in SERVER-KONZEPT.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- HandleManageUI übergibt CSRFToken korrekt ans Template (leeres Hidden-Field
blockierte JS-Inject-Snippet)
- HandleTenantDashboard setzt CSRF-Cookie und befüllt CSRFToken in Template-Daten
- tenant/csrf_helpers.go: setCSRFCookie im tenant-Package (Import-Cycle-Isolation)
- Logout-Formular in tenantDashTmpl hat jetzt statisches CSRF-Hidden-Field
- Doku: POST /logout und POST /login mit CSRF-Anforderungen dokumentiert
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
printf "%q" im Go-Template erzeugte Go-quoted Strings ("..."), die als
Teil der screen_id an die DB übergeben wurden. FK-Constraint schlug fehl,
weil die ID mit eingebetteten Quotes keiner screens-Zeile entsprach.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fehlende slog.Error-Aufrufe in HandleAddUserToScreen, HandleCreateScreenUser,
HandleDeleteScreenUser und HandleRemoveUserFromScreen ergänzt — DB-Fehler
wurden bisher komplett geschluckt und waren nicht diagnostizierbar.
Tenant-Lookup in EnsureAdminUser und CreateScreenUser aus SQL-Subqueries
in eigene Queries extrahiert für bessere Fehlermeldungen bei fehlendem Tenant.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
HandleLoginPost renderte Fehlerseiten (falsches Passwort, leere Felder) ohne
CSRFToken in den Template-Daten. Das hidden field <csrf_token> war leer, sodass
jeder weitere Submit-Versuch mit "Ungültiger CSRF-Token" scheiterte.
Fix: setCSRFCookie am Anfang des Handlers aufrufen und das Token in allen
renderError-Pfaden an das Template übergeben.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Neue Rolle screen_user: User können sich einloggen und nur ihre
zugeordneten Bildschirme verwalten. Admins behalten vollen Zugriff.
- Migration 003: users.role-Spalte + user_screen_permissions (M:N)
- Store: CreateScreenUser, ListScreenUsers, DeleteUser,
GetAccessibleScreens, HasUserScreenAccess,
AddUserToScreen, RemoveUserFromScreen, GetScreenUsers
- Middleware: RequireScreenAccess enforces screen-level access
für alle /manage/{screenSlug}-Routen
- 4 neue Admin-Handler: CreateScreenUser, DeleteScreenUser,
AddUserToScreen, RemoveUserFromScreen (+4 Routes)
- Admin-UI: Tab "Benutzer" (anlegen/löschen) + Screen-User-Modal
(User zuordnen/entfernen) direkt in der Bildschirm-Tabelle
- Login: screen_user wird nach Login zum ersten zugänglichen Screen
weitergeleitet; kein Zugang zu /admin
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Admin users continue to redirect to /manage/ as before. Tenant users
now land on their own dashboard at /tenant/{slug}/dashboard instead of
the incorrect /manage/{slug} path. The fix applies to both the
already-logged-in check in HandleLoginUI and the post-login switch in
HandleLoginPost.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Backend published auf signage/screen/{slug}/playlist-changed nach
Playlist-Mutationen (2s Debounce). Agent subscribed und fetcht
Playlist sofort (3s Debounce). 60s-Polling bleibt als Fallback.
Neue Packages: mqttnotifier (Backend), mqttsubscriber (Agent)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Upload-Fortschrittsbalken per XHR mit Progress-Event
- Checkbox-Toggle statt Ja/Nein-Select für Enabled-Feld
- vars.yml Download-Button im Provisioning-Workflow
- Alle UX-Aufgaben in TODO.md abgehakt
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Flash-Messages nach allen Manage-Aktionen (Upload, Löschen, Speichern, Hinzufügen)
- Screen-Online/Offline-Status als farbiger Punkt in Admin-Tabelle
- overflow-x Wrapper für alle Tabellen (Admin, Playlist, Medienbibliothek)
- Navbar-Burger für mobile Viewports in Admin und Manage
- UX-Gestaltungsplan als Sektion in TODO.md eingetragen
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Player-UI: Content-Type-Handling (image/video/web statt alles-iframe),
Fast-Retry-Polling beim Start, Splash wird korrekt ausgeblendet,
Fallback-Anzeige bei X-Frame-Options-Blockade
- Dev-Display: Backend-URL auf 192.168.64.1 für Multipass-Netz korrigiert
- Media-Upload: Typ wird aus MIME-Type abgeleitet statt blind aus Formular
- TODO: Daten-Bug dokumentiert
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- POST /admin/screens/provision: legt Screen in DB an (Upsert) und zeigt
eine 5-Schritt-Seite mit kopierbaren Code-Blöcken:
1. inventory.yml Eintrag
2. host_vars/{slug}/vars.yml Inhalt
3. ssh-copy-id Befehl
4. ansible-playbook Befehl (mit Vault-Passwort-Hinweis)
5. Link zur Playlist-Verwaltung
- Admin-Formular: IP-Adresse + SSH-User Felder ergänzt
- Altes "nur anlegen"-Formular als aufklappbaren Details-Block versteckt
- Clipboard-Copy-Buttons für jeden Code-Block
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- DELETE /api/v1/screens/{screenId}/status loescht einzelne Screen-Eintraege
- /api/v1/meta listet jetzt 5 Tools inkl. screen-status-delete und diagnostic_ui-Pfade
- filePlayerStatusStore persistiert den Status-Store atomar in einer JSON-Datei
- MORZ_INFOBOARD_STATUS_STORE_PATH aktiviert die Datei-Persistenz (leer = In-Memory)
- Integration-Test deckt den vollstaendigen Lifecycle: POST -> list -> HTML -> JSON -> DELETE -> 404 ab
- DEVELOPMENT.md beschreibt End-to-End-Entwicklungstest und neue Env-Variable
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GET /api/v1/screens/status und GET /status akzeptieren jetzt
derived_state=online|degraded|offline zum direkten Filtern nach der
serverseitig abgeleiteten Diagnoseeinschaetzung. Erlaubte Werte sind
online, degraded und offline; unknown ist explizit nicht erlaubt, da
derived_state immer auf einen der drei Werte abgebildet wird.
Abgrenzung zu server_connectivity: derived_state filtert nach dem
zusammengefassten Zustand (stale + connectivity + status), waehrend
server_connectivity nur den gemeldeten Connectivity-Wert betrifft.
Beide Filter koennen kombiniert werden.
Tests: FiltersByDerivedState, RejectsInvalidDerivedState
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GET /api/v1/screens/status und GET /status akzeptieren jetzt q=<substring>
zum Filtern der Ergebnisliste nach ScreenID. Der Vergleich ist case-
insensitiv. Leerer Wert bedeutet kein Filter; jeder andere String ist gueltig
(keine Validierung noetig). Die Summary-Counts bleiben unveraendert und
beschreiben weiterhin den gesamten Store-Bestand.
Die Quick-Filter auf /status behalten den aktuellen q-Wert beim Klick, damit
der Textfilter nicht verloren geht wenn man z.B. von "All screens" auf
"Stale reports" wechselt.
Tests: FiltersByScreenIDSubstring, ScreenIDFilterIsCaseInsensitive
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Auto-Refresh auf GET /status/{screenId}:
screenDetailPageData bekommt RefreshSeconds (wie statusPageData). Das
Detail-Template rendert das Meta-Tag analog zur Uebersichtsseite mit
denselben 15 Sekunden, damit ein Screen seinen Zustand (z.B. fresh ->
stale, connectivity-Wechsel) auch ohne manuellen Reload sichtbar macht.
Test: meta-refresh-Tag jetzt in TestRouterScreenDetailPageRoute geprueft.
DRY-Refactor: Fehlermeldungen vereinheitlicht:
overviewQueryErrorMessage und overviewQueryErrorCode sind jetzt in
playerstatus.go definiert -- dort wo auch die Validierungslogik lebt.
writeOverviewQueryError delegiert vollstaendig an beide Helper statt
die Meldungen selbst zu duplizieren. Die vorherige Kopie in statuspage.go
mit abweichenden Satzendezeichen und Grossschreibung wurde entfernt.
Beide Fehlerpfade (JSON und HTML) nutzen jetzt exakt dieselben
Meldungstexte.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Drei eng zusammenhaengende Aenderungen in einem Commit, da sie dieselbe
Template-Infrastruktur teilen und gemeinsam die HTML-Diagnoseansicht
abrunden:
1. CSS-Extraktion (Verbesserung, kein Verhalten geaendert)
Das bisherige Inline-CSS wurde in die Konstante statusPageCSS
ausgelagert. statusPageCSSBlock injiziert es per String-Konkatenation
in alle Templates, sodass kein Template-Inheritance-Mechanismus
benoetigt wird und jede Seite eigenstaendig ausfuehrbar bleibt.
2. GET /status/{screenId} -- neue HTML-Detailseite
Zeigt den letzten bekannten Datensatz eines einzelnen Screens:
Derived State, Player-Status, Connectivity und Frische als
Summary-Cards; Timing- und Endpoints-Details in aufgeraeuemten
Key-Value-Tabellen. Verlinkung zurueck auf /status und auf den
bestehenden JSON-Endpunkt. Bei unbekanntem Screen: 404 mit HTML-
Fehlerseite und Rueck-Link.
Route: GET /status/{screenId}
3. HTML-Fehlerseite fuer /status bei ungueltigen Query-Parametern
Bisher lieferte handleStatusPage einen rohen JSON-Fehler, wenn
z.B. ?stale=banana uebergeben wurde -- inkongruent fuer einen
HTML-Endpunkt. writeStatusPageQueryError rendert jetzt dasselbe
statusPageErrorTemplate wie der Not-Found-Fall der Detailseite
und gibt text/html mit 400 zurueck.
Neue Tests (router_test.go):
- ScreenDetailPageRoute: prueft Inhalt, Links, Content-Type
- ScreenDetailPageNotFound: prueft 404 + HTML + Rueck-Link
- StatusPageRejectsInvalidQueryParams: prueft jetzt auch text/html
fuer alle Fehlerfaelle
Docs (PLAYER-STATUS-HTTP.md):
- Query-Parameter-Validierung mit erlaubten Werten und Fehlercodes
- Neue /status/{screenId}-Seite und HTML-Fehlerseiten dokumentiert
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bisher wurden ungueltige Werte fuer server_connectivity und stale im
Listing-Endpunkt und auf der Statusseite stillschweigend ignoriert bzw.
fuehrten zu leeren Ergebnissen ohne Fehlermeldung. Beide Parameter werden
jetzt explizit auf erlaubte Werte geprueft und liefern bei ungueltiger
Eingabe einen 400-Fehler mit beschreibendem error_code – konsistent mit
der bestehenden Validierung fuer updated_since und limit.
Neue Tests (playerstatus_test.go):
- RejectsInvalidServerConnectivity
- RejectsInvalidStale
- RejectsInvalidUpdatedSince
- RejectsInvalidLimit
Neue Tests (router_test.go):
- StatusPageRejectsInvalidQueryParams (table-driven, alle 4 Faelle)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>