### 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>
17 KiB
Info-Board Neu - Aktivierungsoberflaeche fuer saisonale und temporaere Kampagnen
Ziel
Die Aktivierungsoberflaeche ermoeglicht es dem Admin, Kampagnen zeitlich und gezielt auf Screens auszurollen — sofort oder geplant.
Dieses Dokument beschreibt:
- die Aktivierungs-Workflows im Admin-UI
- zeitgesteuerte Aktivierung (Scheduler)
- Screen-Zuordnung und Vorschau
- Status und Kontrolle waehrend der Laufzeit
Siehe auch docs/TEMPLATE-EDITOR.md fuer die Template-Verwaltung und docs/TEMPLATE-KONZEPT.md fuer konzeptionelle Grundlagen.
1. Aktivierungs-Workflows
Workflow 1 — Schnelle Sofort-Aktivierung
Szenario: Admin hat ein Template und will es sofort starten.
Weg:
Admin → Templates → [Template] → "Aktivieren"
┌──────────────────────────────────────────┐
│ Kampagne starten: Weihnachtsmotiv 2025 │
├──────────────────────────────────────────┤
│ │
│ Kampagnen-Name (eindeutig) │
│ [ Weihnachten 2025 _________________] │
│ Vorschau: morz_campaign_xmas2025 │
│ │
│ Zielgruppe pruefen │
│ aus Template: Alle Screens (13) │
│ [Gruppe aendernx] [Screens aendernx] │
│ │
│ Dauer │
│ ⦿ Sofort starten │
│ gueltig ab jetzt │
│ ○ Geplant starten │
│ [Datum/Uhrzeit auswaehlen] │
│ │
│ Gueltig bis │
│ [Datum/Uhrzeit auswaehlen] │
│ oder [ ] unbegrenzt │
│ │
│ Prioritaet gegenueber Playlist │
│ [10____________] hoeher = wichtiger │
│ Standardwert: 1 │
│ │
│ Auto-Deaktivierung bei Ablauf? │
│ ⦿ Ja, danach Fallback zeigen │
│ ○ Nein, manuell deaktivieren │
│ │
│ Vorschau betroffener Screens │
│ [Screenshot-Vorschau mit Kampagnen- │
│ Inhalt fuer ausgew. Screens] │
│ │
│ [Aktivieren] [Abbrechen] │
└──────────────────────────────────────────┘
Aktion:
- Server speichert Kampagne mit
active = true,valid_from = NOW() - Server expandiert Zielgruppe in konkrete Screens
- Alle betroffenen Screens erhalten MQTT-Signal
playlist-changed(obwohl Playlist gleich, aber Kampagnen-Prioritaet aendert sich) - Screens synchonisieren und laden neue Kampagnen-Inhalte
Workflow 2 — Geplante Aktivierung
Szenario: Admin bereitet eine Kampagne vor, soll aber erst am naechsten Tag 8:00 Uhr starten.
Weg:
Admin → Templates → [Template] → "Aktivieren" → "Geplant starten"
┌──────────────────────────────────────────┐
│ Geplante Aktivierung: Ostern 2025 │
├──────────────────────────────────────────┤
│ │
│ Kampagnen-Name │
│ [ Ostern_Dekoration_2025 ____________ ] │
│ │
│ Startdatum und -uhrzeit │
│ [2025-04-14] [08:00] [Kalender/Uhr] │
│ │
│ Enddatum und -uhrzeit (optional) │
│ [2025-04-21] [20:00] [Kalender/Uhr] │
│ oder [ ] Kein Enddatum │
│ │
│ Prioritaet │
│ [1_____________] │
│ │
│ Auto-Deaktivierung? │
│ ⦿ Ja │
│ ○ Nein │
│ │
│ Status │
│ ◯ GEPLANT — wird am 2025-04-14 08:00 │
│ aktiviert │
│ │
│ Erinnerung setzen (optional) │
│ [ ] Erinnerungs-Email 1 Tag vorher │
│ [ ] Erinnerungs-Email 1 Stunde vorher │
│ │
│ [Planen & Speichern] [Abbrechen] │
└──────────────────────────────────────────┘
Aktion:
- Server speichert Kampagne mit
active = false,valid_from = 2025-04-14 08:00 - Server erstellt inneren Scheduler-Job
- Admin sieht Kampagne in Liste mit Status "GEPLANT"
- Um geplanten Zeitpunkt:
- Scheduler setzt
campaigns.active = true - MQTT-Signal an alle betroffenen Screens
- Optionale Erinnerungs-Email an Admin
- Scheduler setzt
Workflow 3 — Schnelle Deaktivierung
Szenario: Kampagne laeuft, Admin will sie sofort stoppen.
Weg:
Admin → Kampagnen → [laufende Kampagne] → "Deaktivieren"
┌──────────────────────────────────────────┐
│ Kampagne deaktivieren? │
├──────────────────────────────────────────┤
│ │
│ Kampagne: Weihnachten 2025 │
│ Status: AKTIV seit 2025-12-01 09:00 │
│ Betroffene Screens: 13 │
│ │
│ Aktion: │
│ ⦿ Sofort deaktivieren │
│ Screens zeigen danach wieder │
│ Tenant-Playlist oder Fallback │
│ │
│ ○ Mit Verzoegerung (Fade-Out) │
│ [2 Min] [5 Min] [Uhr auswaehlen] │
│ Nuetzlich: Licht dimmen, Musik leiser │
│ etc. vor Inhalt-Wechsel │
│ │
│ [Ja, deaktivieren] [Abbrechen] │
└──────────────────────────────────────────┘
Aktion:
- Server setzt
campaigns.active = false - Server sendet MQTT-Signal an Screens
- Screens wechseln sofort (oder mit Verzoegerung) zu Fallback/Playlist
- Kampagne verschwindet aus "Aktive Kampagnen"-Liste
2. Zeitplanung und Scheduler
Automatisierte Scheduler-Jobs
Der Server laeuft einen einfachen Scheduler als Goroutine oder als separaten Service.
// Pseudocode
type CampaignScheduler interface {
RegisterJob(campaignID, activateAt, deactivateAt time.Time)
RunScheduler(ctx context.Context)
}
// Beim Starten
func init() {
scheduler := NewCampaignScheduler()
go scheduler.RunScheduler(ctx)
}
// Im Hintergrund
func (s *CampaignScheduler) RunScheduler(ctx context.Context) {
ticker := time.NewTicker(1 * time.Minute)
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
// Checke alle geplanten Kampagnen
campaigns := db.GetScheduledCampaigns()
for _, c := range campaigns {
if time.Now() >= c.ValidFrom && !c.Active {
// Aktiviere die Kampagne
s.ActivateCampaign(c.ID)
}
if c.ValidUntil != nil && time.Now() >= *c.ValidUntil && c.Active {
// Deaktiviere die Kampagne
s.DeactivateCampaign(c.ID)
}
}
}
}
}
Persistenz ueber Restart
Scheduler-Jobs werden in der Datenbank gespeichert (Spalten valid_from, valid_until, active in campaigns-Tabelle).
Beim Neustart des Servers:
- Server laedt alle geplanten/aktiven Kampagnen
- Scheduler prueft bei jedem Takt (1 Min), ob eine Aktivierung/Deaktivierung faellig ist
- Kein Datenverlust, kein komplexes Job-Persisting noetig
Erinnerungen und Notifications
Optional (Phase 2):
- Email-Erinnerung N Stunden vor Aktivierung
- Webhook-Notification fuer externe Systeme
- In-App-Benachrichtigung im Admin-Dashboard
3. Screen-Zuordnung und Vorschau
Interaktive Zielgruppen-Auswahl
Waehrend der Kampagnen-Erstellung kann der Admin entscheiden, welche Screens betroffen sein sollen.
Zielgruppe
⦿ Alle Screens
○ Nach Gruppe auswaehlen:
□ wall-all (9 Screens)
□ single-info (2 Screens)
□ vertretungsplan-all (2 Screens)
○ Einzelne Screens:
[ Suchfeld: "info" ]
□ info01 (portrait)
□ info02 (portrait)
☑ info03 (portrait)
□ info04 (portrait)
...
Rendering-Vorschau
Admin sieht, wie die Kampagne auf verschiedenen Zielscreens aussieht:
Betroffene Screens: 4 ausgew.
┌─────────────────────────────────────┐
│ info01 (portrait, 1920x1080) │
│ ┌────────────────────────────────┐ │
│ │ │ │
│ │ [Kampagnen-Inhalt: Bild] │ │
│ │ (Portrait-Assets verwendet) │ │
│ │ │ │
│ └────────────────────────────────┘ │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ info05 (landscape, 2560x1440) │
│ ┌────────────────────────────────┐ │
│ │ [Kampagnen-Inhalt: Bild] │ │
│ │ (Landscape-Assets verwendet) │ │
│ └────────────────────────────────┘ │
└─────────────────────────────────────┘
[Scrollen um weitere Screens zu sehen]
Live-Uebersicht waehrend Laufzeit
Wenn eine Kampagne aktiv ist, zeigt das Admin-Dashboard:
Kampagne: Weihnachten 2025 einfuehrung
Status: AKTIV seit 2025-12-01 09:00
Betroffene Screens: 13
✓ Aktiv angezeigt: 11 (info01-info08, info10, info11, info13)
◯ Wartet auf Sync: 1 (info09)
✗ Offline: 1 (info12)
Zuletzt geprueft: vor 30 Sekunden
[Aktualisieren] [Deaktivieren] [Bearbeiten]
4. Kampagnen-Verwaltung waehrend Laufzeit
Aktive Kampagnen — Haupt-Dashboard
Seite: Admin → Aktive Kampagnen (oder Campaigns)
┌─────────────────────────────────┐
│ Aktive Kampagnen │
├─────────────────────────────────┤
│ │
│ Weihnachten 2025 einfuehrung │ ▼
│ Template: Weihnachtsmotiv 2025 │
│ Aktiv seit: 2025-12-01 09:00 │
│ Aktiv bis: 2025-12-26 23:59 │
│ Betroffene: 13 Screens │
│ Status: ✓ Auf allen Screens ok │
│ │
│ [Vorschau] [Bearbeiten] │
│ [Deaktivieren] │
│ │
├─────────────────────────────────┤
│ │
│ Event-Tag 25.03 │
│ Template: screen_specific_scene │
│ Aktiv seit: 2025-03-25 00:00 │
│ Aktiv bis: 2025-03-25 23:59 │
│ Betroffene: 4 Screens │
│ Status: ◯ 1 Screen offline │
│ │
│ [Vorschau] [Bearbeiten] │
│ [Deaktivieren] │
│ │
└─────────────────────────────────┘
Geplante Kampagnen
Seite: Admin → Kampagnen (Alle)
┌─────────────────────────────────┐
│ Geplante Kampagnen │
├─────────────────────────────────┤
│ │
│ Ostern-Dekoration 2025 │ ▼
│ Template: full_screen_media │
│ Status: GEPLANT │
│ Startet: 2025-04-14 08:00 │
│ Endet: 2025-04-21 20:00 │
│ Betroffene: 13 Screens │
│ Erinnerung: 1 Tag vorher │
│ │
│ [Vorschau] [Bearbeiten] │
│ [Jetzt aktivieren] [Loeschen] │
│ │
├─────────────────────────────────┤
│ │
│ Sommer-Kampagne │
│ Status: GEPLANT │
│ Startet: 2025-06-01 00:00 │
│ │
│ ... │
│ │
└─────────────────────────────────┘
Abgelaufene Kampagnen
Seite: Admin → Kampagnen (Archiv)
Zeigt inaktive/abgelaufene Kampagnen fuer Audit-Trail.
[ Kampagne ] Zeitraum Status
Ostern 2025 2025-04-14—04-21 Auto-Deaktiviert
Karneval 2025-02-28—03-05 Manuell deaktiviert
Valentinstag 2025-02-14 Auto-Deaktiviert
5. Prioritaetsverwaltung
Prio-Einstellung pro Kampagne
Prioritaet gegenueber Tenant-Playlist
┌─────────────────────────────────┐
│ Schieber oder Zahlenfeld │
│ │
│ [|━━━━━━━━━━━| ] 10 │
│ 1 5 10 100 │
│ │
│ Bedeutung: │
│ 1 = normale Kampagne │
│ 10 = hohe Prioritaet (Standard) │
│ 100 = Notfall / absolut wichtig │
│ │
│ Diese Prioritaet wird ueber │
│ alle Tenant-Playlists gestellt │
│ (falls mehrere Kampagnen) │
│ verwendet die mit hoechster │
│ Prioritaet │
└─────────────────────────────────┘
Konflikt-Management (mehrere Kampagnen gleichzeitig)
Falls mehrere Kampagnen fuer denselben Screen aktiv sind:
- Sortierende nach Prioritaet (hoechste gewinnt)
- Bei gleicher Prioritaet: nach Start-Zeitstempel (neueste gewinnt)
- Admin sieht im Status-Dashboard einen Warning: "2 Kampagnen fuer info01 aktiv"
Empfehlung: Admin sollte Zeitraeume von Kampagnen nicht ueberlappen lassen.
6. Fehlerbehandlung
Was, wenn ein Screen offline ist?
Kampagne wird aktiviert, aber Screen info03 ist gerade offline:
1. Server weiss, dass info03 Ziel der Kampagne ist
2. Server loggt: "Kampagne XYZ kann nicht auf info03 ausgeliefert werden (offline)"
3. Info03 hat letzte gueltige Kampagne gecacht
4. Sobald info03 wieder online kommt:
- Player synchonisiert
- Server sagt: "Kampagne XYZ ist aktiv"
- Player ladet und rendert
5. Status im Dashboard: "info03 — Offline, wird synchronisiert sobald online"
Rollback bei fehlgeschlagener Aktivierung
Falls eine Kampagne fehlerhaft ist (kaputtes Video, Renderingfehler):
1. Screen zeigt Fehler-Overlay
2. Admin ist informiert (Status-API zeigt Fehler)
3. Admin Aktion 1: Template korrigieren
- Fehlerhaftes Asset austauschen
- Kampagne aktualisieren
- Screens neu synchonisieren
4. Admin Aktion 2: Schnelle Deaktivierung
- Kampagne abschalten
- Fallback/Playlist kehrt zurueck
7. Datenschutz und Audit
Audit-Trail
Alle Kampagnen-Aenderungen werden protokolliert:
{
"ts": "2025-03-25T14:22:00Z",
"event": "campaign_activated",
"campaign_id": "uuid-...",
"campaign_name": "Ostern-Dekoration",
"triggered_by_user_id": "admin123",
"triggered_by_email": "admin@example.com",
"details": {
"valid_from": "2025-04-14T08:00:00Z",
"valid_until": "2025-04-21T20:00:00Z",
"target_screens_count": 13
}
}
Diese Logs sind fuer Compliance und Forensik wichtig.
Sichtbarkeitsbeschraenkung
Nur Benutzer mit Admin-Rolle koennen:
- Kampagnen erstellen/aendernx
- Templates bearbeiten
- Aktivierung planen
Tenant-User sehen keine Kampagnen-Verwaltung.
8. Zusammenfassung
Die Aktivierungsoberflaeche:
- ist einsteigerfreundlich — Multi-Step Formulare mit Vorschau
- unterstuetzt Sofort und Planung — spontan oder Wochen im Voraus
- ist sichtbar — Live-Status und Fehler-Reporting
- ist automatisiert — Scheduler kuemmert sich um Auf-/Abschalten
- ist sicher — Audit-Trail und Rollback-Moeglichkeiten
- ist robust — Offline-Screens werden spaeter synchronisiert