Triff verbindliche Architekturentscheidungen
This commit is contained in:
parent
ae183d399e
commit
7befa61805
7 changed files with 246 additions and 1 deletions
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
6
TODO.md
6
TODO.md
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
177
docs/OFFENE-ARCHITEKTURFRAGEN.md
Normal file
177
docs/OFFENE-ARCHITEKTURFRAGEN.md
Normal 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
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue