Commit graph

174 commits

Author SHA1 Message Date
Jesko Anschütz
0b21be6469 Tenant-Feature Phase 3: Auth-Middleware verdrahtet
- TenantSlug in User-Struct + GetSessionUser per JOIN befüllt
- middleware.go: RequireAuth, RequireAdmin, RequireTenantAccess
- router.go: alle Routen mit passendem Middleware-Stack gesichert

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 17:52:55 +01:00
Jesko Anschütz
7e7a692521 Tenant-Feature Phase 1+2: Auth-Fundament + Login-Flow + UX-Textverbesserung
- DB-Migration 002_auth.sql (users + sessions Tabellen)
- AuthStore mit Session-Management, bcrypt, EnsureAdminUser
- Login/Logout Handler mit Cookie-Session (HttpOnly, SameSite=Lax)
- Login-Template (Bulma-Card, deutsche Labels)
- Config: AdminPassword, DefaultTenantSlug, DevMode
- Fallback-Texte: "Netzwerk offline" → "Server nicht erreichbar"
- TENANT-FEATURE-PLAN.md mit 46 Checkboxen als Steuerungsdatei

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 15:46:14 +01:00
Jesko Anschütz
cea393c1a0 Auth Phase 1 Review: Fix 3 critical bugs in auth foundation
1. [SQL] Fix username uniqueness constraint
   - Changed from global unique to composite unique(tenant_id, username)
   - Multi-tenant apps need same usernames across tenants (e.g., each tenant can have 'admin')

2. [Go] Fix inconsistent error handling in scanSession
   - Now returns pgx.ErrNoRows when session not found (like scanUser)
   - Allows proper 404 vs 500 error distinction in handlers

3. [Go] Add missing VerifyPassword function
   - Implements bcrypt.CompareHashAndPassword for password verification
   - Enables login flow with proper error handling for missing users
   - Paired with existing GenerateFromPassword for secure password hashing

Security checks:
- SQL injection: All queries parameterized (no string interpolation)
- bcrypt: Cost factor 12 (production-recommended)
- Session tokens: PostgreSQL gen_random_uuid() (cryptographically secure)
- Password hashes: Protected with json:"-" tag (never exposed in responses)
- Error handling: Comprehensive, no silent failures

Build & Vet: All checks pass (go build ./..., go vet ./...)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 15:37:18 +01:00
Jesko Anschütz
6bc4d3d2f8 Fix: Protokoll-relative URLs, PDF-Fragment-Merge, Startup-Token-Cache, Test-Nil-Deref
- URL-Normalisierung überspringt jetzt //protocol-relative URLs
- PDF-Viewer-Parameter werden mit bestehenden Fragments gemerged statt blind angehängt
- /api/startup-token setzt Cache-Control: no-store (Server + Client)
- Tote Goroutine mit ignoriertem net.Listen-Error aus TestAssetsServed entfernt

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 15:21:26 +01:00
Jesko Anschütz
2534dbbe05 PDF-Darstellung: Sidebar und Toolbar ausblenden via URL-Parameter
PDF-URLs bekommen #toolbar=0&navpanes=0&scrollbar=0&view=Fit&page=1
angehängt, damit Chromium den PDF-Viewer ohne Sidebar und Toolbar
im Vollbild rendert. PDF.js als Folgeschritt in TODO dokumentiert.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 12:18:52 +01:00
Jesko Anschütz
a334dbd95a Fix: Relative Upload-Pfade zu absoluten Backend-URLs in Playlist
Agent ergänzt relative src-Pfade (/uploads/...) mit ServerBaseURL
beim Playlist-Fetch, damit Chromium Medien direkt vom Backend lädt
statt 404 auf dem lokalen Agent-Server zu bekommen.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 12:08:19 +01:00
Jesko Anschütz
6931181916 Fix: Transition-Race, Auto-Reload nach Deploy, Playlist-Latenz < 1s
- hideAllContent() prüft opacity bevor display=none gesetzt wird
  (verhindert Race mit displayItem)
- Neuer /api/startup-token Endpoint: Browser erkennt Agent-Neustart
  und reloaded automatisch
- MQTT-Debounce von 3s auf 500ms, Browser-Poll von 30s auf 5s
  reduziert für sub-sekunden Playlist-Updates

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 11:48:57 +01:00
Jesko Anschütz
585cb83ed0 MQTT-Playlist-Push: Änderungen erreichen Client binnen 5 Sekunden
Backend published auf signage/screen/{slug}/playlist-changed nach
Playlist-Mutationen (2s Debounce). Agent subscribed und fetcht
Playlist sofort (3s Debounce). 60s-Polling bleibt als Fallback.

