docs: spec user-spezifische Medien für Restricted Users
This commit is contained in:
parent
522f15c3cd
commit
6d74a4aa30
1 changed files with 117 additions and 0 deletions
|
|
@ -0,0 +1,117 @@
|
||||||
|
# User-spezifische Medien für Restricted Users — Design
|
||||||
|
|
||||||
|
**Datum:** 2026-03-28
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
|
||||||
|
Restricted Users können aktuell alle Medien des Tenants sehen und löschen. Sie sollen nur ihre eigenen Medien sehen und verwalten dürfen.
|
||||||
|
|
||||||
|
## Anforderungen
|
||||||
|
|
||||||
|
- **Restricted Users** sehen ausschließlich Medien, die sie selbst hochgeladen haben
|
||||||
|
- **Restricted Users** dürfen nur ihre eigenen Medien löschen
|
||||||
|
- **Admins** sehen alle Medien des Tenants — inkl. solcher ohne Besitzer (Legacy-Medien)
|
||||||
|
- **screen_users** sehen alle Medien des Tenants (unverändert)
|
||||||
|
- Bestehende Medien (ohne Besitzer) bleiben erhalten und funktionieren weiter
|
||||||
|
|
||||||
|
## Ansatz: Filter im Store-Layer
|
||||||
|
|
||||||
|
Ownership wird als Datenbankfeld gespeichert. Die Filter-Logik liegt im Store-Layer, Handlers delegieren nach Rolle. Entspricht dem bestehenden pgx/raw-SQL-Muster (analog K4-Checks in playlist.go).
|
||||||
|
|
||||||
|
## Datenbank
|
||||||
|
|
||||||
|
### Migration
|
||||||
|
|
||||||
|
Neue Spalte in `media_assets`:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
ALTER TABLE media_assets
|
||||||
|
ADD COLUMN created_by_user_id text NULL
|
||||||
|
REFERENCES users(id) ON DELETE SET NULL;
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Nullable**: bestehende Medien behalten `NULL` als Besitzer
|
||||||
|
- **ON DELETE SET NULL**: wird ein User gelöscht, bleibt das Medium erhalten, verliert aber den Besitzer
|
||||||
|
- Kein Backfill nötig — `NULL` = kein Besitzer (Legacy oder Admin-Upload)
|
||||||
|
|
||||||
|
## Go-Datenmodell
|
||||||
|
|
||||||
|
`MediaAsset`-Struct bekommt ein neues Feld:
|
||||||
|
|
||||||
|
```go
|
||||||
|
CreatedByUserID string // leer = kein Besitzer (legacy)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Store-Layer
|
||||||
|
|
||||||
|
### List
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (s *MediaStore) List(ctx context.Context, tenantID, ownerUserID string) ([]MediaAsset, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
- `ownerUserID == ""` → keine Einschränkung, alle Tenant-Medien (Admin, screen_user)
|
||||||
|
- `ownerUserID != ""` → `AND created_by_user_id = $2` (Restricted User)
|
||||||
|
|
||||||
|
### Create
|
||||||
|
|
||||||
|
Kein Signatur-Wandel. `MediaAsset.CreatedByUserID` wird vom Handler befüllt und per Struct übergeben. Bestehende Create-Logik bleibt unverändert.
|
||||||
|
|
||||||
|
## Handler-Layer
|
||||||
|
|
||||||
|
### Upload (API + Manage-Endpoint)
|
||||||
|
|
||||||
|
Beide Upload-Handler befüllen `CreatedByUserID` mit `user.ID`:
|
||||||
|
- `POST /api/v1/tenants/{tenantSlug}/media`
|
||||||
|
- `POST /manage/{screenSlug}/upload`
|
||||||
|
|
||||||
|
### List
|
||||||
|
|
||||||
|
Handler übergibt an `Store.List()`:
|
||||||
|
- `restricted` → `ownerUserID = u.ID`
|
||||||
|
- alle anderen → `ownerUserID = ""`
|
||||||
|
|
||||||
|
### Delete (K3-Check)
|
||||||
|
|
||||||
|
Aktuell: `u.TenantID == asset.TenantID || u.Role == "admin"`
|
||||||
|
|
||||||
|
Neu:
|
||||||
|
|
||||||
|
| Rolle | Bedingung |
|
||||||
|
|---|---|
|
||||||
|
| `admin` | immer erlaubt |
|
||||||
|
| `screen_user` | Tenant-Match reicht (wie bisher) |
|
||||||
|
| `restricted` | Tenant-Match **und** `asset.CreatedByUserID == u.ID` |
|
||||||
|
|
||||||
|
## UI
|
||||||
|
|
||||||
|
### Admin-Ansicht: Badge für Medien ohne Besitzer
|
||||||
|
|
||||||
|
In der Medienliste zeigt der Admin bei Einträgen mit leerem `CreatedByUserID` ein Badge:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<span class="tag is-warning is-light">Kein Besitzer</span>
|
||||||
|
```
|
||||||
|
|
||||||
|
Medien mit Besitzer erhalten kein Badge — der Benutzername wird nicht angezeigt.
|
||||||
|
|
||||||
|
### Restricted-User-Ansicht
|
||||||
|
|
||||||
|
Keine strukturellen Änderungen. Die Liste ist serverseitig gefiltert — der User sieht einfach weniger Einträge.
|
||||||
|
|
||||||
|
## Nicht im Scope
|
||||||
|
|
||||||
|
- Anzeige des Besitzernamens bei Medien
|
||||||
|
- Übertragung von Medien zwischen Usern
|
||||||
|
- Sichtbarkeit von Restricted-User-Medien für screen_users (sie sehen alles im Tenant)
|
||||||
|
|
||||||
|
## Betroffene Dateien
|
||||||
|
|
||||||
|
| Datei | Änderung |
|
||||||
|
|---|---|
|
||||||
|
| `server/backend/db/migrations/00X_media_owner.sql` | neue Spalte `created_by_user_id` |
|
||||||
|
| `server/backend/internal/store/store.go` | `MediaAsset`-Struct, `List()`-Signatur, `Create()` |
|
||||||
|
| `server/backend/internal/httpapi/manage/media.go` | Upload + Delete + List Handler |
|
||||||
|
| `manage/templates.go` | Badge für Medien ohne Besitzer |
|
||||||
|
| `docs/SCHEMA.md` | neue Spalte dokumentieren |
|
||||||
|
| `docs/API-ENDPOINTS.md` | ggf. List-Endpoint-Verhalten dokumentieren |
|
||||||
Loading…
Add table
Reference in a new issue