docs: Dokumentation validiert und korrigiert

- SCHEMA.md: screen_id in user_screen_permissions von uuid auf text korrigiert
- SCHEMA.md: Phasen-Hinweis zur screens-Tabelle hinzugefügt (6 Spalten aktuell implementiert, erweiterte Phase 2-3)
- SERVER-KONZEPT.md: RequireScreenAccess-Middleware dokumentiert inkl. Route-Gruppen und Verhaltens-Details
- server/backend/README.md: Env-Variable DATABASE_URL → MORZ_INFOBOARD_DATABASE_URL korrigiert
- DEVELOPMENT.md: Compose-Stack von "später" auf "existiert bereits" aktualisiert
- API-ENDPOINTS.md: HandlePlayerPlaylist Response um fehlende Felder ergänzt (playlist_id, media_asset_id, order_index, created_at)
- DEVELOPMENT.md: Architekturentscheidungen präzisiert (message_wall=implementiert, Kampagnen=geplant)

Co-Authored-By: Klaus <noreply@example.com>
This commit is contained in:
Jesko Anschütz 2026-03-24 00:10:50 +01:00
parent bb35594211
commit 097cd58c0c
5 changed files with 66 additions and 16 deletions

View file

@ -26,7 +26,7 @@ Projektwurzel:
- `server/backend/` fuer das zentrale Go-Backend - `server/backend/` fuer das zentrale Go-Backend
- `player/agent/` fuer den Go-basierten Player-Agent - `player/agent/` fuer den Go-basierten Player-Agent
- `ansible/` fuer Deployment und Provisionierung - `ansible/` fuer Deployment und Provisionierung
- `compose/` spaeter fuer den zentralen Server-Stack - `compose/` fuer den zentralen Server-Stack
## Aktueller Entwicklungsstand ## Aktueller Entwicklungsstand
@ -287,8 +287,8 @@ Das Playbook erledigt:
## Aktuelle Architekturentscheidungen mit direkter Auswirkung auf Entwicklung ## Aktuelle Architekturentscheidungen mit direkter Auswirkung auf Entwicklung
- `message_wall` wird serverseitig in konkrete Screen-Szenen aufgeloest - `message_wall` wird serverseitig in konkrete Screen-Szenen aufgeloest (implementiert)
- Kampagnengruppen werden serverseitig in konkrete Screen-Assignments expandiert - Kampagnengruppen werden serverseitig in konkrete Screen-Assignments expandiert (geplant)
- `playlist_items` haben im finalen Implementierungsschema keinen direkten `screen_id`-Fremdschluessel - `playlist_items` haben im finalen Implementierungsschema keinen direkten `screen_id`-Fremdschluessel
- Provisionierung wird worker-/jumphost-faehig geplant - Provisionierung wird worker-/jumphost-faehig geplant
- API-Fehler sollen einen einheitlichen Fehlerumschlag nutzen - API-Fehler sollen einen einheitlichen Fehlerumschlag nutzen

View file

@ -204,23 +204,31 @@ Der Player ruft diesen Endpoint auf, um die aktuellen Inhalte zu laden.
"items": [ "items": [
{ {
"id": "uuid...", "id": "uuid...",
"playlist_id": "uuid...",
"media_asset_id": null,
"order_index": 0,
"type": "web", "type": "web",
"src": "http://example.com/page1", "src": "http://example.com/page1",
"title": "Startseite", "title": "Startseite",
"duration_seconds": 30, "duration_seconds": 30,
"enabled": true, "enabled": true,
"valid_from": null, "valid_from": null,
"valid_until": null "valid_until": null,
"created_at": "2026-03-22T16:00:00Z"
}, },
{ {
"id": "uuid...", "id": "uuid...",
"playlist_id": "uuid...",
"media_asset_id": "uuid...",
"order_index": 1,
"type": "image", "type": "image",
"src": "/uploads/banner.jpg", "src": "/uploads/banner.jpg",
"title": "Werbebanner", "title": "Werbebanner",
"duration_seconds": 20, "duration_seconds": 20,
"enabled": true, "enabled": true,
"valid_from": null, "valid_from": null,
"valid_until": null "valid_until": null,
"created_at": "2026-03-22T16:00:00Z"
} }
] ]
} }

View file

@ -76,7 +76,7 @@ Spalten:
```sql ```sql
id uuid primary key id uuid primary key
user_id text not null references users(id) on delete cascade user_id text not null references users(id) on delete cascade
screen_id uuid not null references screens(id) on delete cascade screen_id text not null references screens(id) on delete cascade
created_at timestamptz not null default now() created_at timestamptz not null default now()
unique(user_id, screen_id) unique(user_id, screen_id)
``` ```
@ -139,6 +139,8 @@ Zweck:
- physische Displays bzw. Player-Geraete - physische Displays bzw. Player-Geraete
**Hinweis:** Die Spalten unten beschreiben das geplante Schema (Phase 2-3). Der aktuelle Code (Migration `001_core.sql`) implementiert nur eine Teilmenge: `id`, `tenant_id`, `slug`, `name`, `orientation`, `created_at` (6 Spalten). Weitere Felder werden in späteren Phasen hinzugefügt.
Spalten: Spalten:
```sql ```sql

View file