Neue Packages: mqttnotifier (Backend), mqttsubscriber (Agent)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 11:35:50 +01:00
Jesko Anschütz
d4ab1da5aa Fix: Player-UI Content unsichtbar wegen display='' statt display='block'
Die Transition-Logik in displayItem() setzte element.style.display = '',
wodurch die CSS-Klassen-Regel display:none wieder griff und alle
Content-Elemente (iframe, img, video) unsichtbar blieben.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 11:20:16 +01:00
Jesko Anschütz
fa74ceb5d8 UX Block 3: Upload-Fortschritt, Toggle-Switch, vars.yml-Download
- Upload-Fortschrittsbalken per XHR mit Progress-Event
- Checkbox-Toggle statt Ja/Nein-Select für Enabled-Feld
- vars.yml Download-Button im Provisioning-Workflow
- Alle UX-Aufgaben in TODO.md abgehakt

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 11:06:27 +01:00
Jesko Anschütz
62c1b8cd5c UX Block 2: Lösch-Modals, Status-Page Deutsch, Transitions, lokale Assets, Accessibility
- Lösch-Bestätigung: Bulma-Modal statt browser-nativer confirm()
- Status-Page komplett auf Deutsch, relative Zeitstempel ("vor 2 Min")
- Querlinks Admin ↔ Status-Page
- Bulma CSS + SortableJS als lokale go:embed Assets statt CDN
- Player-UI: sanfte Fade-Transitions (500ms) bei Content-Wechsel
- Player-UI: erweitertes Sysinfo-Overlay (Titel, Playlist-Länge, Netzwerk)
- Aria-Labels für Lösch-Buttons und Drag-Handles
- Larry-Fixes: Null-Checks in copy()/switchTab(), Umlaut-Korrektur

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 11:03:04 +01:00
Jesko Anschütz
883a8146c5 UX Block 1: Flash-Messages, Screen-Status, Responsive-Tabellen, Navbar-Burger
- Flash-Messages nach allen Manage-Aktionen (Upload, Löschen, Speichern, Hinzufügen)
- Screen-Online/Offline-Status als farbiger Punkt in Admin-Tabelle
- overflow-x Wrapper für alle Tabellen (Admin, Playlist, Medienbibliothek)
- Navbar-Burger für mobile Viewports in Admin und Manage
- UX-Gestaltungsplan als Sektion in TODO.md eingetragen

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 10:55:15 +01:00
Jesko Anschütz
f11bd4f6c4 Bugfixes: Player-UI Content-Rendering, Backend-URL Dev-Display, MIME-Type-Erkennung
- Player-UI: Content-Type-Handling (image/video/web statt alles-iframe),
  Fast-Retry-Polling beim Start, Splash wird korrekt ausgeblendet,
  Fallback-Anzeige bei X-Frame-Options-Blockade
- Dev-Display: Backend-URL auf 192.168.64.1 für Multipass-Netz korrigiert
- Media-Upload: Typ wird aus MIME-Type abgeleitet statt blind aus Formular
- TODO: Daten-Bug dokumentiert

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 10:50:17 +01:00
Jesko Anschütz
aff12a4d81 Doku-Sync: README, TODO, DEVELOPMENT und API-Docs auf Implementierungsstand nachgezogen
README, DEVELOPMENT und TODO spiegelten noch den Stand vor Ebene 1+2 wider.
Checkboxen in TODO von ~18 auf ~70 aktualisiert, drei neue API-Dokumentationsdateien ergänzt.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 09:55:36 +01:00
Jesko Anschütz
12c10f0337 Admin-UI: Bildschirm einrichten mit Ansible-Anleitung (Variante A)
- POST /admin/screens/provision: legt Screen in DB an (Upsert) und zeigt
  eine 5-Schritt-Seite mit kopierbaren Code-Blöcken:
  1. inventory.yml Eintrag
  2. host_vars/{slug}/vars.yml Inhalt
  3. ssh-copy-id Befehl
  4. ansible-playbook Befehl (mit Vault-Passwort-Hinweis)
  5. Link zur Playlist-Verwaltung
