Files
signage/AGENTS.md
2026-06-20 15:05:43 +02:00

9.7 KiB
Raw Blame History

CANCOM Simple Signage — Agent Guide

Start

pip install -r requirements.txt
python app.py
docker compose up -d

python app.py (via .venv/bin/python app.py) serves on http://localhost:5005. app.py runs Flask with debug=True, host="0.0.0.0", and port=5005.

Source Of Truth

  • Single Flask app in app.py; there is no database.
  • 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.
  • 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

Standorte (sites) sind die oberste Organisationsebene und gruppieren Screens.

Route Beschreibung
GET /player/<site>/<screen> Player für Screen an einem Standort
GET /admin Redirect zum ersten zugänglichen Standort
GET /admin/<site> Admin-Dashboard für einen Standort (@site_access_required)
GET /admin/<site>/priority Priority-Playlist als separate Seite
GET /media/<site>/<screen>/<file> Medien-Datei ausliefern
GET /media/priority/<file> Priority-Medien (global)
GET /media/<site>/background/<filename> Hintergrundbild der Willkommensseite
GET /playlist/<site>/<screen>/hash Playlist-Checksumme für Auto-Reload
GET /willkommen?site=<site> Willkommensseite für Standort generieren
POST /admin/<site>/upload-background Hintergrundbild für Willkommensseite hochladen
POST /admin/<site>/delete-background Hintergrundbild zurücksetzen auf Standard
POST /api/customer API-Endpunkt (JSON mit "site"-Feld)
GET /admin/<site>/add-screen?name=<name> Neuen Screen anlegen
POST /admin/<site>/delete-screen/<screen> Screen + Medien löschen
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-voice/<screen> Voice-Agent-Einstellungen speichern (Enabled, Label, Target, Position)
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

  • GET /player/<site>/<screen> renders the playlist and auto-reloads from /playlist/<site>/<screen>/hash.

  • 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.

  • .html items in media/ are rendered inline as content, not in an iframe.

  • 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.

  • 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.

  • 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.

  • 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").

  • Gemeinsame HTML-Bausteine: _header.html, _footer.html, _styles.html (CSS-Variablen --ccm-*, Dark Mode, Card-Border-Radius 1rem).

  • add_customer_to_lobby_playlist entfernt welcome.html sowohl als String als auch als Dict aus der Playlist vor dem Einfügen.

  • add_screen legt ein Verzeichnis unter media/<site>/<screen>/ an und einen Config-Eintrag.

  • delete_screen entfernt den Screen aus der Config und löscht das Verzeichnis rekursiv.

  • Custom-URL-Button: pro Screen konfigurierbar (custom_url + custom_url_label + custom_url_enabled + custom_url_target + custom_url_position) im Admin-Formular (Aktionen-Tab); wird im Player als Button an wählbarer Position (9 Positionen: oben/mitte/unten × links/mitte/rechts) angezeigt und öffnet die URL wahlweise in einem iframe-Overlay mit Zurück-Button (overlay) oder per Direkt-Weiterleitung (redirect); Player pausiert während das Overlay geöffnet ist. Die unteren Positionen weichen automatisch 16px über dem Newsticker-Balken aus.

  • Voice-Agent-Button: globale config.voice_agent_url wird pro Screen im Tab "Digital Voice Agent" konfiguriert; Einstellungen (voice_agent_enabled, voice_agent_label, voice_agent_target, voice_agent_position) funktionieren identisch zum Aktionen-Tab. Die URL ist read-only im Admin sichtbar und wird global in config.json gesetzt.

  • Voice-Agent-Typewriter: Tagline im Button zeigt wechselnde mehrsprachige Texte mit Buchstaben-für-Buchstaben-Effekt; nach 2,5s Pause fade-out über 0,8s, dann nächster Text.

  • Voice-Agent-Overlay: positioniert den Zurück-Button exakt an der Position des geklickten Buttons (identisches Positionssystem).

  • 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.

  • 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 (nur Admins).

  • 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.

  • 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.

  • 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

  • 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.
  • 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(), 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.
  • 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.
  • welcome.html wird in media/<site>/lobby/ gespeichert.
  • 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.
  • static/dva.png: Bild für den Voice-Agent-Button (optional, per Admin-Toggle ein-/ausblendbar).