@ -291,24 +291,64 @@ Eingehende Anfrage
├─► RequireAdmin Prueft user.Role == "admin" ├─► RequireAdmin Prueft user.Role == "admin"
│ → Fehler: 403 Forbidden │ → Fehler: 403 Forbidden
└─► RequireTenant Prueft user.TenantSlug == {tenantSlug} aus dem URL-Pfad. ├─► RequireTenant Prueft user.TenantSlug == {tenantSlug} aus dem URL-Pfad.
Access Admins duerfen immer durch. │ Access Admins duerfen immer durch.
→ Fehler: 403 Forbidden │ → Fehler: 403 Forbidden
└─► RequireScreen Enforces per-screen access control.
Access Admins duerfen auf alle Screens zugreifen.
Screen-User brauchen expliziten Eintrag in `user_screen_permissions`.
Tenant-User duerfen auf alle Screens ihres Tenants zugreifen.
→ Fehler: 404 Not Found (Screen) oder 403 Forbidden (kein Zugriff)
``` ```
### Route-Gruppen im Router ### Route-Gruppen im Router
| Gruppe | Middleware | Beispielrouten | | Gruppe | Middleware | Beispielrouten |
|----------------|------------------------------------|---------------------------------------------| |----------------|----------------------------------------------------|---------------------------------------------|
| Oeffentlich | keine | `/healthz`, `/login`, `/api/v1/screens/register` | | Oeffentlich | keine | `/healthz`, `/login`, `/api/v1/screens/register` |
| Auth-only | RequireAuth | `/manage/{screenSlug}/...` | | Auth-only | RequireAuth | `/api/v1/items/{itemId}`, `/api/v1/media/{id}` |
| Admin-only | RequireAuth + RequireAdmin | `/admin`, `/admin/screens/...` | | Admin-only | RequireAuth + RequireAdmin | `/admin`, `/admin/screens/...` |
| Tenant-scoped | RequireAuth + RequireTenantAccess | `/tenant/{tenantSlug}/...`, `/api/v1/tenants/{tenantSlug}/...` | | Tenant-scoped | RequireAuth + RequireTenantAccess | `/tenant/{tenantSlug}/...`, `/api/v1/tenants/{tenantSlug}/...` |
| Screen-scoped | RequireAuth + RequireScreenAccess | `/manage/{screenSlug}/...`, `/api/v1/screens/{screenId}/playlist` |
Der Hilfsfunktion `chain(middlewares...)` in `router.go` wrappet Handler von aussen nach innen. Der Hilfsfunktion `chain(middlewares...)` in `router.go` wrappet Handler von aussen nach innen.
--- ---
## RequireScreenAccess Middleware
Die Middleware `RequireScreenAccess` erzwingt Zugriffskontrolle auf Screen-Ressourcen und wird ausschliesslich fuer Routen verwendet, deren Handler screen-spezifische Operationen durchfuehren.
**Verhalten:**
- **Admin-User**: duerfen auf alle Screens zugreifen (Bypass).
- **Screen-User**: duerfen nur auf Screens zugreifen, fuer die sie einen expliziten Eintrag in `user_screen_permissions` haben.
- **Tenant-User**: duerfen auf alle Screens ihres Tenants zugreifen (noch nicht vollstaendig implementiert).
**Implementierung:**
Die Middleware extrahiert den `screenSlug` aus dem URL-Pfad-Parameter, schlaegt den Screen in der Datenbank auf und prueft via `ScreenStore.HasUserScreenAccess()`, ob der Nutzer Zugriff hat. Admins umgehen diese Pruefung.
**Fehlerbehandlung:**
- Screen nicht gefunden: `404 Not Found`
- Kein Zugriff auf Screen: `403 Forbidden`
**Verwendung in Router:**
Die `authScreen`-Middleware-Kombination wird fuer folgende Routes verwendet:
- `GET /manage/{screenSlug}` — Playlist-Management-UI
- `POST /manage/{screenSlug}/upload` — Medium hochladen
- `POST /manage/{screenSlug}/items` — Item 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
---
## Tenant-Dashboard ## Tenant-Dashboard
Das Tenant-Self-Service-Dashboard ist unter `/tenant/{tenantSlug}/dashboard` erreichbar. Das Tenant-Self-Service-Dashboard ist unter `/tenant/{tenantSlug}/dashboard` erreichbar.

View file

@ -132,7 +132,7 @@ Alle Werte per Umgebungsvariable:
| Variable | Bedeutung | Standard | | Variable | Bedeutung | Standard |
|-----------------------------------|----------------------------------------------------------|---------------| |-----------------------------------|----------------------------------------------------------|---------------|
| `MORZ_INFOBOARD_HTTP_ADDR` | HTTP-Listen-Adresse | `:8080` | | `MORZ_INFOBOARD_HTTP_ADDR` | HTTP-Listen-Adresse | `:8080` |
| `DATABASE_URL` | PostgreSQL-Connection-String | — | | `MORZ_INFOBOARD_DATABASE_URL` | PostgreSQL-Connection-String | — |
| `MORZ_INFOBOARD_UPLOAD_DIR` | Verzeichnis fuer hochgeladene Medien | `/tmp/morz-uploads` | | `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_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_ADMIN_PASSWORD` | Passwort des initialen Admin-Users (leer = kein Anlegen) | leer |