Doku-Sync: Auth, Tenant-Dashboard, Middleware, Schema nachgezogen
- SCHEMA.md: users-Tabelle korrigiert, sessions-Tabelle ergänzt - API-ENDPOINTS.md: Auth-Routen + Tenant-Dashboard-Routen ergänzt - SERVER-KONZEPT.md: Abschnitte Authentifizierung, Middleware-Kette, Tenant-Dashboard neu - backend/README.md: komplett neu auf Basis aktueller Implementierung - DEVELOPMENT.md: veraltete "nicht vorhanden"-Punkte bereinigt Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
0e66bfdb24
commit
4268da7988
5 changed files with 379 additions and 36 deletions
|
|
@ -40,9 +40,9 @@ Bereits vorhanden:
|
||||||
|
|
||||||
Noch nicht vorhanden:
|
Noch nicht vorhanden:
|
||||||
|
|
||||||
- admin-seitige Benutzerautentifizierung und Zugriffskontrolle
|
|
||||||
- Multi-Tenancy-Isolation auf API-Ebene
|
|
||||||
- produktives SSL/TLS-Handling fuer Deployment
|
- produktives SSL/TLS-Handling fuer Deployment
|
||||||
|
- Docker-Secret-Integration fuer `MORZ_INFOBOARD_ADMIN_PASSWORD`
|
||||||
|
- Ansible-Variable `morz_admin_password` als Vault-Variable (Phase 6)
|
||||||
|
|
||||||
## Voraussetzungen auf dem Entwicklungsrechner
|
## Voraussetzungen auf dem Entwicklungsrechner
|
||||||
|
|
||||||
|
|
@ -287,11 +287,15 @@ Das Playbook erledigt:
|
||||||
|
|
||||||
## Empfohlene naechste Implementierungsschritte
|
## Empfohlene naechste Implementierungsschritte
|
||||||
|
|
||||||
1. Backend: einheitliches Fehlerformat und Routing-Grundstruktur anlegen
|
Die Punkte 1–4 der urspruenglichen Liste (Fehlerformat, Routing, Status, MQTT) sind umgesetzt.
|
||||||
2. Backend: Konfigurations- und App-Lifecycle stabilisieren
|
Offene Punkte aus Phase 6 des Tenant-Feature-Plans (`docs/TENANT-FEATURE-PLAN.md`):
|
||||||
3. Agent und Backend: den HTTP-Statuspfad als Grundlage fuer Identitaet, Persistenz und spaetere Admin-Vorschau erweitern
|
|
||||||
4. Agent: danach MQTT-spezifische Reachability und feinere Connectivity-Schwellenlogik aufsetzen
|
1. Docker-Secret fuer `MORZ_INFOBOARD_ADMIN_PASSWORD` in `compose/` einrichten
|
||||||
5. Danach die Netzwerk-, Sync- und Kommandopfade schrittweise produktionsnah ausbauen
|
2. Ansible-Variable `morz_admin_password` als Vault-Variable definieren
|
||||||
|
3. Code-Review durch Larry (SQL-Injection, Session-Fixation, bcrypt-Cost, Middleware-Reihenfolge)
|
||||||
|
4. End-to-End-Test-Checkliste in `docs/TEST-CHECKLIST-DEV.md` durchlaufen
|
||||||
|
5. Deployment: Image bauen, Migration `002_auth.sql` pruefen, Logs kontrollieren
|
||||||
|
6. Langfristig: Netzwerk-, Sync- und Kommandopfade produktionsnah ausbauen
|
||||||
|
|
||||||
## End-to-End-Entwicklungstest (Backend + Agent)
|
## End-to-End-Entwicklungstest (Backend + Agent)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -507,6 +507,122 @@ Spezialendpoint zur Auflösung von Nachrichten-Wand-Anfragen (noch in Entwicklun
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Authentifizierung (Web-Formulare)
|
||||||
|
|
||||||
|
Alle Auth-Routen erfordern keine vorherige Authentifizierung.
|
||||||
|
|
||||||
|
### GET /login
|
||||||
|
|
||||||
|
Zeigt das Login-Formular.
|
||||||
|
|
||||||
|
- Wenn ein gueltiges `morz_session`-Cookie vorhanden ist, wird direkt zum jeweiligen Dashboard
|
||||||
|
weitergeleitet (`/admin` fuer Admins, `/tenant/{slug}/dashboard` fuer Tenant-User).
|
||||||
|
|
||||||
|
**Response:** HTML-Seite mit Benutzername/Passwort-Formular und optionaler Flash-Message.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### POST /login
|
||||||
|
|
||||||
|
Verarbeitet die Login-Eingabe.
|
||||||
|
|
||||||
|
**Request (Form-Encoded):**
|
||||||
|
```
|
||||||
|
username=admin&password=geheim
|
||||||
|
```
|
||||||
|
|
||||||
|
**Verhalten:**
|
||||||
|
- Passwort wird per `bcrypt.CompareHashAndPassword` geprueft
|
||||||
|
- Bei Erfolg wird ein `morz_session`-Cookie gesetzt (HttpOnly, Secure, 24h TTL)
|
||||||
|
- Weiterleitung je nach Rolle: `admin` → `/admin`, `tenant` → `/tenant/{slug}/dashboard`
|
||||||
|
- Bei Fehler: Rueckkehr zur Login-Seite mit Flash-Message
|
||||||
|
|
||||||
|
**Status:**
|
||||||
|
- `303 See Other` — Erfolg, Weiterleitung
|
||||||
|
- `303 See Other` — Fehler, Rueckkehr zur Login-Seite mit `?msg=`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### POST /logout
|
||||||
|
|
||||||
|
Meldet den aktuellen Benutzer ab.
|
||||||
|
|
||||||
|
**Verhalten:**
|
||||||
|
- Session wird in der DB geloescht (`DeleteSession`)
|
||||||
|
- Cookie wird mit `MaxAge=-1` geloescht
|
||||||
|
- Weiterleitung zu `/login`
|
||||||
|
|
||||||
|
**Status:**
|
||||||
|
- `303 See Other`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tenant Self-Service Dashboard (Web-Formulare)
|
||||||
|
|
||||||
|
Alle Tenant-Routen erfordern `RequireAuth` + `RequireTenantAccess`.
|
||||||
|
Admins koennen auf jeden Tenant zugreifen; Tenant-User nur auf ihren eigenen.
|
||||||
|
|
||||||
|
### GET /tenant/{tenantSlug}/dashboard
|
||||||
|
|
||||||
|
Zeigt das Tenant-Self-Service-Dashboard.
|
||||||
|
|
||||||
|
**Tabs:**
|
||||||
|
- Tab A "Meine Monitore" — Screen-Karten mit Live-Status (via JS-Fetch aus `/api/v1/screens/status`)
|
||||||
|
- Tab B "Mediathek" — Upload-Formular und Dateiliste
|
||||||
|
|
||||||
|
**Query-Parameter:**
|
||||||
|
- `tab=media` — oeffnet direkt Tab B (z. B. nach Upload-Redirect)
|
||||||
|
- `flash=uploaded` / `flash=deleted` — zeigt Erfolgs-Flash-Message
|
||||||
|
|
||||||
|
**Response:** HTML-Seite.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### POST /tenant/{tenantSlug}/upload
|
||||||
|
|
||||||
|
Laedt ein Medium fuer den Tenant hoch.
|
||||||
|
|
||||||
|
**Request (Multipart Form):**
|
||||||
|
```
|
||||||
|
type: image (oder video, pdf)
|
||||||
|
title: Mein Bild
|
||||||
|
file: <binary data>
|
||||||
|
```
|
||||||
|
|
||||||
|
oder fuer eine Web-URL:
|
||||||
|
```
|
||||||
|
type: web
|
||||||
|
title: Externe Website
|
||||||
|
url: http://example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
**Verhalten:**
|
||||||
|
- Datei wird in `MORZ_INFOBOARD_UPLOAD_DIR` gespeichert
|
||||||
|
- MIME-Typ wird aus dem Upload-Header abgeleitet
|
||||||
|
- Max. Upload-Groesse: 512 MB
|
||||||
|
|
||||||
|
**Status:**
|
||||||
|
- `303 See Other` → `/tenant/{slug}/dashboard?tab=media&flash=uploaded`
|
||||||
|
- `400 Bad Request` — fehlender Typ oder Datei
|
||||||
|
- `404 Not Found` — Tenant nicht vorhanden
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### POST /tenant/{tenantSlug}/media/{mediaId}/delete
|
||||||
|
|
||||||
|
Loescht ein Medien-Asset des Tenants.
|
||||||
|
|
||||||
|
**Verhalten:**
|
||||||
|
- Eigentuemer-Pruefung: `asset.TenantID` muss mit dem Tenant uebereinstimmen
|
||||||
|
- Physische Datei wird geloescht sofern vorhanden
|
||||||
|
|
||||||
|
**Status:**
|
||||||
|
- `303 See Other` → `/tenant/{slug}/dashboard?tab=media&flash=deleted`
|
||||||
|
- `403 Forbidden` — Asset gehoert nicht diesem Tenant
|
||||||
|
- `404 Not Found` — Tenant oder Asset nicht vorhanden
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Admin UI (Web-Formulare)
|
## Admin UI (Web-Formulare)
|
||||||
|
|
||||||
### GET /admin
|
### GET /admin
|
||||||
|
|
@ -827,6 +943,11 @@ Typische HTTP-Status:
|
||||||
|
|
||||||
## Änderungshistorie
|
## Änderungshistorie
|
||||||
|
|
||||||
|
- **2026-03-23 (Update):** Auth- und Tenant-Dashboard-Endpoints ergaenzt (Doris / Doku-Review)
|
||||||
|
- `GET /login`, `POST /login`, `POST /logout` dokumentiert
|
||||||
|
- `GET /tenant/{tenantSlug}/dashboard` dokumentiert
|
||||||
|
- `POST /tenant/{tenantSlug}/upload` dokumentiert
|
||||||
|
- `POST /tenant/{tenantSlug}/media/{mediaId}/delete` dokumentiert
|
||||||
- **2026-03-23:** Initiale Dokumentation aller HTTP-Endpoints basierend auf Code-Review
|
- **2026-03-23:** Initiale Dokumentation aller HTTP-Endpoints basierend auf Code-Review
|
||||||
- Alle Screen-Management-Endpoints dokumentiert
|
- Alle Screen-Management-Endpoints dokumentiert
|
||||||
- Alle Playlist-Management-Endpoints dokumentiert
|
- Alle Playlist-Management-Endpoints dokumentiert
|
||||||
|
|
|
||||||
|
|
@ -48,21 +48,51 @@ Zweck:
|
||||||
Spalten:
|
Spalten:
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
id uuid primary key
|
id text primary key default gen_random_uuid()::text
|
||||||
tenant_id uuid null references tenants(id) on delete set null
|
tenant_id text not null references tenants(id) on delete cascade
|
||||||
username text not null unique
|
username text not null
|
||||||
email text not null unique
|
|
||||||
password_hash text not null
|
password_hash text not null
|
||||||
role text not null
|
role text not null default 'tenant'
|
||||||
active boolean not null default true
|
created_at timestamptz not null default now()
|
||||||
last_login_at timestamptz null
|
unique(tenant_id, username)
|
||||||
created_at timestamptz not null
|
|
||||||
updated_at timestamptz not null
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Regeln:
|
Regeln:
|
||||||
|
|
||||||
- `role` in v1: `admin`, `tenant_user`
|
- `role` in v1: `admin`, `tenant`
|
||||||
|
- `username` ist nur innerhalb eines Tenants eindeutig (Unique-Constraint auf `(tenant_id, username)`)
|
||||||
|
- `tenant_id` ist `NOT NULL` — jeder User gehoert genau einem Tenant
|
||||||
|
- IDs sind `text`, nicht `uuid`, enthalten aber UUID-Werte (via `gen_random_uuid()::text`)
|
||||||
|
- Felder wie `email`, `active`, `last_login_at` und `updated_at` existieren in v1 nicht
|
||||||
|
|
||||||
|
### `sessions`
|
||||||
|
|
||||||
|
Zweck:
|
||||||
|
|
||||||
|
- Sitzungstokens fuer den Browser-Login
|
||||||
|
|
||||||
|
Spalten:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
id text primary key default gen_random_uuid()::text
|
||||||
|
user_id text not null references users(id) on delete cascade
|
||||||
|
created_at timestamptz not null default now()
|
||||||
|
expires_at timestamptz not null default (now() + interval '8 hours')
|
||||||
|
```
|
||||||
|
|
||||||
|
Indizes:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
create index idx_sessions_user_id on sessions(user_id);
|
||||||
|
create index idx_sessions_expires_at on sessions(expires_at);
|
||||||
|
```
|
||||||
|
|
||||||
|
Regeln:
|
||||||
|
|
||||||
|
- Session-TTL beim Anlegen betraegt standardmaessig 8 Stunden (Migration-Default);
|
||||||
|
`AuthStore.CreateSession` uebergibt die tatsaechliche TTL als Parameter (aktuell 24 Stunden)
|
||||||
|
- Abgelaufene Sessions werden stuendlich per Hintergrund-Ticker bereinigt (`CleanExpiredSessions`)
|
||||||
|
- Cookie-Name: `morz_session`; `HttpOnly=true`, `Secure=true` (ausser `MORZ_INFOBOARD_DEV_MODE=true`)
|
||||||
|
|
||||||
### `screen_groups`
|
### `screen_groups`
|
||||||
|
|
||||||
|
|
@ -494,6 +524,21 @@ last_failed_sync_at timestamptz null
|
||||||
last_error_message text null
|
last_error_message text null
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Auth-Datenbankschema
|
||||||
|
|
||||||
|
Die Auth-Tabellen werden durch `server/backend/internal/db/migrations/002_auth.sql` angelegt
|
||||||
|
und sind vollstaendig unter den Abschnitten `users` und `sessions` oben beschrieben.
|
||||||
|
|
||||||
|
Der `AuthStore` (`internal/store/auth.go`) stellt folgende Methoden bereit:
|
||||||
|
|
||||||
|
- `GetUserByUsername(ctx, username)` — Nutzer per Username laden (inkl. `TenantSlug` via LEFT JOIN)
|
||||||
|
- `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 wenn nicht vorhanden
|
||||||
|
- `VerifyPassword(ctx, userID, password)` — Passwort gegen bcrypt-Hash pruefen
|
||||||
|
|
||||||
## Wichtige Indizes
|
## Wichtige Indizes
|
||||||
|
|
||||||
Empfohlen mindestens:
|
Empfohlen mindestens:
|
||||||
|
|
|
||||||
|
|
@ -247,6 +247,96 @@ Sinnvolle Komponenten in `compose/`:
|
||||||
- `mosquitto`
|
- `mosquitto`
|
||||||
- optional `worker`
|
- 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
|
## Sicherheitsgrundsaetze
|
||||||
|
|
||||||
- Root-Bootstrap-Geheimnisse nur kurzlebig oder referenziert speichern
|
- Root-Bootstrap-Geheimnisse nur kurzlebig oder referenziert speichern
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,109 @@
|
||||||
# Backend
|
# Backend
|
||||||
|
|
||||||
Dieses Verzeichnis enthaelt das erste Geruest fuer das zentrale Backend.
|
Dieses Verzeichnis enthaelt das zentrale Go-Backend fuer das Info-Board-System.
|
||||||
|
|
||||||
Ziel fuer die erste Ausbaustufe:
|
## Aufgaben
|
||||||
|
|
||||||
- HTTP-API in Go
|
- HTTP-API und serverseitige HTML-UI (Bulma)
|
||||||
- Health-Endpunkt
|
- PostgreSQL-Anbindung mit automatischen Migrationen
|
||||||
- saubere Projektstruktur fuer spaetere API-, Worker- und Datenbankmodule
|
- Session-basierte Authentifizierung und rollenbasierte Zugriffskontrolle
|
||||||
- erste serverseitige Aufloesungslogik fuer `message_wall`
|
- Medienverwaltung und Playlist-Management
|
||||||
|
- Player-Status-Ingest und Diagnose
|
||||||
|
- MQTT-Notifizierungen bei Playlist-Aenderungen
|
||||||
|
|
||||||
Geplante Unterstruktur:
|
## Unterstruktur
|
||||||
|
|
||||||
- `cmd/api/` fuer den API-Startpunkt
|
- `cmd/api/` — Startpunkt des Backends
|
||||||
- `internal/app/` fuer App-Initialisierung
|
- `internal/app/` — App-Initialisierung und Lifecycle
|
||||||
- `internal/campaigns/` fuer Kampagnen- und Template-Logik
|
- `internal/config/` — Konfiguration via Umgebungsvariablen
|
||||||
- `internal/httpapi/` fuer HTTP-Routing und Handler
|
- `internal/db/` — PostgreSQL-Anbindung und Migrations-Runner
|
||||||
- `internal/config/` fuer Konfiguration
|
- `internal/store/` — Datenbankzugriff (TenantStore, ScreenStore, MediaStore, PlaylistStore, AuthStore)
|
||||||
|
- `internal/httpapi/` — HTTP-Routing, Middleware und Handler
|
||||||
|
- `internal/httpapi/manage/` — Admin-UI und Playlist-Management-UI
|
||||||
|
- `internal/httpapi/tenant/` — Tenant-Self-Service-Dashboard
|
||||||
|
- `internal/mqttnotifier/` — MQTT-Notifizierungen
|
||||||
|
- `internal/reqcontext/` — Context-Keys fuer authentifizierten User
|
||||||
|
|
||||||
Aktuell vorhanden:
|
## Aktuelle Endpunkte
|
||||||
|
|
||||||
- `GET /healthz`
|
### Oeffentlich (keine Auth)
|
||||||
- `GET /api/v1`
|
|
||||||
- `GET /api/v1/meta`
|
| Methode | Pfad | Beschreibung |
|
||||||
- `POST /api/v1/tools/message-wall/resolve` als erste serverseitige Layout-Aufloesung fuer `message_wall`
|
|---------|-------------------------------------|---------------------------------------|
|
||||||
- einheitliches API-Fehlerformat im HTTP-Layer
|
| GET | `/healthz` | Health-Check |
|
||||||
|
| GET | `/api/v1` | API-Entrypoint |
|
||||||
|
| GET | `/api/v1/meta` | Metainformationen |
|
||||||
|
| POST | `/api/v1/player/status` | Status-Ingest 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/{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) |
|
||||||
|
|
||||||
|
### 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 |
|
||||||
|
|
||||||
|
### 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` |
|
||||||
|
| `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_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`.
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue