# 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 ### `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 ### `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_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 `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` Der Player kann damit erkennen: - ob neue Konfiguration vorliegt - ob Medien nachgeladen werden muessen - ob die lokale Kopie noch gueltig ist ## 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