# Info-Board Neu - Server-Konzept

## Ziel

Der Server ist die zentrale Steuer- und Verwaltungsinstanz der Plattform.

Er soll:

- Benutzer, Firmen und Screens verwalten
- Medien und Playlists bereitstellen
- globale Templates und Kampagnen steuern
- Provisionierung neuer Screens ausloesen
- Status, Screenshots und Heartbeats sammeln
- MQTT und HTTPS sauber trennen

## Grundprinzip

Der Server ist die Quelle der fachlichen Wahrheit.

Der Player bleibt trotzdem lauffaehig, wenn der Server temporaer nicht erreichbar ist.

Das bedeutet:

- Server verwaltet Konfiguration und Inhalte zentral
- Player fuehrt lokal und robust aus
- Server sendet Steuerimpulse, Player synchronisiert aktiv nach

## Hauptkomponenten

### Reverse Proxy

Aufgaben:

- TLS-Terminierung
- Routing fuer API und Web-UIs
- optionale Auth-/Header-Regeln

### Backend-API

Bevorzugte Sprache:

- `Go`

Aufgaben:

- Authentifizierung und Autorisierung
- CRUD fuer Tenants, Users, Screens, Medien, Playlists
- Verwaltung globaler Templates und Kampagnen
- Player-Sync-Endpunkte
- Speicherung von Status und Screenshots
- Start von Provisionierungsjobs

### Admin-UI

Aufgaben:

- Gesamtübersicht aller Screens
- Vorschau und Status
- Template-/Kampagnenverwaltung
- Kommandos und Provisionierung

### Tenant-UI

Aufgaben:

- Uploads und Medienverwaltung pro Firma
- Pflege der monitorbezogenen Playlist
- Vorschau des eigenen Screens

### Datenbank

Empfehlung:

- PostgreSQL

Aufgaben:

- Speicherung fachlicher Daten
- Status, Jobs, Revisionen, Zuordnungen

### MQTT-Broker

Empfehlung:

- Mosquitto

Aufgaben:

- Heartbeats
- Statusmeldungen
- Events
- Kommandos und ACKs

### Dateispeicher

Aufgaben:

- Uploads
- Screenshots
- ggf. serverseitig vorbereitete Medien

V1:

- Dateisystem ausreichend

## Fachliche Bereiche

## 1. Mandanten und Benutzer

Der Server trennt:

- globale Admins
- tenantgebundene Nutzer

Regel:

- Firmen sehen nur ihren Bereich
- Admins sehen alles

## 2. Screen-Verwaltung

Der Server kennt jeden Screen mit:

- ID
- Name
- Klasse
- Orientierung
- Rotation
- Tenant-Zuordnung
- technischem Registrierungsstatus

## 3. Medienverwaltung

Der Server verwaltet:

- Uploads
- externe Medienreferenzen
- Metadaten
- tenant- oder screenspezifische Zuordnung

## 4. Playlist-Verwaltung

Der Server verwaltet tenantbezogene Inhalte pro Screen.

Wichtige Felder:

- Reihenfolge
- Dauer
- `valid_from`
- `valid_until`
- Fehlerstrategie
- Cache-Politik

## 5. Template- und Kampagnenverwaltung

Der Server stellt den globalen Orchestrierungsbereich bereit.

Funktionen:

- Templates erstellen
- Szenen pflegen
- Zielgruppen waehlen
- Kampagnen aktivieren/deaktivieren
- Zeitfenster setzen
- Uebersteuerung sichtbar machen

## 6. Provisionierung

Der Server startet Provisionierungsjobs fuer neue Screens.

Aufgaben:

- Eingaben aus Admin-UI entgegennehmen
- Job anlegen
- Secret-Handling absichern
- Worker oder Jobrunner starten
- Fortschritt speichern
- Fehler sauber rueckmelden

## API-Bereiche

Die API soll mindestens diese Domänen haben:

- `auth`
- `tenants`
- `users`
- `screens`
- `media`
- `playlists`
- `templates`
- `campaigns`
- `player`
- `commands`
- `provisioning`

## Revisionsmodell

Der Server arbeitet mit Revisionen, damit Player effizient synchronisieren koennen.

Mindestens:

- `config_revision`
- `playlist_revision`
- `media_revision`
- `campaign_revision`

## Status- und Vorschaukonzept

Der Server speichert:

- letzten bekannten Heartbeat
- letzten Status
- letzten Screenshot
- aktuelle Inhaltsquelle pro Screen

Die Admin-UI soll damit erkennen:

- online/offline
- normaler tenantbezogener Betrieb
- globale Kampagnen-Uebersteuerung
- Fallback-Betrieb
- Fehlerzustand

## Provisionierungsjobrunner

Die Provisionierung soll nicht direkt in Web-Requests laufen.

Stattdessen:

- API legt Job an
- dedizierter Worker/Jobrunnner arbeitet ihn ab
- Fortschritt wird in DB gespeichert

Zusaetzlich fuer v1 festzulegen:

- ACK-Timeout-Handling fuer `device_commands` ueber Worker
- Secret-Handling fuer Provisionierungs-Bootstrap ueber kurzlebige Secret-Referenzen
- physische Netzposition des Workers fuer SSH-Erreichbarkeit als Betriebsparameter

## Docker-Compose-Zielbild

Sinnvolle Komponenten in `compose/`:

- `reverse-proxy`
- `backend`
- `postgres`
- `mosquitto`
- optional `worker`

## Authentifizierung

Der Server verwendet einen Session-basierten Login-Flow mit `bcrypt`-Passwort-Hashing.

### Login-Flow

1. `GET /login` rendert das Login-Formular (Bulma-Card zentriert).
2. `POST /login` prueft Username und Passwort:
   - `AuthStore.GetUserByUsername` laedt den User inkl. Tenant-Slug.
   - `bcrypt.CompareHashAndPassword` prueft das Passwort (Cost-Faktor 12).
   - Bei Erfolg legt `AuthStore.CreateSession` eine Session an (TTL 24 Stunden).
   - Das Session-Token wird als `morz_session`-Cookie gesetzt (`HttpOnly=true`, `Secure=true`).
   - Im `DevMode` (`MORZ_INFOBOARD_DEV_MODE=true`) wird `Secure=false` gesetzt fuer lokalen HTTP-Betrieb.
   - Weiterleitung je nach Rolle: `admin` → `/admin`, `tenant` → `/tenant/{slug}/dashboard`.
3. `POST /logout` loescht die Session in der DB und entfernt den Cookie.

### Cookie-Lebensdauer

- Standard-TTL: 24 Stunden
- Der Cookie verfaellt automatisch; die DB wird stuendlich durch `CleanExpiredSessions` bereinigt.

### Admin-User-Bootstrap

Beim Server-Start wird `EnsureAdminUser` aufgerufen, wenn `MORZ_INFOBOARD_ADMIN_PASSWORD` gesetzt ist.
Der Admin-User wird dem Tenant mit Slug `MORZ_INFOBOARD_DEFAULT_TENANT` (Standard: `morz`) zugeordnet.
Ist der User bereits vorhanden, passiert nichts. Fehler sind nicht fatal — der Server startet trotzdem.

---

## Middleware-Kette

Alle geschuetzten Routen werden durch Middleware-Funktionen in `internal/httpapi/middleware.go` abgesichert.

```
Eingehende Anfrage
       │
       ▼
 RequireAuth              Liest morz_session-Cookie, prueft Session via DB,
                          speichert *store.User im Request-Context.
                          → Fehler: Redirect zu /login?next=<Pfad>
       │
       ├─► RequireAdmin   Prueft user.Role == "admin"
       │                  → Fehler: 403 Forbidden
       │
       └─► RequireTenant  Prueft user.TenantSlug == {tenantSlug} aus dem URL-Pfad.
           Access         Admins duerfen immer durch.
                          → Fehler: 403 Forbidden
```

### Route-Gruppen im Router

| Gruppe         | Middleware                         | Beispielrouten                              |
|----------------|------------------------------------|---------------------------------------------|
| Oeffentlich    | keine                              | `/healthz`, `/login`, `/api/v1/screens/register` |
| Auth-only      | RequireAuth                        | `/manage/{screenSlug}/...`                  |
| Admin-only     | RequireAuth + RequireAdmin         | `/admin`, `/admin/screens/...`              |
| Tenant-scoped  | RequireAuth + RequireTenantAccess  | `/tenant/{tenantSlug}/...`, `/api/v1/tenants/{tenantSlug}/...` |

Der Hilfsfunktion `chain(middlewares...)` in `router.go` wrappet Handler von aussen nach innen.

---

## Tenant-Dashboard

Das Tenant-Self-Service-Dashboard ist unter `/tenant/{tenantSlug}/dashboard` erreichbar.

### URL-Schema

| Methode | Pfad                                        | Beschreibung              |
|---------|---------------------------------------------|---------------------------|
| GET     | `/tenant/{tenantSlug}/dashboard`            | Dashboard rendern         |
| POST    | `/tenant/{tenantSlug}/upload`               | Medium hochladen          |
| POST    | `/tenant/{tenantSlug}/media/{mediaId}/delete` | Medium loeschen         |

### Tabs

- **Tab A – Meine Monitore:** Zeigt Screen-Karten mit Live-Status. Der Status wird per JavaScript
  aus `GET /api/v1/screens/status` geladen und alle 30 Sekunden aktualisiert.
  Status-Badge: `is-success` (online), `is-danger` (offline), `is-warning` (unbekannt).
- **Tab B – Mediathek:** Upload-Formular (Bild, Video, PDF oder Web-URL) und Dateiliste
  mit Loeschen-Button. Nach Upload oder Loeschen Redirect mit `?tab=media&flash=uploaded/deleted`.

### Eigentuemer-Pruefung beim Loeschen

`HandleTenantDeleteMedia` prueft, dass `asset.TenantID == tenant.ID`, bevor es loescht.
Damit ist sichergestellt, dass ein Tenant keine Assets anderer Tenants loeschen kann,
selbst wenn er die `mediaId` erraten wuerde.

---

## Sicherheitsgrundsaetze

- Root-Bootstrap-Geheimnisse nur kurzlebig oder referenziert speichern
- API- und MQTT-Zugaenge getrennt behandeln
- alle Admin-Kommandos auditieren
- Tenant-Trennung strikt serverseitig erzwingen

## API-Fehlermodell

Vor Implementierungsbeginn gilt ein einheitlicher Fehlerumschlag.

Empfehlung:

```json
{
  "error": {
    "code": "screen_not_found",
    "message": "Screen existiert nicht",
    "details": null
  }
}
```

## Zielstruktur im Repo

Empfohlene Unterstruktur fuer den Server:

- `server/backend/`
- `server/admin-ui/`
- `server/tenant-ui/`
- `server/worker/`
- `compose/`

## Testfaelle fuer den Server

- Tenant sieht nur eigene Daten
- Admin sieht alle Daten
- Kampagne ueberschreibt tenantbezogenen Content korrekt
- Screen-Provisionierung legt Job sauber an
- Player-Sync ueber Revisionen funktioniert
- MQTT-Kommandos werden protokolliert
- Screenshot-Upload erscheint im Admin-Dashboard
