From 7befa61805ebda4f3cb2fd56a564136d5802e2f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesko=20Ansch=C3=BCtz?= Date: Sun, 22 Mar 2026 13:35:41 +0100 Subject: [PATCH] Triff verbindliche Architekturentscheidungen --- README.md | 1 + TODO.md | 6 ++ docs/OFFENE-ARCHITEKTURFRAGEN.md | 177 +++++++++++++++++++++++++++++++ docs/PROVISIONIERUNGSKONZEPT.md | 20 ++++ docs/SCHEMA.md | 8 +- docs/SERVER-KONZEPT.md | 22 ++++ docs/TEMPLATE-KONZEPT.md | 13 +++ 7 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 docs/OFFENE-ARCHITEKTURFRAGEN.md diff --git a/README.md b/README.md index 3359d24..93517c0 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ Die Trennung von `/srv/docker/infoboard-netboot` ist sinnvoll, damit: - Provisionierungskonzept: `docs/PROVISIONIERUNGSKONZEPT.md` - Player-Konzept: `docs/PLAYER-KONZEPT.md` - Server-Konzept: `docs/SERVER-KONZEPT.md` +- Offene Architekturfragen: `docs/OFFENE-ARCHITEKTURFRAGEN.md` ## Projektstruktur diff --git a/TODO.md b/TODO.md index 037d583..31d56dc 100644 --- a/TODO.md +++ b/TODO.md @@ -19,6 +19,8 @@ - [ ] Kommandokatalog fuer Admin-Aktionen finalisieren - [ ] Template- und Kampagnenmodell fuer globale monitoruebergreifende Uebersteuerung finalisieren - [ ] 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 @@ -32,6 +34,10 @@ - [ ] API fuer Templates, Kampagnen, Aktivierung und Deaktivierung ausarbeiten - [ ] Provisionierungs-Workflow fuer neue Screens technisch durchplanen - [ ] 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 diff --git a/docs/OFFENE-ARCHITEKTURFRAGEN.md b/docs/OFFENE-ARCHITEKTURFRAGEN.md new file mode 100644 index 0000000..aa4620c --- /dev/null +++ b/docs/OFFENE-ARCHITEKTURFRAGEN.md @@ -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 diff --git a/docs/PROVISIONIERUNGSKONZEPT.md b/docs/PROVISIONIERUNGSKONZEPT.md index fd6a00d..ec796b6 100644 --- a/docs/PROVISIONIERUNGSKONZEPT.md +++ b/docs/PROVISIONIERUNGSKONZEPT.md @@ -26,6 +26,12 @@ Technische Leitlinie: - Jobrunner fuehrt SSH-/Ansible-Schritte aus - 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 Mindestens erforderlich: @@ -184,6 +190,12 @@ Regeln: - wenn Speicherung noetig ist, nur kurzlebig und stark geschuetzt - 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 Nach erfolgreicher Provisionierung gilt: @@ -251,6 +263,14 @@ Typische Fehlerfaelle: - Chromium oder X11 startet 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: - Stage diff --git a/docs/SCHEMA.md b/docs/SCHEMA.md index cacf46e..16655dd 100644 --- a/docs/SCHEMA.md +++ b/docs/SCHEMA.md @@ -264,7 +264,6 @@ Spalten: ```sql id uuid primary key 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 order_index integer not null type text not null @@ -380,6 +379,7 @@ updated_at timestamptz not null Zweck: - explizite Zielzuordnung einer Kampagne +- Gruppen werden in v1 serverseitig in konkrete Screen-Zuordnungen expandiert Spalten: @@ -539,3 +539,9 @@ Ein Playlist-Item ist aktiv, wenn: - Medienkonvertierung oder serverseitige Derivate - mehrere aktive Kampagnen mit Kollisionsregeln - 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 diff --git a/docs/SERVER-KONZEPT.md b/docs/SERVER-KONZEPT.md index ad57a89..f0b6378 100644 --- a/docs/SERVER-KONZEPT.md +++ b/docs/SERVER-KONZEPT.md @@ -231,6 +231,12 @@ Stattdessen: - dedizierter Worker/Jobrunnner arbeitet ihn ab - 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 Sinnvolle Komponenten in `compose/`: @@ -248,6 +254,22 @@ Sinnvolle Komponenten in `compose/`: - alle Admin-Kommandos auditieren - 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 Empfohlene Unterstruktur fuer den Server: diff --git a/docs/TEMPLATE-KONZEPT.md b/docs/TEMPLATE-KONZEPT.md index d0212e5..321eb46 100644 --- a/docs/TEMPLATE-KONZEPT.md +++ b/docs/TEMPLATE-KONZEPT.md @@ -36,6 +36,11 @@ Technische Anforderungen: - definierte Zielgruppe von Screens - 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` 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 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