# Info-Board Neu - Datenmodell ## Ziel Das Datenmodell trennt klar zwischen: - Mandanten und Benutzern - Bildschirmen und ihrer technischen Laufzeitinformation - Medien und Playlists - Steuerbefehlen und Rueckmeldungen Das Modell ist so ausgelegt, dass: - jede Firma nur ihren eigenen Monitor bzw. Kanal pflegt - die Administration alle Monitore zentral sehen und steuern kann - Offline-Betrieb der Player moeglich bleibt - Zeitsteuerung mit `valid_from` und `valid_until` sauber abbildbar ist ## Kernentitaeten ### `tenant` Repraesentiert eine Firma bzw. einen abgeschotteten Inhaltsbereich. Felder: - `id` - `slug` - `name` - `active` - `created_at` - `updated_at` Hinweise: - ein Tenant kann spaeter mehrere Screens haben, auch wenn zunaechst meist 1:1 gearbeitet wird - alle Medien und Playlists sind einem Tenant zugeordnet ### `user` Repraesentiert einen Benutzer der Firmen- oder Admin-Oberflaeche. Felder: - `id` - `tenant_id` nullable fuer globale Admins - `username` - `email` - `password_hash` - `role` (`admin`, `tenant_user`) - `active` - `last_login_at` - `created_at` - `updated_at` Regeln: - `tenant_user` darf nur Daten des eigenen Tenants sehen und bearbeiten - `admin` darf alle Tenants und alle Screens verwalten ### `screen` Repraesentiert einen physischen Monitor bzw. einen Player-Client. Felder: - `id` - `tenant_id` - `slug` - `name` - `description` - `location` - `hardware_name` - `enabled` - `rotation` (`0`, `90`, `180`, `270`) - `resolution_width` - `resolution_height` - `fallback_dir` - `snapshot_interval_seconds` - `offline_overlay_enabled` - `created_at` - `updated_at` Hinweise: - der `slug` dient als technische Kennung fuer Konfiguration, API und MQTT - `fallback_dir` beschreibt die lokale oder synchronisierte Fallback-Quelle auf dem Client ### `screen_registration` Optionale Trennung zwischen fachlichem Screen und technischer Registrierung. Felder: - `id` - `screen_id` - `device_uuid` - `hostname` - `api_token_hash` - `mqtt_client_id` - `last_seen_at` - `last_ip` - `player_version` - `os_version` - `created_at` - `updated_at` Zweck: - Wiedererkennung eines konkreten Geraets - sichere Kommunikation mit API und Broker - technische Inventarisierung ### `provisioning_job` Repraesentiert einen Provisionierungslauf fuer einen neuen oder neu aufzubauenden Screen. Felder: - `id` - `screen_id` - `requested_by_user_id` - `target_ip` - `target_port` - `remote_user` - `auth_mode` (`password`, `key`) - `provided_secret_ref` - `ssh_key_fingerprint` nullable - `status` (`queued`, `running`, `succeeded`, `failed`, `cancelled`) - `stage` (`connect`, `bootstrap`, `key_install`, `package_install`, `deploy`, `verify`, `done`) - `log_excerpt` nullable - `error_message` nullable - `started_at` nullable - `finished_at` nullable - `created_at` - `updated_at` Regeln: - das eigentliche Passwort wird nicht im Klartext in der Datenbank gespeichert - `provided_secret_ref` verweist auf einen kurzlebigen oder extern geschuetzten Secret-Eintrag - pro Job soll klar erkennbar sein, in welchem Schritt ein Fehler aufgetreten ist ### `screen_group` Optionale logische Gruppierung von Screens. Felder: - `id` - `slug` - `name` - `description` - `created_at` - `updated_at` ### `screen_group_member` Zuordnung eines Screens zu einer Gruppe. Felder: - `id` - `screen_group_id` - `screen_id` - `created_at` Zweck: - praktische Zielauswahl fuer Kampagnen und Provisionierungsaktionen ### `media_asset` Repraesentiert ein verwaltetes Medium. Felder: - `id` - `tenant_id` - `screen_id` nullable - `title` - `description` - `type` (`image`, `video`, `pdf`, `web`) - `source_kind` (`upload`, `remote_url`) - `storage_path` nullable bei reinen Web-URLs - `original_url` nullable - `mime_type` - `checksum` - `size_bytes` - `enabled` - `created_by_user_id` - `created_at` - `updated_at` Regeln: - `upload` bedeutet: Datei liegt im Medien-Storage - `remote_url` bedeutet: Quelle ist extern und wird vom Player oder Server gecacht - `screen_id` kann optional gesetzt werden, wenn ein Medium nur fuer einen Monitor gedacht ist ### `playlist` Repraesentiert eine logische Playlist eines Screens. Felder: - `id` - `tenant_id` - `screen_id` - `name` - `is_active` - `default_duration_seconds` - `fallback_enabled` - `fallback_dir` - `shuffle_enabled` - `created_at` - `updated_at` Regeln: - pro Screen gibt es in v1 genau eine aktive Playlist - spaeter koennen mehrere Playlists mit Umschaltung moeglich werden ### `display_template` Repraesentiert ein globales Admin-Template zur monitoruebergreifenden Orchestrierung. Beispiele: - Schriftzug ueber mehrere Monitore - saisonales Motiv auf allen Monitoren - Veranstaltungslayout fuer einen befristeten Zeitraum Felder: - `id` - `slug` - `name` - `description` - `template_type` (`message_wall`, `full_screen_media`, `screen_specific_scene`) - `enabled` - `created_by_user_id` - `created_at` - `updated_at` ### `template_scene` Repraesentiert die konkrete Auspraegung eines Templates fuer einen oder mehrere Screens. Felder: - `id` - `display_template_id` - `screen_id` nullable - `screen_slot` nullable - `type` (`image`, `video`, `pdf`, `web`, `html`) - `src` - `duration_seconds` - `load_timeout_seconds` - `cache_policy` - `on_error` - `layout_json` - `created_at` - `updated_at` Hinweise: - `screen_id` erlaubt die direkte Zuweisung zu einem konkreten Monitor - `screen_slot` erlaubt spaeter abstrakte Wand-Slots wie `row1-col2` - `layout_json` kann z. B. Textpositionen, Farben oder Teilbereiche des Gesamtmotivs beschreiben ### `campaign` Repraesentiert eine aktivierbare globale Kampagne auf Basis eines Templates. Felder: - `id` - `display_template_id` - `name` - `description` - `priority` - `active` - `valid_from` nullable - `valid_until` nullable - `override_mode` (`replace_tenant_content`, `replace_all`, `merge_reserved_slots`) - `created_by_user_id` - `created_at` - `updated_at` Regeln: - Kampagnen ueberschreiben in v1 standardmaessig den Firmencontent - nach Ende oder Deaktivierung greift automatisch wieder der tenantbezogene Normalbetrieb ### `template_assignment` Repraesentiert die Zuordnung einer Kampagne oder eines Templates zu einem oder mehreren Screens. Felder: - `id` - `campaign_id` - `screen_id` - `enabled` - `created_at` - `updated_at` Zweck: - Auswahl einzelner Monitore, Gruppen oder aller Monitore ### `playlist_item` Repraesentiert einen einzelnen Eintrag in einer Playlist. Felder: - `id` - `playlist_id` - `screen_id` - `order_index` - `media_asset_id` nullable - `type` (`image`, `video`, `pdf`, `web`, `dir`) - `src` - `title` - `duration_seconds` - `load_timeout_seconds` - `cache_policy` (`required`, `prefer_cache`, `no_cache`) - `on_error` (`skip`, `retry`, `fallback`) - `retry_count` - `valid_from` nullable - `valid_until` nullable - `enabled` - `created_at` - `updated_at` Regeln: - `media_asset_id` wird bei verwalteten Medien gesetzt - `src` enthaelt den effektiven Pfad oder die URL, damit der Player auch ohne Join-Struktur arbeiten kann - `type=dir` erlaubt definierte Verzeichnisreferenzen innerhalb der Playlist ### `playlist_item_dir_rule` Optionale Zusatzregel fuer `type=dir`. Felder: - `id` - `playlist_item_id` - `directory_path` - `sort_mode` (`name_asc`, `name_desc`, `mtime_asc`, `mtime_desc`, `shuffle`) - `per_item_duration_seconds` - `recursive` - `file_filter` nullable Zweck: - sauberere Modellierung fuer Verzeichnisbasierte Fallbacks oder Einschuebe ### `screen_status` Repraesentiert den letzten bekannten Laufzeitstatus eines Players. Felder: - `screen_id` - `online` - `server_connected` - `mqtt_connected` - `last_heartbeat_at` - `last_sync_at` - `current_playlist_id` - `current_playlist_item_id` nullable - `current_content_source` (`campaign`, `tenant_playlist`, `fallback`) - `current_campaign_id` nullable - `current_item_type` nullable - `current_item_label` nullable - `current_item_started_at` nullable - `current_item_duration_seconds` nullable - `cache_state` (`ok`, `stale`, `missing`, `error`) - `overlay_state` (`online`, `degraded`, `offline`) - `error_code` nullable - `error_message` nullable - `player_version` - `uptime_seconds` - `free_disk_bytes` nullable - `temperature_celsius` nullable - `updated_at` Hinweise: - `screen_status` ist eine verdichtete Sicht fuer Dashboard und Ueberwachung - historische Verlaeufe koennen spaeter separat gespeichert werden ### `screen_snapshot` Repraesentiert eine Vorschauaufnahme des aktuellen Bildschirminhalts. Felder: - `id` - `screen_id` - `captured_at` - `storage_path` - `width` - `height` - `mime_type` - `source` (`scheduled`, `item_change`, `manual`) Regeln: - in der UI wird typischerweise nur der letzte Snapshot angezeigt - aeltere Snapshots koennen nach Zeit oder Anzahl aufgeraeumt werden ### `device_command` Repraesentiert einen an einen Screen gesendeten Befehl. Felder: - `id` - `screen_id` - `command_type` (`reload`, `restart_player`, `reboot`, `display_on`, `display_off`, `refresh_snapshot`, `clear_cache`) - `payload_json` - `requested_by_user_id` - `requested_at` - `delivery_state` (`queued`, `sent`, `acknowledged`, `failed`, `expired`) - `delivered_at` nullable - `acknowledged_at` nullable - `result_code` nullable - `result_message` nullable Zweck: - nachvollziehbare Fernsteuerung mit Audit-Trail ### `sync_state` Repraesentiert den Synchronisationsstand eines Screens. Felder: - `screen_id` - `config_revision` - `playlist_revision` - `media_revision` - `last_successful_sync_at` - `last_failed_sync_at` nullable - `last_error_message` nullable Zweck: - Erkennung, ob ein Player auf dem aktuellen Stand ist ## Beziehungen - ein `tenant` hat viele `users` - ein `tenant` hat viele `screens` - ein `tenant` hat viele `media_assets` - ein `screen` hat genau eine aktive `playlist` in v1 - eine `playlist` hat viele `playlist_items` - ein `display_template` hat viele `template_scenes` - ein `display_template` hat viele `campaigns` - eine `campaign` hat viele `template_assignments` - ein `screen` hat viele `provisioning_jobs` - eine `screen_group` hat viele `screen_group_members` - ein `screen` hat genau einen aktuellen `screen_status` - ein `screen` hat viele `screen_snapshots` - ein `screen` hat viele `device_commands` - ein `screen` hat genau einen aktuellen `sync_state` ## Zugriffsregeln ### Tenant-User Ein `tenant_user` darf: - nur den eigenen `tenant` sehen - nur den eigenen `screen` bzw. die dem Tenant zugeordneten Screens sehen - nur eigene Medien hochladen und pflegen - nur eigene Playlists bearbeiten - nur die Vorschau des eigenen Screens sehen Ein `tenant_user` darf nicht: - andere Tenants sehen - globale Steuerkommandos senden - andere Screens administrieren ### Admin Ein `admin` darf: - alle Tenants, Screens, Medien und Playlists sehen - globale Steuerbefehle senden - Vorschau und Status aller Screens sehen - neue Screens und Benutzer anlegen ## Revisions- und Caching-Modell Damit Offline-Betrieb einfach bleibt, arbeitet der Player nicht gegen beliebige Einzelobjekte, sondern gegen Revisionen. Sinnvoll sind mindestens: - `playlist_revision` - `media_revision` - `config_revision` - `campaign_revision` Der Player kann damit erkennen: - ob neue Konfiguration vorliegt - ob Medien nachgeladen werden muessen - ob die lokale Kopie noch gueltig ist - ob sich globale Uebersteuerungen geaendert haben ## Prioritaetsmodell der Inhaltsauswahl Der Player wertet Inhalte in dieser Reihenfolge aus: 1. aktive Kampagne mit gueltigem Zeitfenster und Assignment fuer den Screen 2. aktive tenantbezogene Playlist des Screens 3. Fallback-Verzeichnis Eine Kampagne ist aktiv, wenn: - `active = true` - `valid_from` leer oder erreicht ist - `valid_until` leer oder noch nicht abgelaufen ist - eine `template_assignment` fuer den Screen aktiv ist Dadurch ist die globale Uebersteuerung ein Kernbestandteil und kein nachtraeglicher Sonderfall. ## Beispiel fuer aktive Playlist-Auswertung Ein `playlist_item` ist aktiv, wenn: - `enabled = true` - `valid_from` leer oder in der Vergangenheit liegt - `valid_until` leer oder in der Zukunft liegt Wenn kein aktives Item existiert: - greift die globale Fallback-Regel des Screens oder der Playlist ## Beispiel fuer Admin-Steuerung Wenn ein Admin `reload` ausloest: 1. Ein `device_command` wird gespeichert 2. der Befehl wird per MQTT an den Screen signalisiert 3. der Player fuehrt den Reload aus 4. der Player sendet ein ACK 5. `device_command.delivery_state` wird aktualisiert ## Offene spaetere Erweiterungen - mehrere Screens pro Tenant in der Firmenoberflaeche - Vorlagen und globale Playlists - Historie der Statusdaten als Zeitreihe - Geplante Kampagnen mit Prioritaeten - serverseitige Konvertierung oder Transkodierung von Medien