morz-infoboard/docs/KAMPAGNEN-AKTIVIERUNG.md
Jesko Anschütz dd3ec070f7 Security-Review + Phase 6: CSRF, Rate-Limiting, Tenant-Isolation, Screenshot, Ansible
### 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>
2026-03-23 21:06:35 +01:00

483 lines
17 KiB
Markdown

# 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
### 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.
```go
// 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:
1. Server laedt alle geplanten/aktiven Kampagnen
2. Scheduler prueft bei jedem Takt (1 Min), ob eine Aktivierung/Deaktivierung faellig ist
3. 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:
1. Sortierende nach Prioritaet (hoechste gewinnt)
2. Bei gleicher Prioritaet: nach Start-Zeitstempel (neueste gewinnt)
3. 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:
```json
{
"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