Triff verbindliche Architekturentscheidungen

This commit is contained in:
Jesko Anschütz 2026-03-22 13:35:41 +01:00
parent ae183d399e
commit 7befa61805
7 changed files with 246 additions and 1 deletions

View file

@ -20,6 +20,7 @@ Die Trennung von `/srv/docker/infoboard-netboot` ist sinnvoll, damit:
- Provisionierungskonzept: `docs/PROVISIONIERUNGSKONZEPT.md` - Provisionierungskonzept: `docs/PROVISIONIERUNGSKONZEPT.md`
- Player-Konzept: `docs/PLAYER-KONZEPT.md` - Player-Konzept: `docs/PLAYER-KONZEPT.md`
- Server-Konzept: `docs/SERVER-KONZEPT.md` - Server-Konzept: `docs/SERVER-KONZEPT.md`
- Offene Architekturfragen: `docs/OFFENE-ARCHITEKTURFRAGEN.md`
## Projektstruktur ## Projektstruktur

View file

@ -19,6 +19,8 @@
- [ ] Kommandokatalog fuer Admin-Aktionen finalisieren - [ ] Kommandokatalog fuer Admin-Aktionen finalisieren
- [ ] Template- und Kampagnenmodell fuer globale monitoruebergreifende Uebersteuerung finalisieren - [ ] Template- und Kampagnenmodell fuer globale monitoruebergreifende Uebersteuerung finalisieren
- [ ] Prioritaetsregel `campaign > tenant_playlist > fallback` verbindlich festschreiben - [ ] Prioritaetsregel `campaign > tenant_playlist > fallback` verbindlich festschreiben
- [x] Entscheidung dokumentieren, dass `playlist_items.screen_id` entfernt wird
- [x] Entscheidung dokumentieren, dass Gruppen bei Kampagnen serverseitig in Einzel-Assignments expandiert werden
## Phase 2 - Technische Zielarchitektur ## Phase 2 - Technische Zielarchitektur
@ -32,6 +34,10 @@
- [ ] API fuer Templates, Kampagnen, Aktivierung und Deaktivierung ausarbeiten - [ ] API fuer Templates, Kampagnen, Aktivierung und Deaktivierung ausarbeiten
- [ ] Provisionierungs-Workflow fuer neue Screens technisch durchplanen - [ ] Provisionierungs-Workflow fuer neue Screens technisch durchplanen
- [ ] Secret-Handling fuer initiale Root-Passwoerter oder Bootstrap-Zugaenge definieren - [ ] Secret-Handling fuer initiale Root-Passwoerter oder Bootstrap-Zugaenge definieren
- [x] API-Fehlermodell und gemeinsame Fehlerantworten festlegen
- [x] ACK-Timeout-Strategie fuer `device_command` festlegen
- [x] `message_wall`-Rendering serverseitig verbindlich entscheiden
- [x] Netzwerktopologie fuer SSH-basierte Erstprovisionierung als Worker-/Jumphost-faehiges Modell festlegen
## Phase 3 - Player-Design ## Phase 3 - Player-Design

View file

@ -0,0 +1,177 @@
# Info-Board Neu - Architekturentscheidungen vor dem ersten Code
## Ziel
Dieses Dokument sammelt die Punkte, die vor dem Einstieg in die Implementierung bewusst entschieden werden mussten.
Es geht nicht um offene Detailfragen fuer spaeter, sondern um Entscheidungen mit Einfluss auf Datenmodell, API, Player-Verhalten und Admin-UI.
Stand: Diese Punkte sind fuer v1 entschieden.
## 1. `message_wall`-Rendering
### Frage
Wer berechnet die Aufteilung eines Gesamtmotivs oder Schriftzugs auf die beteiligten Displays?
Moegliche Varianten:
- serverseitig beim Aktivieren einer Kampagne
- playerseitig anhand von Slot-Metadaten
### Entscheidung
Fuer v1 wird `message_wall` serverseitig in konkrete Screen-Szenen aufgeloest.
### Begruendung
- weniger Komplexitaet im Player
- identische Berechnung fuer alle beteiligten Screens
- einfachere Vorschau im Admin-Backend
- Kampagnen werden vor Aktivierung bereits in konkrete Zielinhalte ueberfuehrt
### Konsequenz
- `message_wall` wird im Backend oder Worker in konkrete `template_scenes` pro Slot/Screen transformiert
- der Player bekommt nur noch seinen eigenen fertigen Ausschnitt
- `layout_json` muss trotzdem klar definiert werden, dient dann aber primaer der Serverlogik und UI-Vorschau
## 2. Secret-Handling fuer Provisionierung
### Frage
Wie werden initiale Root-Passwoerter oder Bootstrap-Secrets verarbeitet, ohne sie unkontrolliert in der Datenbank liegen zu lassen?
### Entscheidung
Separate Secret-Verwaltung mit kurzer Lebensdauer:
- Secret nicht direkt in `provisioning_jobs`
- stattdessen separater Secret-Eintrag mit TTL
- verschluesselt mit App-Key
- automatische Loeschung nach Abschluss oder Abbruch des Jobs
### V1-Loesung fuer dieses Projekt
- Tabelle fuer kurzlebige Provisionierungs-Secrets
- AES-verschluesselte Speicherung via Server-App-Key
- Referenz ueber `provided_secret_ref`
- Cleanup-Job entfernt Secrets nach erfolgreicher Provisionierung oder nach Ablauf
## 3. ACK-Timeout fuer Kommandos
### Frage
Wann wird ein `device_command` ohne ACK als fehlgeschlagen oder abgelaufen markiert?
### Entscheidung
Ein serverseitiger Worker uebernimmt Timeouts und Statusuebergaenge.
Vorgeschlagenes Verhalten:
- `queued` -> `sent`
- falls innerhalb eines konfigurierten Zeitfensters kein ACK kommt: `expired`
- falls technische Zustellung fehlschlaegt: `failed`
### Startwerte fuer v1
- `reload`, `refresh_snapshot`: 30 Sekunden
- `restart_player`, `display_on`, `display_off`: 60 Sekunden
- `reboot`: 120 Sekunden
Wenn nach Ablauf kein ACK eingegangen ist, wird der Befehl auf `expired` gesetzt.
### Konsequenz
- es braucht einen periodischen Background-Job
- ACK-Timeouts werden nicht im Web-Request, sondern im Worker behandelt
## 4. Netzwerktopologie fuer Erstprovisionierung
### Frage
Kann der zentrale Server die Zielgeraete bei der Erstinstallation direkt per SSH erreichen?
### Entscheidung
Das wird als harte Betriebsannahme explizit dokumentiert und geprueft.
Moegliche Betriebsmodelle:
- Server steht im selben Netz oder VLAN wie die Player
- Server erreicht die Player ueber Routing/Firewall-Freigaben
- Provisionierungs-Worker laeuft auf einem Jumphost im richtigen Netz
V1-Entscheidung:
- die Provisionierung wird so gebaut, dass der Worker auch auf einem separaten Jumphost laufen kann
- direkte SSH-Erreichbarkeit vom Hauptserver ist erlaubt, aber nicht vorausgesetzt
- die Netzposition des Workers wird als Betriebsparameter behandelt
### Konsequenz
Die Provisionierungsarchitektur ist erst dann belastbar, wenn klar ist:
- wo der Worker laeuft
- welche Netze er erreicht
- ob SSH direkt moeglich ist
- ob spaeter ein Jumphost-Konzept benoetigt wird
## 5. Kleine Modellbereinigung vor Implementierung
### `playlist_items.screen_id`
Aktuell ist das Feld fachlich redundant, weil das Item bereits ueber die Playlist einem Screen zugeordnet ist.
Entscheidung:
- das Feld wird aus dem finalen Implementierungsschema entfernt
- die Screen-Zuordnung laeuft ausschliesslich ueber `playlists.screen_id`
### `template_assignments`
Aktuell ist das Schema auf konkrete Screens ausgelegt.
Entscheidung:
- fuer v1 werden Gruppen serverseitig in Einzelzuordnungen expandiert
- laufende Kampagnen werden bei spaeteren Gruppenänderungen nicht automatisch neu berechnet
- falls das spaeter anders gewuenscht ist, braucht es ein bewusstes Reconciliation-Konzept
## 6. API-Fehlermodell
### Frage
Wie sehen konsistente API-Fehlerantworten aus?
### Entscheidung
Ein gemeinsamer Fehlerumschlag fuer alle API-Endpunkte.
Beispiel:
```json
{
"error": {
"code": "screen_not_found",
"message": "Screen existiert nicht",
"details": null
}
}
```
### Konsequenz
- Backend, Frontends und Player koennen Fehler einheitlich behandeln
- Logging und Monitoring werden einfacher
## Ergebnis fuer v1
1. `message_wall` wird serverseitig aufgeloest
2. Provisionierungs-Secrets werden kurzlebig und verschluesselt referenziert gespeichert
3. ACK-Timeouts werden vom Worker ausgewertet und setzen Kommandos auf `expired`
4. Provisionierung wird worker-basiert und jumperfaehig geplant
5. `playlist_items.screen_id` wird im finalen Schema entfernt
6. Gruppen werden bei Kampagnen in Einzel-Assignments expandiert
7. API-Fehler verwenden einen einheitlichen Fehlerumschlag

View file

@ -26,6 +26,12 @@ Technische Leitlinie:
- Jobrunner fuehrt SSH-/Ansible-Schritte aus - Jobrunner fuehrt SSH-/Ansible-Schritte aus
- Ergebnis wird protokolliert und in der UI sichtbar gemacht - Ergebnis wird protokolliert und in der UI sichtbar gemacht
Verbindliche Betriebsannahme:
- der Provisionierungs-Worker muss das Zielgeraet per SSH erreichen koennen
- falls die Player in getrennten Netzen stehen, ist ein Jumphost- oder Routing-Konzept erforderlich
- die Provisionierung wird so gebaut, dass ein separater Worker oder Jumphost unterstuetzt wird
## Eingaben fuer einen neuen Screen ## Eingaben fuer einen neuen Screen
Mindestens erforderlich: Mindestens erforderlich:
@ -184,6 +190,12 @@ Regeln:
- wenn Speicherung noetig ist, nur kurzlebig und stark geschuetzt - wenn Speicherung noetig ist, nur kurzlebig und stark geschuetzt
- besser: nur Referenz auf temporären Secret-Speicher - besser: nur Referenz auf temporären Secret-Speicher
Verbindliche v1-Loesung:
- separate kurzlebige Secret-Verwaltung
- verschluesselte Speicherung mit App-Key
- automatische Loeschung nach Jobabschluss oder TTL-Ablauf
### Verwaltung nach Erstinstallation ### Verwaltung nach Erstinstallation
Nach erfolgreicher Provisionierung gilt: Nach erfolgreicher Provisionierung gilt:
@ -251,6 +263,14 @@ Typische Fehlerfaelle:
- Chromium oder X11 startet nicht - Chromium oder X11 startet nicht
- Player-Agent registriert sich nicht - Player-Agent registriert sich nicht
## Verbindliche Vorgabe fuer den Betrieb
Vor der produktiven Einfuehrung ist umzusetzen oder zu klaeren:
- ob der Server selbst in allen relevanten Player-Netzen steht
- ob ein dedizierter Provisionierungs-Worker in einem passenden Netz benoetigt wird
- ob spaeter mehrere Standorte oder VLANs zu erwarten sind
Zu jedem Fehler soll sichtbar sein: Zu jedem Fehler soll sichtbar sein:
- Stage - Stage

View file

@ -264,7 +264,6 @@ Spalten:
```sql ```sql
id uuid primary key id uuid primary key
playlist_id uuid not null references playlists(id) on delete cascade playlist_id uuid not null references playlists(id) on delete cascade
screen_id uuid not null references screens(id) on delete cascade
media_asset_id uuid null references media_assets(id) on delete set null media_asset_id uuid null references media_assets(id) on delete set null
order_index integer not null order_index integer not null
type text not null type text not null
@ -380,6 +379,7 @@ updated_at timestamptz not null
Zweck: Zweck:
- explizite Zielzuordnung einer Kampagne - explizite Zielzuordnung einer Kampagne
- Gruppen werden in v1 serverseitig in konkrete Screen-Zuordnungen expandiert
Spalten: Spalten:
@ -539,3 +539,9 @@ Ein Playlist-Item ist aktiv, wenn:
- Medienkonvertierung oder serverseitige Derivate - Medienkonvertierung oder serverseitige Derivate
- mehrere aktive Kampagnen mit Kollisionsregeln - mehrere aktive Kampagnen mit Kollisionsregeln
- Slot-Topologien fuer unterschiedlich grosse Wandsysteme - Slot-Topologien fuer unterschiedlich grosse Wandsysteme
## Verbindliche Architekturentscheidungen fuer v1
- `playlist_items` enthalten keinen direkten `screen_id`-Fremdschluessel
- Kampagnengruppen werden serverseitig in `template_assignments` auf konkrete Screens expandiert
- `message_wall` wird nicht im Player segmentiert, sondern serverseitig aufbereitet

View file

@ -231,6 +231,12 @@ Stattdessen:
- dedizierter Worker/Jobrunnner arbeitet ihn ab - dedizierter Worker/Jobrunnner arbeitet ihn ab
- Fortschritt wird in DB gespeichert - 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 ## Docker-Compose-Zielbild
Sinnvolle Komponenten in `compose/`: Sinnvolle Komponenten in `compose/`:
@ -248,6 +254,22 @@ Sinnvolle Komponenten in `compose/`:
- alle Admin-Kommandos auditieren - alle Admin-Kommandos auditieren
- Tenant-Trennung strikt serverseitig erzwingen - 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 ## Zielstruktur im Repo
Empfohlene Unterstruktur fuer den Server: Empfohlene Unterstruktur fuer den Server:

View file

@ -36,6 +36,11 @@ Technische Anforderungen:
- definierte Zielgruppe von Screens - definierte Zielgruppe von Screens
- klare Zuordnung `welcher Ausschnitt auf welchem Screen` - klare Zuordnung `welcher Ausschnitt auf welchem Screen`
Verbindliche Entscheidung fuer v1:
- serverseitige Aufloesung in konkrete Screen-Szenen statt playerseitiger Segmentberechnung
- der Player erhaelt nur den fuer ihn fertigen Ausschnitt
### `full_screen_media` ### `full_screen_media`
Dieser Typ dient fuer ein identisches oder pro Zielgeraet passend skaliertes Vollbildmotiv. Dieser Typ dient fuer ein identisches oder pro Zielgeraet passend skaliertes Vollbildmotiv.
@ -275,3 +280,11 @@ Von Beginn an mitzudenken sind:
- moegliche getrennte Assets fuer Portrait und Landscape - moegliche getrennte Assets fuer Portrait und Landscape
Damit ist die globale Orchestrierung ein tragender Bestandteil des Systems und kein nachtraeglicher Sonderbau. Damit ist die globale Orchestrierung ein tragender Bestandteil des Systems und kein nachtraeglicher Sonderbau.
## Verbindliche Vorgabe fuer v1
Vor der UI- und Render-Implementierung gilt:
- `message_wall` wird serverseitig in konkrete Zielinhalte aufgeloest
- `layout_json` beschreibt die serverseitige Segmentlogik und Admin-Vorschau
- Slots werden geometrisch serverseitig interpretiert, nicht im Player berechnet