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

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

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:

  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:

{
  "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