Doku ergänzt
This commit is contained in:
62
AGENTS.md
62
AGENTS.md
@@ -14,18 +14,19 @@ docker compose up -d
|
|||||||
|
|
||||||
- Single Flask app in `app.py`; there is no database.
|
- Single Flask app in `app.py`; there is no database.
|
||||||
- Persistent state is `config.json` plus files under `media/<site>/<screen>/`.
|
- Persistent state is `config.json` plus files under `media/<site>/<screen>/`.
|
||||||
|
- User data in `users.json` (hashed passwords via werkzeug.security scrypt).
|
||||||
- Port 5005; `README.md` hat den korrekten Port.
|
- Port 5005; `README.md` hat den korrekten Port.
|
||||||
- Existing repo instructions in this file are the main local guidance; there is no `opencode.json` or workflow config in this repo.
|
- Existing repo instructions in this file are the main local guidance; there is no `opencode.json` or workflow config in this repo.
|
||||||
|
|
||||||
## Multi-Standort-URL-Struktur (seit v4.2.0)
|
## Multi-Standort-URL-Struktur
|
||||||
|
|
||||||
Standorte (sites) sind die oberste Organisationsebene und gruppieren Screens.
|
Standorte (sites) sind die oberste Organisationsebene und gruppieren Screens.
|
||||||
|
|
||||||
| Route | Beschreibung |
|
| Route | Beschreibung |
|
||||||
|-------|-------------|
|
|-------|-------------|
|
||||||
| `GET /player/<site>/<screen>` | Player für Screen an einem Standort |
|
| `GET /player/<site>/<screen>` | Player für Screen an einem Standort |
|
||||||
| `GET /admin` | Redirect zum ersten Standort |
|
| `GET /admin` | Redirect zum ersten zugänglichen Standort |
|
||||||
| `GET /admin/<site>` | Admin-Dashboard für einen Standort |
|
| `GET /admin/<site>` | Admin-Dashboard für einen Standort (`@site_access_required`) |
|
||||||
| `GET /admin/<site>/priority` | Priority-Playlist als separate Seite |
|
| `GET /admin/<site>/priority` | Priority-Playlist als separate Seite |
|
||||||
| `GET /media/<site>/<screen>/<file>` | Medien-Datei ausliefern |
|
| `GET /media/<site>/<screen>/<file>` | Medien-Datei ausliefern |
|
||||||
| `GET /media/priority/<file>` | Priority-Medien (global) |
|
| `GET /media/priority/<file>` | Priority-Medien (global) |
|
||||||
@@ -37,22 +38,57 @@ Standorte (sites) sind die oberste Organisationsebene und gruppieren Screens.
|
|||||||
| `POST /api/customer` | API-Endpunkt (JSON mit "site"-Feld) |
|
| `POST /api/customer` | API-Endpunkt (JSON mit "site"-Feld) |
|
||||||
| `GET /admin/<site>/add-screen?name=<name>` | Neuen Screen anlegen |
|
| `GET /admin/<site>/add-screen?name=<name>` | Neuen Screen anlegen |
|
||||||
| `POST /admin/<site>/delete-screen/<screen>` | Screen + Medien löschen |
|
| `POST /admin/<site>/delete-screen/<screen>` | Screen + Medien löschen |
|
||||||
| `GET /add-site?name=<name>` | Neuen Standort anlegen |
|
| `GET /add-site?name=<name>` | Neuen Standort anlegen (nur Admin) |
|
||||||
| `POST /admin/<site>/update-actions/<screen>` | Aktionen-Einstellungen speichern (Custom-URL + Position) |
|
| `POST /admin/<site>/update-actions/<screen>` | Aktionen-Einstellungen speichern (Custom-URL + Position) |
|
||||||
| `POST /admin/<site>/update-voice/<screen>` | Voice-Agent-Einstellungen speichern (Enabled, Label, Target, Position) |
|
| `POST /admin/<site>/update-voice/<screen>` | Voice-Agent-Einstellungen speichern (Enabled, Label, Target, Position) |
|
||||||
| `POST /admin/<site>/delete-site` | Standort + alle Screens/Medien löschen |
|
| `POST /admin/<site>/delete-site` | Standort + alle Screens/Medien löschen (nur Admin) |
|
||||||
|
| `GET /change-password` | Passwort ändern-Seite |
|
||||||
|
| `GET /admin/users` | User-Liste (nur Admin) |
|
||||||
|
| `GET /admin/users/create` | User anlegen (nur Admin) |
|
||||||
|
| `GET /admin/users/edit/<email>` | User bearbeiten (nur Admin) |
|
||||||
|
|
||||||
|
## User-Verwaltung (seit v5.6.0)
|
||||||
|
|
||||||
|
- Login via E-Mail + Passwort (scrypt-gehasht in `users.json`)
|
||||||
|
- Drei Rollen:
|
||||||
|
- **Admin**: Zugriff auf alle Standorte + Userverwaltung (anlegen/bearbeiten/löschen/Reset)
|
||||||
|
- **Superuser**: Zugriff auf alle Standorte, keine Userverwaltung
|
||||||
|
- **User**: Zugriff nur auf zugewiesene Standorte
|
||||||
|
- Erster Login erfordert Passwort-Änderung (`must_change_password=True`)
|
||||||
|
- Admin-Reset generiert temporäres Passwort (wird im UI angezeigt)
|
||||||
|
- Fehlender/leerer `password_hash` → Login ohne Prüfung, direkt zu `/change-password`
|
||||||
|
- Letzter Admin kann nicht gelöscht werden
|
||||||
|
- `users.json` wird beim ersten Start aus `config.json.admin` befüllt
|
||||||
|
- Notfall: `users.json` löschen → Server-Neustart erzeugt neuen Admin
|
||||||
|
|
||||||
|
### User-Routen
|
||||||
|
|
||||||
|
| Route | Beschreibung |
|
||||||
|
|-------|-------------|
|
||||||
|
| `POST /admin/users/create` | User anlegen |
|
||||||
|
| `POST /admin/users/delete/<email>` | User löschen (letzter Admin geschützt) |
|
||||||
|
| `POST /admin/users/reset-password/<email>` | Passwort-Reset (temporäres Passwort) |
|
||||||
|
| `POST /admin/users/edit/<email>` | User bearbeiten speichern |
|
||||||
|
|
||||||
|
### Zugriffs-Dekoratoren
|
||||||
|
|
||||||
|
| Dekorator | Wirkung |
|
||||||
|
|-----------|---------|
|
||||||
|
| `@login_required` | User muss eingeloggt sein |
|
||||||
|
| `@admin_required` | Nur Admins |
|
||||||
|
| `@site_access_required` | Admin/Superuser → alle Sites, User → nur freigegebene |
|
||||||
|
|
||||||
## Behavior To Preserve
|
## Behavior To Preserve
|
||||||
|
|
||||||
- `GET /player/<site>/<screen>` renders the playlist and auto-reloads from `/playlist/<site>/<screen>/hash`.
|
- `GET /player/<site>/<screen>` renders the playlist and auto-reloads from `/playlist/<site>/<screen>/hash`.
|
||||||
- `GET /admin` requires login; `config.json.admin` holds the credentials.
|
- `GET /admin` requires login; redirects to first accessible site for the user.
|
||||||
- URL playlist items are stored as dicts like `{"url": "https://...", "zoom": 0.8}` and the zoom value must survive save/reorder flows.
|
- URL playlist items are stored as dicts like `{"url": "https://...", "zoom": 0.8}` and the zoom value must survive save/reorder flows.
|
||||||
- `.html` items in `media/` are rendered inline as content, not in an iframe.
|
- `.html` items in `media/` are rendered inline as content, not in an iframe.
|
||||||
- `config.priority.enabled` makes the priority playlist show on every screen.
|
- `config.priority.enabled` makes the priority playlist show on every screen.
|
||||||
- `POST /api/customer` generates `welcome.html` and inserts it at the front of the lobby playlist for the specified site.
|
- `POST /api/customer` generates `welcome.html` and inserts it at the front of the lobby playlist for the specified site.
|
||||||
- New standorte can be added by creating `media/<neuer-standort>/<screen>/` directories and optionally adding config to `config.json["sites"][<neuer-standort>]`.
|
- New standorte can be added by creating `media/<neuer-standort>/<screen>/` directories and optionally adding config to `config.json["sites"][<neuer-standort>]`.
|
||||||
- Priority playlist (`config.priority`) is global and affects all sites/screens.
|
- Priority playlist (`config.priority`) is global and affects all sites/screens.
|
||||||
- Willkommensseite (`customer.html`) accepts up to 3 customer names; logos are fetched via OpenAI→Brandfetch and displayed in a flex row.
|
- Willkommensseite (`customer.html`) accepts up to 3 customer names; logos are fetched via OpenAI→Brandfetch and displayed in a flex row (portrait: column).
|
||||||
- `generate_welcome_html(customer_names, logo_urls)` takes lists for up to 3 customers; logos have equal width (280px) with `max-height: 180px`.
|
- `generate_welcome_html(customer_names, logo_urls)` takes lists for up to 3 customers; logos have equal width (280px) with `max-height: 180px`.
|
||||||
- `customer_names` are preserved in form fields after POST (via `value`-Attribute).
|
- `customer_names` are preserved in form fields after POST (via `value`-Attribute).
|
||||||
- Admin-UI nutzt keyadmin-Design: `brand-surface` (#2b2f36), `nav-surface` (rot #DA002D), Dark Mode per `localStorage("signage-theme")`.
|
- Admin-UI nutzt keyadmin-Design: `brand-surface` (#2b2f36), `nav-surface` (rot #DA002D), Dark Mode per `localStorage("signage-theme")`.
|
||||||
@@ -67,20 +103,25 @@ Standorte (sites) sind die oberste Organisationsebene und gruppieren Screens.
|
|||||||
- `voice_agent_show_image` (per Screen): optionales Bild (`static/dva.png`) über dem Button, per Admin-Toggle schaltbar (Beta).
|
- `voice_agent_show_image` (per Screen): optionales Bild (`static/dva.png`) über dem Button, per Admin-Toggle schaltbar (Beta).
|
||||||
- Action-Button (custom_url) und Voice-Agent-Button haben einheitliches Styling: weißer Hintergrund, schwarzer Text, 20px Border-Radius, fette Schrift.
|
- Action-Button (custom_url) und Voice-Agent-Button haben einheitliches Styling: weißer Hintergrund, schwarzer Text, 20px Border-Radius, fette Schrift.
|
||||||
|
|
||||||
- Neue Standorte können über den `+`-Button im Header oder über `GET /add-site?name=<name>` angelegt werden.
|
- Neue Standorte können über den `+`-Button im Header oder über `GET /add-site?name=<name>` angelegt werden (nur Admins).
|
||||||
- `delete_site` entfernt den Standort aus Config und löscht das Medienverzeichnis rekursiv.
|
- `delete_site` entfernt den Standort aus Config und löscht das Medienverzeichnis rekursiv (nur Admins).
|
||||||
- `config.json["server_url"]` (z. B. `http://signage.ccmake.de`) wird in der Admin-Ansicht für die Player-URLs verwendet.
|
- `config.json["server_url"]` (z. B. `http://signage.ccmake.de`) wird in der Admin-Ansicht für die Player-URLs verwendet.
|
||||||
- Screen-Card-Body hat Tabler-Tabs: **Playlist** (1, aktiv), **Einstellungen** (2), **Aktionen** (3), **Digital Voice Agent** (4), **Medien** (5); Priority-Seite ebenfalls Tabs **Playlist** und **Medien**.
|
- Screen-Card-Body hat Tabler-Tabs: **Playlist** (1, aktiv), **Einstellungen** (2), **Aktionen** (3), **Digital Voice Agent** (4), **Medien** (5); Priority-Seite ebenfalls Tabs **Playlist** und **Medien**.
|
||||||
- `stay_on_first`: Wenn aktiviert bleibt der Player auf dem ersten Playlist-Element stehen (kein Durchlauf).
|
- `stay_on_first`: Wenn aktiviert bleibt der Player auf dem ersten Playlist-Element stehen (kein Durchlauf).
|
||||||
- Tab-Reihenfolge in Screen-Cards: Playlist → Einstellungen → Aktionen → Digital Voice Agent → Medien.
|
- Tab-Reihenfolge in Screen-Cards: Playlist → Einstellungen → Aktionen → Digital Voice Agent → Medien.
|
||||||
- Player-URL im Screen-Header ist ein klickbarer Link in grauer Farbe.
|
- Player-URL im Screen-Header ist ein klickbarer Link in grauer Farbe.
|
||||||
|
- `add_customer` und `/customer` erfordern jetzt Login (`@login_required`) mit Site-Zugriffsprüfung.
|
||||||
|
- `/admin` redirectet zum ersten für den User zugänglichen Standort (nicht mehr global ersten).
|
||||||
|
- User-Dropdown im Header: zeigt E-Mail + Role-Badge, Menü mit Passwort ändern, Userverwaltung (Admin), Abmelden.
|
||||||
|
|
||||||
## Repo Quirks
|
## Repo Quirks
|
||||||
|
|
||||||
- `media/` and media file extensions are gitignored.
|
- `media/` and media file extensions are gitignored.
|
||||||
|
- `users.json` is NOT gitignored (tracked for initial admin setup, contains no secrets by default).
|
||||||
- The app has no configured tests, lint, typecheck, formatter, or CI.
|
- The app has no configured tests, lint, typecheck, formatter, or CI.
|
||||||
- Hardcoded secrets exist in tracked files; do not commit new secrets or reshuffle them casually.
|
- Hardcoded secrets exist in tracked files; do not commit new secrets or reshuffle them casually.
|
||||||
- Wichtige Helper-Funktionen in `app.py`: `load_config()`, `save_config()`, `get_site_list()`, `get_screen_config()`, `is_url()`, `normalize_url()`, `playlist_item_name()`, `playlist_item_enabled()`, `load_priority_files()`, `prio_redirect()`, `get_background_url()`.
|
- Wichtige Helper-Funktionen in `app.py`: `load_config()`, `save_config()`, `get_site_list()`, `get_screen_config()`, `is_url()`, `normalize_url()`, `playlist_item_name()`, `playlist_item_enabled()`, `load_priority_files()`, `prio_redirect()`, `get_background_url()`, `load_users()`, `save_users()`, `get_user()`, `init_user_db()`, `get_accessible_sites()`.
|
||||||
|
- Zugriffs-Dekoratoren in `app.py`: `admin_required`, `site_access_required`.
|
||||||
- Hintergrundbild der Willkommensseite wird pro Standort unter `media/<site>/background.*` gespeichert; Fallback auf `static/wallpaper.png` wenn keine Datei existiert.
|
- Hintergrundbild der Willkommensseite wird pro Standort unter `media/<site>/background.*` gespeichert; Fallback auf `static/wallpaper.png` wenn keine Datei existiert.
|
||||||
- `get_background_url(site)` prüft auf benutzerdefiniertes Hintergrundbild für einen Standort.
|
- `get_background_url(site)` prüft auf benutzerdefiniertes Hintergrundbild für einen Standort.
|
||||||
- `generate_welcome_html(customer_names, logo_urls, background_url=None)` akzeptiert optionalen `background_url`-Parameter.
|
- `generate_welcome_html(customer_names, logo_urls, background_url=None)` akzeptiert optionalen `background_url`-Parameter.
|
||||||
@@ -88,4 +129,3 @@ Standorte (sites) sind die oberste Organisationsebene und gruppieren Screens.
|
|||||||
- `search_customer_logo` in `generate_welcome_page.py` nutzt OpenAI GPT-4 + Brandfetch CDN.
|
- `search_customer_logo` in `generate_welcome_page.py` nutzt OpenAI GPT-4 + Brandfetch CDN.
|
||||||
- `admin_priority` rendert `priority.html` mit `site_list`, `current_site`, `priority_files`, `server_url`.
|
- `admin_priority` rendert `priority.html` mit `site_list`, `current_site`, `priority_files`, `server_url`.
|
||||||
- `static/dva.png`: Bild für den Voice-Agent-Button (optional, per Admin-Toggle ein-/ausblendbar).
|
- `static/dva.png`: Bild für den Voice-Agent-Button (optional, per Admin-Toggle ein-/ausblendbar).
|
||||||
|
|
||||||
|
|||||||
47
README.md
47
README.md
@@ -17,6 +17,8 @@ Browserbasiertes Digital-Signage-System für interne Info-Screens.
|
|||||||
- **Custom-URL-Button**: pro Screen konfigurierbarer Aktions-Button mit frei wählbarer Position (9 Positionen) im Player (öffnet URL in iframe-Overlay mit Zurück-Button oder per Direkt-Weiterleitung)
|
- **Custom-URL-Button**: pro Screen konfigurierbarer Aktions-Button mit frei wählbarer Position (9 Positionen) im Player (öffnet URL in iframe-Overlay mit Zurück-Button oder per Direkt-Weiterleitung)
|
||||||
- **Digital Voice Agent**: global konfigurierbarer Voice-Agent-Button pro Screen (9 Positionen, iframe-Overlay mit positionsgetreuem Zurück-Button, Typewriter-Tagline mit mehrsprachigen Wechseltexten, optionales Bild über dem Button)
|
- **Digital Voice Agent**: global konfigurierbarer Voice-Agent-Button pro Screen (9 Positionen, iframe-Overlay mit positionsgetreuem Zurück-Button, Typewriter-Tagline mit mehrsprachigen Wechseltexten, optionales Bild über dem Button)
|
||||||
- **Stay-on-First**: Screen kann auf erstem Playlist-Element stehen bleiben (kein automatischer Refresh)
|
- **Stay-on-First**: Screen kann auf erstem Playlist-Element stehen bleiben (kein automatischer Refresh)
|
||||||
|
- **User-Verwaltung**: Mehrere User mit Rollen (Admin / Superuser / User), E-Mail als Login, Passwort-Hashing (scrypt), Berechtigungen pro Standort
|
||||||
|
- **Passwort-Workflow**: First-Login-Änderung, Admin-Reset mit temporärem Passwort, Notfall-Login bei fehlendem Hash
|
||||||
- Tab-basierte Admin-UI pro Screen: Playlist, Einstellungen, Aktionen, Digital Voice Agent, Medien (Tabler Tabs)
|
- Tab-basierte Admin-UI pro Screen: Playlist, Einstellungen, Aktionen, Digital Voice Agent, Medien (Tabler Tabs)
|
||||||
- Priority-Seite ebenfalls mit Tabs: Playlist und Medien
|
- Priority-Seite ebenfalls mit Tabs: Playlist und Medien
|
||||||
- Dark Mode (localStorage-persistiert)
|
- Dark Mode (localStorage-persistiert)
|
||||||
@@ -35,6 +37,7 @@ Flask App (Server)
|
|||||||
├── Player UI /player/<site>/<screen>
|
├── Player UI /player/<site>/<screen>
|
||||||
├── Priority-Seite /admin/<site>/priority
|
├── Priority-Seite /admin/<site>/priority
|
||||||
├── config.json
|
├── config.json
|
||||||
|
├── users.json
|
||||||
├── media/
|
├── media/
|
||||||
│ ├── <site>/
|
│ ├── <site>/
|
||||||
│ │ ├── <screen>/
|
│ │ ├── <screen>/
|
||||||
@@ -59,6 +62,7 @@ signage/
|
|||||||
├── app.py # Flask-App (alle Routen)
|
├── app.py # Flask-App (alle Routen)
|
||||||
├── generate_welcome_page.py # Logo-Suche + Willkommensseite-Generierung
|
├── generate_welcome_page.py # Logo-Suche + Willkommensseite-Generierung
|
||||||
├── config.json # Persistente Konfiguration
|
├── config.json # Persistente Konfiguration
|
||||||
|
├── users.json # User-Datenbank (gehashte Passwörter)
|
||||||
├── media/
|
├── media/
|
||||||
│ ├── <site>/
|
│ ├── <site>/
|
||||||
│ │ ├── lobby/
|
│ │ ├── lobby/
|
||||||
@@ -71,6 +75,10 @@ signage/
|
|||||||
│ ├── customer.html # Willkommensseite-Formular
|
│ ├── customer.html # Willkommensseite-Formular
|
||||||
│ ├── player.html # Player-Ansicht
|
│ ├── player.html # Player-Ansicht
|
||||||
│ ├── login.html
|
│ ├── login.html
|
||||||
|
│ ├── change_password.html # Passwort ändern (First-Login / Reset)
|
||||||
|
│ ├── user_list.html # User-Liste (Admin)
|
||||||
|
│ ├── user_create.html # User anlegen (Admin)
|
||||||
|
│ ├── user_edit.html # User bearbeiten (Admin)
|
||||||
│ ├── _header.html # Gemeinsamer Header
|
│ ├── _header.html # Gemeinsamer Header
|
||||||
│ ├── _footer.html # Gemeinsamer Footer
|
│ ├── _footer.html # Gemeinsamer Footer
|
||||||
│ └── _styles.html # Zentrale CSS (Variablen, Dark Mode)
|
│ └── _styles.html # Zentrale CSS (Variablen, Dark Mode)
|
||||||
@@ -140,6 +148,14 @@ docker compose up -d
|
|||||||
| `GET /playlist/<site>/<screen>/hash` | Playlist-Checksumme (für Auto-Reload) |
|
| `GET /playlist/<site>/<screen>/hash` | Playlist-Checksumme (für Auto-Reload) |
|
||||||
| `GET /willkommen?site=<site>` | Willkommensseite-Formular (GET + POST) |
|
| `GET /willkommen?site=<site>` | Willkommensseite-Formular (GET + POST) |
|
||||||
| `POST /api/customer` | API-Endpunkt für Willkommensseite (JSON) |
|
| `POST /api/customer` | API-Endpunkt für Willkommensseite (JSON) |
|
||||||
|
| `GET /change-password` | Passwort ändern (First-Login / nach Admin-Reset) |
|
||||||
|
| `GET /admin/users` | User-Liste (Admin) |
|
||||||
|
| `GET /admin/users/create` | User anlegen (Admin) |
|
||||||
|
| `GET /admin/users/edit/<email>` | User bearbeiten (Admin) |
|
||||||
|
| `POST /admin/users/create` | User anlegen (Admin) |
|
||||||
|
| `POST /admin/users/delete/<email>` | User löschen (Admin, letzter Admin geschützt) |
|
||||||
|
| `POST /admin/users/reset-password/<email>` | Passwort-Reset mit temporärem Passwort (Admin) |
|
||||||
|
| `POST /admin/users/edit/<email>` | User bearbeiten speichern (Admin) |
|
||||||
| `GET /media/<site>/<screen>/<file>` | Medien-Datei ausliefern |
|
| `GET /media/<site>/<screen>/<file>` | Medien-Datei ausliefern |
|
||||||
| `GET /media/priority/<file>` | Priority-Medien (global) |
|
| `GET /media/priority/<file>` | Priority-Medien (global) |
|
||||||
| `GET /media/<site>/background/<filename>` | Hintergrundbild der Willkommensseite |
|
| `GET /media/<site>/background/<filename>` | Hintergrundbild der Willkommensseite |
|
||||||
@@ -159,6 +175,7 @@ http://localhost:5005/admin/<standort>
|
|||||||
- Digital Voice Agent pro Screen konfigurieren (Position, Bild ein/aus, Typewriter-Tagline)
|
- Digital Voice Agent pro Screen konfigurieren (Position, Bild ein/aus, Typewriter-Tagline)
|
||||||
- Stay-on-First-Modus pro Screen (kein automatischer Refresh)
|
- Stay-on-First-Modus pro Screen (kein automatischer Refresh)
|
||||||
- Standorte anlegen & löschen
|
- Standorte anlegen & löschen
|
||||||
|
- **Userverwaltung**: User anlegen, bearbeiten, löschen, Passwort-Reset (temporäres Passwort wird angezeigt)
|
||||||
|
|
||||||
### Player-URL
|
### Player-URL
|
||||||
|
|
||||||
@@ -181,7 +198,25 @@ Maximal 3 Kunden eingeben → Logos werden via OpenAI + Brandfetch gesucht → `
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Globale Konfiguration
|
### Globale Konfiguration (config.json)
|
||||||
|
|
||||||
|
| Feld | Typ | Beschreibung |
|
||||||
|
|------|-----|-------------|
|
||||||
|
| `server_url` | string | Öffentliche Server-URL für Player-Links |
|
||||||
|
| `voice_agent_url` | string | URL des Digital Voice Agents (global, read-only im Admin) |
|
||||||
|
|
||||||
|
### User-Rollen (users.json)
|
||||||
|
|
||||||
|
| Rolle | Zugriff | Userverwaltung |
|
||||||
|
|-------|---------|----------------|
|
||||||
|
| **Admin** | Alle Standorte | Ja (anlegen/bearbeiten/löschen/Reset) |
|
||||||
|
| **Superuser** | Alle Standorte | Nein |
|
||||||
|
| **User** | Nur freigegebene Standorte | Nein |
|
||||||
|
|
||||||
|
- Usernamen sind E-Mail-Adressen
|
||||||
|
- Passwörter werden als scrypt-Hash gespeichert
|
||||||
|
- `users.json` wird beim ersten Start aus `config.json.admin` befüllt
|
||||||
|
- **Notfall**: `users.json` löschen → Server-Neustart erzeugt neuen Admin aus `config.json`
|
||||||
|
|
||||||
| Feld | Typ | Beschreibung |
|
| Feld | Typ | Beschreibung |
|
||||||
|------|-----|-------------|
|
|------|-----|-------------|
|
||||||
@@ -269,6 +304,10 @@ Maximal 3 Kunden eingeben → Logos werden via OpenAI + Brandfetch gesucht → `
|
|||||||
- `load_priority_files()` – Priority-Playlist + Dateien laden
|
- `load_priority_files()` – Priority-Playlist + Dateien laden
|
||||||
- `prio_redirect(site)` – Redirect-Pfad zur Priority-Seite
|
- `prio_redirect(site)` – Redirect-Pfad zur Priority-Seite
|
||||||
- `get_background_url(site)` – URL zum benutzerdefinierten Hintergrundbild oder `None`
|
- `get_background_url(site)` – URL zum benutzerdefinierten Hintergrundbild oder `None`
|
||||||
|
- `load_users()` / `save_users()` / `get_user()` – User aus `users.json`
|
||||||
|
- `init_user_db()` – initialen Admin aus `config.json` anlegen
|
||||||
|
- `get_accessible_sites(cfg, user)` – Standorte nach User-Berechtigung filtern
|
||||||
|
- `admin_required` / `site_access_required` – Zugriffs-Dekoratoren
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -280,7 +319,11 @@ Wird über `localStorage("signage-theme")` persistiert. Umschalt-Button im Heade
|
|||||||
|
|
||||||
## Sicherheit
|
## Sicherheit
|
||||||
|
|
||||||
- Admin-Bereich per Flask-Login geschützt
|
- Admin-Bereich per Flask-Login geschützt (E-Mail + Passwort)
|
||||||
|
- Passwörter gehasht (scrypt) in `users.json`
|
||||||
|
- Drei Rollen: Admin (alle Standorte + Userverwaltung), Superuser (alle Standorte), User (nur zugewiesene Standorte)
|
||||||
|
- Zugriffskontrolle per Decorator (`@admin_required`, `@site_access_required`)
|
||||||
|
- Letzter Admin kann nicht gelöscht werden
|
||||||
- Player-Seiten Read-Only
|
- Player-Seiten Read-Only
|
||||||
- Externe Nutzung via Reverse Proxy + TLS empfohlen
|
- Externe Nutzung via Reverse Proxy + TLS empfohlen
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,6 @@
|
|||||||
{
|
{
|
||||||
"admin": {
|
"admin": {
|
||||||
"password_hash": "scrypt:32768:8:1$2IMM3RQ2eqdrtYj1$960cf075435d7c7584ecf240a3b1858551727e875c9ff61874d4269f412ec6eb8cdef732e509408968460fe62534b193f2a691cf3ef329a45a61c20ec3d12c8a",
|
"password_hash": "scrypt:32768:8:1$JNEBAJBSSigcWCxK$4809dcc38b2abcaa3f6397d052560f9f59dedf7c95cf99b61bac0df08a65b64287907522841cbb23e06b36760814007d5757542db1c968a8536be7fd6604a382",
|
||||||
"role": "admin",
|
|
||||||
"sites": [],
|
|
||||||
"must_change_password": false
|
|
||||||
},
|
|
||||||
"erik.thiele@cancom.de": {
|
|
||||||
"password_hash": "scrypt:32768:8:1$Cy3cP6oIQuIsYSad$f5feb9e476e79cd917f12ca62a6813104679cd96cb76647cb177e640946054ab8a3f8748020f0782a07e4d47d970ea670b5910b05a685939c2161115eadbc440",
|
|
||||||
"role": "admin",
|
"role": "admin",
|
||||||
"sites": [],
|
"sites": [],
|
||||||
"must_change_password": false
|
"must_change_password": false
|
||||||
|
|||||||
Reference in New Issue
Block a user