- API-ENDPOINTS.md: neue Sektion "Display-Steuerung" mit POST /api/v1/screens/{screenSlug}/display und POST /api/v1/screens/{screenSlug}/schedule
- SCHEMA.md: Hinweis zur vereinfachten screen_status-Tabelle (Migration 004) und neue Sektion screen_schedules (Migration 005)
- server/backend/README.md: scheduler-Package beschrieben, Migrationen 004+005 ergaenzt, Endpunkt-Tabelle aktualisiert
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
189 lines
12 KiB
Markdown
189 lines
12 KiB
Markdown
# Backend
|
|
|
|
Dieses Verzeichnis enthaelt das zentrale Go-Backend fuer das Info-Board-System.
|
|
|
|
## Aufgaben
|
|
|
|
- HTTP-API und serverseitige HTML-UI (Bulma)
|
|
- PostgreSQL-Anbindung mit automatischen Migrationen
|
|
- Session-basierte Authentifizierung und rollenbasierte Zugriffskontrolle
|
|
- Medienverwaltung und Playlist-Management
|
|
- Player-Status-Ingest und Diagnose
|
|
- MQTT-Notifizierungen bei Playlist-Aenderungen
|
|
|
|
## Unterstruktur
|
|
|
|
- `cmd/api/` — Startpunkt des Backends
|
|
- `internal/app/` — App-Initialisierung und Lifecycle
|
|
- `internal/config/` — Konfiguration via Umgebungsvariablen
|
|
- `internal/db/` — PostgreSQL-Anbindung und Migrations-Runner
|
|
- `internal/store/` — Datenbankzugriff (TenantStore, ScreenStore, MediaStore, PlaylistStore, AuthStore)
|
|
- `internal/fileutil/` — Upload-Hilfsfunktionen (SaveUploadedFile mit Tenant-Isolation)
|
|
- `internal/httpapi/` — HTTP-Routing, Middleware und Handler
|
|
- `internal/httpapi/csrf.go` — Double-Submit-Cookie CSRF-Schutz
|
|
- `internal/httpapi/ratelimit.go` — Rate-Limiting fuer /login (Brute-Force-Schutz)
|
|
- `internal/httpapi/uploads.go` — Upload-Handler konsolidiert
|
|
- `internal/httpapi/screenshot.go` — Handler fuer Player-Screenshot-Upload und Screenshot-Abruf
|
|
- `internal/httpapi/screenshot_store.go` — In-Memory-Store fuer Screenshots (`ScreenshotStore`, thread-safe via `sync.RWMutex`)
|
|
- `internal/httpapi/manage/` — Admin-UI und Playlist-Management-UI
|
|
- `internal/httpapi/manage/csrf_helpers.go` — CSRF-Token Helpers fuer Templates (manage-Package)
|
|
- `internal/httpapi/tenant/` — Tenant-Self-Service-Dashboard
|
|
- `internal/httpapi/tenant/csrf_helpers.go` — CSRF-Token Helpers fuer Templates (tenant-Package, Import-Cycle-Isolation)
|
|
- `internal/mqttnotifier/` — MQTT-Notifizierungen (`NotifyChanged`, `RequestScreenshot`, `SendDisplayCommand`)
|
|
- `internal/scheduler/` — Display-Zeitplan-Scheduler (prueft jede Minute aktive Zeitplaene und sendet MQTT-Befehle)
|
|
- `internal/reqcontext/` — Context-Keys fuer authentifizierten User
|
|
|
|
### scheduler (`internal/scheduler/scheduler.go`)
|
|
|
|
Startet eine Goroutine (`scheduler.Run`), die jede Minute alle aktiven Zeitplaene aus
|
|
`screen_schedules` laedt und — sofern `power_on_time` oder `power_off_time` mit der aktuellen
|
|
Uhrzeit übereinstimmt — per MQTT den Befehl `display_on` bzw. `display_off` sendet.
|
|
|
|
Der Scheduler wird in `internal/app/app.go` als Goroutine gestartet und laeuft bis zum
|
|
Kontext-Abbruch beim Server-Shutdown.
|
|
|
|
## Datenbank-Stores
|
|
|
|
### AuthStore (`internal/store/auth.go`)
|
|
|
|
**Screen-User Management:**
|
|
- `CreateScreenUser(ctx, tenantID, username, passwordHash)` — neuen Screen-User anlegen
|
|
- `ListScreenUsers(ctx, tenantID)` — alle Screen-User eines Tenants auflisten
|
|
- `DeleteUser(ctx, userID)` — User und alle zugeordneten Permissions loeschen
|
|
|
|
**Authentifizierung:**
|
|
- `GetUserByUsername(ctx, username)` — Nutzer per Username laden
|
|
- `CreateSession(ctx, userID, ttl)` — neue Session anlegen
|
|
- `GetSessionUser(ctx, sessionID)` — User zu gueltigem Session-Token laden
|
|
- `DeleteSession(ctx, sessionID)` — Session loeschen (Logout)
|
|
- `CleanExpiredSessions(ctx)` — abgelaufene Sessions bereinigen
|
|
- `EnsureAdminUser(ctx, tenantSlug, password)` — Admin-User beim Start anlegen
|
|
- `VerifyPassword(ctx, userID, password)` — Passwort gegen bcrypt-Hash pruefen
|
|
|
|
### ScreenStore (`internal/store/screen.go`)
|
|
|
|
**Screen-User Zugriffskontrolle:**
|
|
- `GetAccessibleScreens(ctx, userID)` — alle Screens, auf die der User Zugriff hat
|
|
- `HasUserScreenAccess(ctx, userID, screenID)` — prueft ob User auf Screen zugreifen darf (boolean)
|
|
- `AddUserToScreen(ctx, userID, screenID)` — User zu Screen hinzufuegen (Eintrag in `user_screen_permissions`)
|
|
- `RemoveUserFromScreen(ctx, userID, screenID)` — User von Screen entfernen
|
|
- `GetScreenUsers(ctx, screenID)` — alle User, die auf Screen Zugriff haben
|
|
|
|
## Aktuelle Endpunkte
|
|
|
|
### Oeffentlich (keine Auth)
|
|
|
|
| Methode | Pfad | Beschreibung |
|
|
|---------|-------------------------------------|---------------------------------------|
|
|
| GET | `/healthz` | Health-Check |
|
|
| GET | `/api/v1` | API-Entrypoint |
|
|
| GET | `/api/v1/meta` | Metainformationen |
|
|
| POST | `/api/v1/player/status` | Status-Ingest vom Player-Agent |
|
|
| POST | `/api/v1/player/screenshot` | Screenshot-Upload vom Player-Agent |
|
|
| GET | `/api/v1/screens/status` | Uebersicht aller Screen-Status |
|
|
| GET | `/api/v1/screens/{screenId}/status` | Einzelner Screen-Status |
|
|
| DELETE | `/api/v1/screens/{screenId}/status` | Screen-Status loeschen |
|
|
| GET | `/api/v1/screens/{screenId}/playlist` | Playlist fuer Player (kein Auth) |
|
|
| POST | `/api/v1/screens/register` | Agent-Selbstregistrierung |
|
|
| POST | `/api/v1/tools/message-wall/resolve`| Message-Wall-Aufloesungsendpunkt |
|
|
| GET | `/status` | HTML-Diagnoseseite |
|
|
| GET | `/status/{screenId}` | HTML-Detailseite Einzelscreen |
|
|
| GET | `/uploads/{filename}` | Hochgeladene Dateien abrufen |
|
|
| GET | `/static/bulma.min.css` | Statisches CSS |
|
|
| GET | `/static/Sortable.min.js` | Statisches JS |
|
|
| GET | `/login` | Login-Formular |
|
|
| POST | `/login` | Login verarbeiten |
|
|
| POST | `/logout` | Session beenden |
|
|
|
|
### Nur eingeloggte Benutzer (`RequireAuth`)
|
|
|
|
| Methode | Pfad | Beschreibung |
|
|
|---------|-------------------------------------------|---------------------------------------|
|
|
| GET | `/manage` | Screen-Uebersicht fuer screen_user |
|
|
| GET | `/manage/{screenSlug}` | Playlist-Management-UI |
|
|
| POST | `/manage/{screenSlug}/upload` | Medium fuer Screen hochladen |
|
|
| POST | `/manage/{screenSlug}/items` | Item zur Playlist hinzufuegen |
|
|
| POST | `/manage/{screenSlug}/items/{itemId}` | Item aktualisieren |
|
|
| POST | `/manage/{screenSlug}/items/{itemId}/delete` | Item loeschen |
|
|
| POST | `/manage/{screenSlug}/reorder` | Items reordnen |
|
|
| POST | `/manage/{screenSlug}/media/{mediaId}/delete` | Medium loeschen |
|
|
| GET | `/api/v1/playlists/{screenId}` | Playlist mit Metadaten abrufen |
|
|
| POST | `/api/v1/playlists/{playlistId}/items` | Item zur Playlist hinzufuegen (API) |
|
|
| PATCH | `/api/v1/items/{itemId}` | Item aktualisieren (API) |
|
|
| DELETE | `/api/v1/items/{itemId}` | Item loeschen (API) |
|
|
| PUT | `/api/v1/playlists/{playlistId}/order` | Items reordnen (API) |
|
|
| PATCH | `/api/v1/playlists/{playlistId}/duration` | Standard-Dauer setzen (API) |
|
|
| DELETE | `/api/v1/media/{id}` | Medium loeschen (API) |
|
|
| GET | `/api/v1/screens/{screenId}/screenshot` | Screenshot eines Screens abrufen |
|
|
| POST | `/api/v1/screens/{screenSlug}/display` | Display ein-/ausschalten (MQTT) |
|
|
| POST | `/api/v1/screens/{screenSlug}/schedule` | Display-Zeitplan speichern |
|
|
|
|
### Nur Admins (`RequireAuth` + `RequireAdmin`)
|
|
|
|
| Methode | Pfad | Beschreibung |
|
|
|---------|-------------------------------------------|---------------------------------------|
|
|
| GET | `/admin` | Admin-Uebersicht |
|
|
| POST | `/admin/screens/provision` | Provisionierungs-Job starten |
|
|
| POST | `/admin/screens` | Neuen Screen anlegen |
|
|
| POST | `/admin/screens/{screenId}/delete` | Screen loeschen |
|
|
| POST | `/admin/users` | Screen-User anlegen |
|
|
| POST | `/admin/users/{userID}/delete` | Screen-User loeschen |
|
|
| POST | `/admin/screens/{screenID}/users` | User zu Screen hinzufuegen |
|
|
| POST | `/admin/screens/{screenID}/users/{userID}/remove` | User von Screen entfernen |
|
|
|
|
### Tenant-scoped (`RequireAuth` + `RequireTenantAccess`)
|
|
|
|
| Methode | Pfad | Beschreibung |
|
|
|---------|---------------------------------------------------|---------------------------------|
|
|
| GET | `/tenant/{tenantSlug}/dashboard` | Tenant-Self-Service-Dashboard |
|
|
| POST | `/tenant/{tenantSlug}/upload` | Medium hochladen |
|
|
| POST | `/tenant/{tenantSlug}/media/{mediaId}/delete` | Medium loeschen |
|
|
| GET | `/api/v1/tenants/{tenantSlug}/screens` | Screens eines Tenants auflisten |
|
|
| POST | `/api/v1/tenants/{tenantSlug}/screens` | Screen anlegen |
|
|
| GET | `/api/v1/tenants/{tenantSlug}/media` | Medien eines Tenants auflisten |
|
|
| POST | `/api/v1/tenants/{tenantSlug}/media` | Medium hochladen (API) |
|
|
|
|
## Konfiguration
|
|
|
|
Alle Werte per Umgebungsvariable:
|
|
|
|
| Variable | Bedeutung | Standard |
|
|
|-----------------------------------|----------------------------------------------------------|---------------|
|
|
| `MORZ_INFOBOARD_HTTP_ADDR` | HTTP-Listen-Adresse | `:8080` |
|
|
| `MORZ_INFOBOARD_DATABASE_URL` | PostgreSQL-Connection-String | — |
|
|
| `MORZ_INFOBOARD_UPLOAD_DIR` | Verzeichnis fuer hochgeladene Medien | `/tmp/morz-uploads` |
|
|
| `MORZ_INFOBOARD_STATUS_STORE_PATH`| Pfad zur JSON-Persistenz-Datei fuer Status-Store | leer (in-memory) |
|
|
| `MORZ_INFOBOARD_ADMIN_PASSWORD` | Passwort des initialen Admin-Users (leer = kein Anlegen) | leer |
|
|
| `MORZ_INFOBOARD_DEFAULT_TENANT` | Slug des Tenants, dem der Admin zugeordnet wird | `morz` |
|
|
| `MORZ_INFOBOARD_DEV_MODE` | `true` = Session-Cookie ohne Secure-Flag (nur lokal) | `false` |
|
|
| `MORZ_INFOBOARD_REGISTER_SECRET` | Pre-Shared-Secret fuer POST /api/v1/screens/register | leer |
|
|
| `MORZ_INFOBOARD_MQTT_BROKER` | MQTT-Broker-URL (leer = kein MQTT) | leer |
|
|
| `MORZ_INFOBOARD_MQTT_USERNAME` | MQTT-Benutzername | leer |
|
|
| `MORZ_INFOBOARD_MQTT_PASSWORD` | MQTT-Passwort | leer |
|
|
|
|
Detailliertere Beschreibung und lokale Startbeispiele: `DEVELOPMENT.md`.
|
|
|
|
## Middleware
|
|
|
|
### `RequireScreenAccess`
|
|
|
|
Middleware zur rollenbasierten Zugriffskontrolle auf Screen-Ressourcen.
|
|
|
|
**Verhalten:**
|
|
- Admins duerfen auf alle Screens zugreifen
|
|
- Screen-User duerfen nur auf Screens zugreifen, fuer die sie in `user_screen_permissions` eingetragen sind
|
|
- Tenant-User duerfen auf alle Screens ihres Tenants zugreifen
|
|
- Response: `403 Forbidden` wenn keine Berechtigung
|
|
|
|
**Verwendung:**
|
|
- `GET /api/v1/screens/{screenId}/playlist`
|
|
- `POST /manage/{screenSlug}/...`
|
|
- Alle privaten Screen-Endpunkte
|
|
|
|
## Migrationen
|
|
|
|
- `001_core.sql` — initiales Schema (Tenants, Screens, Playlists, Media, etc.)
|
|
- `002_auth.sql` — Auth-Tabellen (`users`, `sessions`)
|
|
- `003_user_screen_permissions.sql` — Screen-User Management (`user_screen_permissions`)
|
|
- `004_screen_status.sql` — Display-Zustand pro Screen (`screen_status`: screen_id, display_state, reported_at)
|
|
- `005_screen_schedules.sql` — Zeitplan pro Screen (`screen_schedules`: screen_id, schedule_enabled, power_on_time, power_off_time)
|