- Admin-Formular: IP-Adresse + SSH-User Felder ergänzt
- Altes "nur anlegen"-Formular als aufklappbaren Details-Block versteckt
- Clipboard-Copy-Buttons für jeden Code-Block

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 06:07:14 +01:00
Jesko Anschütz
e03948f25d Kopplung Agent↔Backend: Selbstregistrierung + Playlist-Rotation
Backend:
- ScreenStore.Upsert(): idempotentes INSERT ON CONFLICT für Self-Registration
- POST /api/v1/screens/register: Agent registriert sich beim Start (upsert)
- manage/register.go: neuer Handler, immer unter Tenant "morz"

Agent:
- config: screen_name + screen_orientation (mit Fallback auf screen_id / landscape)
- app.go: registerScreen() — POST /api/v1/screens/register beim Start (Retry 30s)
- app.go: pollPlaylist() — GET /api/v1/screens/{slug}/playlist alle 60s
- app.go: nowFn liefert Playlist statt statischer URL; PlayerContentURL als Fallback
- playerserver: PlaylistItem-Struct in NowPlaying; JS rotiert Items per duration_seconds
- JS: Playlist-Fingerprint verhindert Reset laufender Rotation bei unverändertem Stand

Ansible:
- config.json.j2: screen_name + screen_orientation ergänzt
- host_vars/info10: screen_name + screen_orientation
- host_vars/info01-dev: screen_name + screen_orientation

Kopplung per Konvention: screen_id (config.json) = slug (DB)
Beim ersten Neustart der Agents erscheinen die Bildschirme automatisch im Admin-UI.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 05:57:58 +01:00
Jesko Anschütz
d395804612 Bugfixes: JSON-Tags, Tenant-Lookup, Dockerfile Go-Version
- store: JSON-Tags auf allen Domain-Typen (snake_case statt PascalCase)
- media.go: PathValue("tenantId") → "tenantSlug" + Tenant-Lookup via TenantStore
- media.go: leere Asset-Liste gibt [] statt null zurück
- router.go: TenantStore an HandleListMedia/HandleUploadMedia weitergeben
- Dockerfile: golang:1.24 → golang:1.25 (go.mod fordert >= 1.25)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 23:26:56 +01:00
Jesko Anschütz
a2561a704a compose: Backend-Service in server-stack.yml ergänzt
- Backend-Container mit Dockerfile aus server/backend/
- Postgres healthcheck damit Backend erst startet wenn DB bereit ist
- uploads-Volume für hochgeladene Dateien
- MORZ_INFOBOARD_DATABASE_URL zeigt auf postgres-Service

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 23:18:45 +01:00
Jesko Anschütz
803f355220 Baue Ebene 2: PostgreSQL-Backend, Medien-Upload und Playlist-UI
- DB-Package mit pgxpool, Migrations-Runner und eingebetteten SQL-Dateien
- Schema: tenants, screens, media_assets, playlists, playlist_items
- Store-Layer: alle Repositories (TenantStore, ScreenStore, MediaStore, PlaylistStore)
- JSON-API: Screens, Medien, Playlist-CRUD, Player-Sync-Endpunkt
- Admin-UI (/admin): Screens anlegen, löschen, zur Playlist navigieren
- Playlist-UI (/manage/{slug}): Drag&Drop-Sortierung, Item-Bearbeitung,
  Medienbibliothek, Datei-Upload (Bild/Video/PDF) und Web-URL
- Router auf RouterDeps umgestellt; manage-Routen nur wenn Stores vorhanden
- parseOptionalTime akzeptiert nun RFC3339 und datetime-local HTML-Format

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 22:53:00 +01:00
Jesko Anschütz
bbcf0a1228 Baue Ebene 1: Player-UI, Kiosk-Display und vollstaendiges Ansible-Deployment
Player-UI (playerserver):
- Lokale Kiosk-Seite unter /player mit orientierungsgerechtem Splash-Bild
- Splash-PNGs (Portrait/Landscape) eingebettet via go:embed
- Unteres-Drittel-Overlay mit erweiterbaren Sysinfo-Items (Hostname, Uptime)
- /api/now-playing und /api/sysinfo JSON-Endpunkte
- iframe-Overlay fuer spaetere Inhalts-URL

Ansible-Rolle signage_display (neu):
- Pakete: xserver-xorg-core, xinit, openbox, chromium, unclutter
- Kiosk-Skript mit openbox als WM (noetig fuer korrektes --kiosk-Vollbild)
- systemd-Unit mit Conflicts=getty@tty1 (behebt TTY-Blockierung beim Start)
- Chromium Managed Policy: TranslateEnabled=false, Notifications/Geolocation blockiert
- --lang=de Flag gegen Sprachauswahl-Dialog

Ansible-Rolle signage_player (erweitert):
- Legt signage_user an falls nicht vorhanden
- PlayerListenAddr und PlayerContentURL in Konfiguration
- journald volatile Storage (SD-Karten-Schonung)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 22:34:16 +01:00
Jesko Anschütz
d461abc3f5 Reduziere SD-Karten-Schreiblast: leiser Agent + journald volatile
- Agent loggt im Normalfall nichts mehr (kein heartbeat_tick, kein
  mqtt_heartbeat_sent, kein status_report_sent)
- nur noch Fehler und Zustandsaenderungen werden geloggt
- Ansible: journald auf Storage=volatile + RuntimeMaxUse=20M (RAM-only,
  automatisches Verdraengen alter Eintraege bei vollem Puffer)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 21:13:23 +01:00
Jesko Anschütz
b111cf8421 Fuege Ansible-Rolle signage_player fuer Agent-Deployment hinzu
- Rolle signage_player: baut Binary lokal (linux/arm64), deployt es,
  schreibt config.json per Template, installiert und aktiviert systemd-Unit
- inventory.yml mit Host info10 (10.0.0.200)
- group_vars/signage_players: getrennte vars.yml (oeffentlich) und
  vault.yml (Secrets, gitignored) fuer MQTT-Credentials
- host_vars/info10: ansible_host, ansible_user, screen_id
- site.yml zeigt auf signage_players-Gruppe und signage_player-Rolle
- Binaries und vault.yml in .gitignore

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 21:09:54 +01:00
Jesko Anschütz
1dbebc0a2b Ergaenze MQTT-Authentifizierung mit Username und Password
- Config: mqtt_username / mqtt_password (JSON + Env MORZ_INFOBOARD_MQTT_USERNAME/PASSWORD)
- mqttheartbeat.New() nimmt username und password entgegen,
  setzt Credentials nur wenn username nicht leer ist (kein-Auth-Broker bleibt kompatibel)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 21:00:15 +01:00
Jesko Anschütz
d0137179e5 Fuege MQTT-Heartbeat zum Agent hinzu (kein Broker konfiguriert = skip)
- neues Paket mqttheartbeat: Publisher mit paho, topic signage/screen/<id>/heartbeat,
  payload {screen_id, ts, status, server_connectivity}, auto-reconnect bei Ausfall
- MORZ_INFOBOARD_MQTT_BROKER leer (Standard) -> MQTT komplett uebersprungen
- app.emitHeartbeat() publiziert bei jedem Tick per MQTT wenn Broker konfiguriert,
  loggt Fehler und laeuft weiter (kein Stop bei MQTT-Ausfall)
- mqtt.Close() bei context.Done()
- MQTTBroker-Default von tcp://127.0.0.1:1883 auf "" geaendert
- erste externe Dep: github.com/eclipse/paho.mqtt.golang v1.5.1

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 20:54:12 +01:00
Jesko Anschütz
a99f8a5784 Aktualisiere Doku und gleiche /api/v1-Toolsliste mit /api/v1/meta an
- docs/PLAYER-STATUS-HTTP.md vollstaendig neu strukturiert:
  q=, derived_state=, DELETE-Endpoint und Datei-Persistenz ergaenzt,
  Abgrenzung "keine dauerhafte Speicherung" entfernt
- GET /api/v1 listet jetzt dieselben 5 Tools wie /api/v1/meta
  (player-status-ingest und screen-status-delete ergaenzt)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 20:41:47 +01:00
Jesko Anschütz
56635554c7 Fuege Screen-Loeschung, Meta-Update, Datei-Persistenz und Lifecycle-Test hinzu
- DELETE /api/v1/screens/{screenId}/status loescht einzelne Screen-Eintraege
- /api/v1/meta listet jetzt 5 Tools inkl. screen-status-delete und diagnostic_ui-Pfade
- filePlayerStatusStore persistiert den Status-Store atomar in einer JSON-Datei
- MORZ_INFOBOARD_STATUS_STORE_PATH aktiviert die Datei-Persistenz (leer = In-Memory)
- Integration-Test deckt den vollstaendigen Lifecycle: POST -> list -> HTML -> JSON -> DELETE -> 404 ab
- DEVELOPMENT.md beschreibt End-to-End-Entwicklungstest und neue Env-Variable

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 20:34:37 +01:00
Jesko Anschütz
ea90af1403 Ergaenze derived_state als Query-Filter fuer Uebersicht und Status-API
GET /api/v1/screens/status und GET /status akzeptieren jetzt
derived_state=online|degraded|offline zum direkten Filtern nach der
serverseitig abgeleiteten Diagnoseeinschaetzung. Erlaubte Werte sind
online, degraded und offline; unknown ist explizit nicht erlaubt, da
derived_state immer auf einen der drei Werte abgebildet wird.

Abgrenzung zu server_connectivity: derived_state filtert nach dem
zusammengefassten Zustand (stale + connectivity + status), waehrend
server_connectivity nur den gemeldeten Connectivity-Wert betrifft.
Beide Filter koennen kombiniert werden.

Tests: FiltersByDerivedState, RejectsInvalidDerivedState

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 20:29:16 +01:00
Jesko Anschütz
8243eb10c9 Ergaenze Screen-ID-Filter (q=) fuer Uebersicht und Status-API
GET /api/v1/screens/status und GET /status akzeptieren jetzt q=<substring>
zum Filtern der Ergebnisliste nach ScreenID. Der Vergleich ist case-
insensitiv. Leerer Wert bedeutet kein Filter; jeder andere String ist gueltig
(keine Validierung noetig). Die Summary-Counts bleiben unveraendert und
beschreiben weiterhin den gesamten Store-Bestand.

Die Quick-Filter auf /status behalten den aktuellen q-Wert beim Klick, damit
der Textfilter nicht verloren geht wenn man z.B. von "All screens" auf
"Stale reports" wechselt.

Tests: FiltersByScreenIDSubstring, ScreenIDFilterIsCaseInsensitive

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 20:28:01 +01:00
Jesko Anschütz
57e0cdb43c Ergaenze Auto-Refresh auf Detailseite und bereinige Fehlermeldungs-Duplikat
Auto-Refresh auf GET /status/{screenId}:
  screenDetailPageData bekommt RefreshSeconds (wie statusPageData). Das
  Detail-Template rendert das Meta-Tag analog zur Uebersichtsseite mit
  denselben 15 Sekunden, damit ein Screen seinen Zustand (z.B. fresh ->
  stale, connectivity-Wechsel) auch ohne manuellen Reload sichtbar macht.
  Test: meta-refresh-Tag jetzt in TestRouterScreenDetailPageRoute geprueft.

DRY-Refactor: Fehlermeldungen vereinheitlicht:
  overviewQueryErrorMessage und overviewQueryErrorCode sind jetzt in
  playerstatus.go definiert -- dort wo auch die Validierungslogik lebt.
  writeOverviewQueryError delegiert vollstaendig an beide Helper statt
  die Meldungen selbst zu duplizieren. Die vorherige Kopie in statuspage.go
  mit abweichenden Satzendezeichen und Grossschreibung wurde entfernt.
  Beide Fehlerpfade (JSON und HTML) nutzen jetzt exakt dieselben
  Meldungstexte.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 20:16:22 +01:00
Jesko Anschütz
cfab277dc4 Fuege HTML-Detailseite und HTML-Fehlerseite fuer den Status-UI-Pfad hinzu
Drei eng zusammenhaengende Aenderungen in einem Commit, da sie dieselbe
Template-Infrastruktur teilen und gemeinsam die HTML-Diagnoseansicht
abrunden:

1. CSS-Extraktion (Verbesserung, kein Verhalten geaendert)
   Das bisherige Inline-CSS wurde in die Konstante statusPageCSS
   ausgelagert. statusPageCSSBlock injiziert es per String-Konkatenation
   in alle Templates, sodass kein Template-Inheritance-Mechanismus
   benoetigt wird und jede Seite eigenstaendig ausfuehrbar bleibt.

2. GET /status/{screenId} -- neue HTML-Detailseite
   Zeigt den letzten bekannten Datensatz eines einzelnen Screens:
   Derived State, Player-Status, Connectivity und Frische als
   Summary-Cards; Timing- und Endpoints-Details in aufgeraeuemten
   Key-Value-Tabellen. Verlinkung zurueck auf /status und auf den
   bestehenden JSON-Endpunkt. Bei unbekanntem Screen: 404 mit HTML-
   Fehlerseite und Rueck-Link.
   Route: GET /status/{screenId}

3. HTML-Fehlerseite fuer /status bei ungueltigen Query-Parametern
   Bisher lieferte handleStatusPage einen rohen JSON-Fehler, wenn
   z.B. ?stale=banana uebergeben wurde -- inkongruent fuer einen
   HTML-Endpunkt. writeStatusPageQueryError rendert jetzt dasselbe
   statusPageErrorTemplate wie der Not-Found-Fall der Detailseite
   und gibt text/html mit 400 zurueck.

Neue Tests (router_test.go):
- ScreenDetailPageRoute: prueft Inhalt, Links, Content-Type
- ScreenDetailPageNotFound: prueft 404 + HTML + Rueck-Link
- StatusPageRejectsInvalidQueryParams: prueft jetzt auch text/html
  fuer alle Fehlerfaelle

Docs (PLAYER-STATUS-HTTP.md):
- Query-Parameter-Validierung mit erlaubten Werten und Fehlercodes
- Neue /status/{screenId}-Seite und HTML-Fehlerseiten dokumentiert

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 20:03:24 +01:00
Jesko Anschütz
a7889231c0 Validiere server_connectivity und stale als Query-Parameter
Bisher wurden ungueltige Werte fuer server_connectivity und stale im
Listing-Endpunkt und auf der Statusseite stillschweigend ignoriert bzw.
fuehrten zu leeren Ergebnissen ohne Fehlermeldung. Beide Parameter werden
jetzt explizit auf erlaubte Werte geprueft und liefern bei ungueltiger
Eingabe einen 400-Fehler mit beschreibendem error_code – konsistent mit
der bestehenden Validierung fuer updated_since und limit.

Neue Tests (playerstatus_test.go):
- RejectsInvalidServerConnectivity
- RejectsInvalidStale
- RejectsInvalidUpdatedSince
- RejectsInvalidLimit

Neue Tests (router_test.go):
- StatusPageRejectsInvalidQueryParams (table-driven, alle 4 Faelle)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 19:50:01 +01:00
Jesko Anschütz
0b199f9289 Mache Statusseite als Diagnoseansicht nutzbarer
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-03-22 19:39:17 +01:00
Jesko Anschütz
bdc77e87e3 Lege erste sichtbare Statusseite an
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-03-22 19:19:45 +01:00
Jesko Anschütz
9727e53e35 Ergaenze Statusuebersicht um Summenwerte
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-03-22 19:12:00 +01:00
Jesko Anschütz
5a109f95cb Erweitere Statusuebersicht um Zeit- und Mengengrenzen
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-03-22 19:08:55 +01:00
Jesko Anschütz
cbe0c40f45 Priorisiere Statusuebersicht fuer Diagnosefaelle
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-03-22 18:52:18 +01:00
Jesko Anschütz
4ba3b4ddef Leite Diagnosezustand im Statuspfad ab
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-03-22 18:49:48 +01:00
Jesko Anschütz
852bba6264 Filtere Statusuebersicht nach Diagnosekriterien
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-03-22 18:47:16 +01:00
Jesko Anschütz
85949937cc Validiere Statuswerte und verfeinere Frischelogik
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-03-22 18:45:37 +01:00
Jesko Anschütz
e219eac5d7 Ziehe Meta-Tests fuer Player-Status nach
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-03-22 18:42:11 +01:00
Jesko Anschütz
1f4fa3d985 Schaerfe Semantik des Statuspfads nach
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-03-22 18:41:32 +01:00
Jesko Anschütz
45e7b776ab Erweitere Basis-API um Status-Endpunkte
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-03-22 18:35:43 +01:00
Jesko Anschütz
7d4a7f6194 Beschreibe Status-Endpunkte in der API-Meta
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-03-22 18:34:28 +01:00
Jesko Anschütz
943553234d Lege Statusuebersicht fuer Screens an
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-03-22 18:33:14 +01:00
Jesko Anschütz
cc06b5a728 Leite Frische des letzten Player-Status ab
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-03-22 18:30:49 +01:00
Jesko Anschütz
8f0f06ae25 Transportiere Server-Connectivity im Statuspfad
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-03-22 18:27:44 +01:00
Jesko Anschütz
a69135c0b9 Fuehre Offline-Schwelle fuer Server-Connectivity ein
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-03-22 18:25:01 +01:00
Jesko Anschütz
2c780d3e60 Dokumentiere Statusspeicherung und Connectivity-Zustaende
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-03-22 18:22:31 +01:00
Jesko Anschütz
9ee24fe4ae Trenne Lifecycle und Server-Connectivity im Agenten
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-03-22 18:22:23 +01:00
Jesko Anschütz
896eade0fb Halte letzten Player-Status im Backend
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-03-22 18:19:17 +01:00