# Deployment — SLC-Workshop Companion (App) **Auftrag für die Server-Claude / Ops:** Diese App **statisch** ausliefern. Kein Build-Schritt, keine Secrets, keine Datenbank. > **Neu (v0.10): kleiner Feedback-Endpoint.** Die App sammelt jetzt Workshop-Feedback > (Phasen + Prüfschritte) und **speichert es auf dem Server** zur späteren Auswertung. > Dafür braucht es **einen** schlanken POST-Sammelpunkt (Flat-File, **keine Datenbank**). > Referenz liegt bei: [`feedback.php`](feedback.php). Ist der Endpoint (noch) nicht > aktiv, geht **nichts verloren**: die App hält das Feedback lokal vor (Retry-Queue) > und der Header-Button **„⇩ Feedback"** exportiert es als Backup-Datei. ## Was das ist - Eine **statische Single-Page-PWA**. Alle Inhalte (Stationen, Quiz, Use-Cases) sind in `index.html` eingebettet — keine Laufzeit-API nötig. - Dateien im Ordner `04_Tablet-Quiz/app/`: - `index.html` — die App - `manifest.webmanifest` — PWA-Manifest - `sw.js` — Service Worker (Offline-Cache der App-Shell) - `icon.svg` — App-Icon - `feedback.php` — **optionaler** Feedback-Sammelpunkt (Flat-File, JSON Lines) ## Ziel Den Ordner `04_Tablet-Quiz/app/` als **statisches Web-Root** über den vorhandenen Webserver bereitstellen, erreichbar per **HTTPS** (oder localhost). > **Wichtig:** Der Service Worker (Offline/Installierbar) läuft **nur über HTTPS > oder localhost**. Über reines `http://` ohne TLS registriert er sich nicht — > die App funktioniert dann trotzdem, nur ohne Offline-Cache. ## Schritte 1. Repo auf dem Server bereitstellen/aktualisieren: `git clone https://git.1789.cloud/patrick/SLC_Game.git` (bzw. `git pull`). 2. Den Ordner `SLC_Game/04_Tablet-Quiz/app/` als statisches Root einbinden. ### Beispiel nginx ```nginx server { listen 443 ssl; server_name slc.example.intern; # anpassen # ssl_certificate ... ; ssl_certificate_key ... ; root /srv/SLC_Game/04_Tablet-Quiz/app; # Pfad anpassen index index.html; location = /sw.js { add_header Cache-Control "no-cache"; } # SW immer frisch location / { try_files $uri $uri/ /index.html; } } ``` ### Beispiel Caddy (TLS automatisch) ```caddy slc.example.intern { root * /srv/SLC_Game/04_Tablet-Quiz/app file_server header /sw.js Cache-Control "no-cache" } ``` ## Feedback-Endpoint (empfohlen — für die Auswertung) Die App schickt jedes gespeicherte Feedback per `POST` als JSON an den im `` hinterlegten Pfad (Default: `feedback.php`, gleiche Domain). Der Service Worker fasst POSTs nicht an — der Offline-Cache stört also nicht. **Variante A — PHP (empfohlen, mit nginx der kürzeste Weg).** `feedback.php` braucht **nur den PHP-Core** (json + Datei-I/O) — **keine** Extensions (kein php-mysql/curl/mbstring). Konkret auf Ubuntu 24.04 + nginx (PHP ist dort nicht vorinstalliert, aber 8.3 im Repo): ```bash sudo apt install php-fpm # zieht php8.3-fpm; Socket: /run/php/php8.3-fpm.sock # Datenverzeichnis AUSSERHALB des Web-Roots anlegen und dem nginx/php-User geben: sudo mkdir -p /srv/slc-feedback && sudo chown www-data:www-data /srv/slc-feedback ``` ```nginx # im server{}-Block der SLC-App: # 1) NUR feedback.php als PHP ausführen (kein generelles .php — Hardening): location = /feedback.php { include fastcgi_params; fastcgi_pass unix:/run/php/php8.3-fpm.sock; # Pfad ggf. anpassen fastcgi_param SCRIPT_FILENAME $document_root/feedback.php; fastcgi_param SLC_FEEDBACK_DIR /srv/slc-feedback; # Daten ausserhalb des Web-Roots } # 2) Datenverzeichnis im Web-Root (Fallback) niemals ausliefern: location ^~ /feedback-data/ { deny all; return 404; } ``` Daten landen als **JSON Lines** in `$SLC_FEEDBACK_DIR/feedback.jsonl` (eine Zeile pro Feedback) → für die Auswertung einlesen / zu CSV konvertieren. Ohne gesetzte `SLC_FEEDBACK_DIR` schreibt das Skript nach `./feedback-data/` (per `.gitignore` ausgenommen, von nginx via Regel 2 nicht ausgeliefert). > **Verifikation:** `curl https:///feedback.php` → `{"ok":true,...}`. Nach einem > gespeicherten Feedback wächst `feedback.jsonl` um eine Zeile. **Variante B — kein PHP verfügbar.** Endpoint auf einen beliebigen JSON-POST-Empfänger zeigen lassen (z. B. ein kleines Node-/Worker-Skript, das den Body an eine Datei anhängt) und im `` dessen URL eintragen. **Variante C — gar kein Server-Save.** Auch ohne Endpoint funktioniert alles: das Feedback bleibt in der Retry-Queue und kann über den Header-Button **„⇩ Feedback"** als JSON-Datei exportiert werden. ## Verifikation nach dem Deploy 1. URL öffnen → Startbildschirm „SLC Companion". 2. DevTools → Application → **Service Workers**: `sw.js` ist *activated*. 3. Flugmodus/Offline → Seite neu laden → App lädt weiterhin (Offline-Cache). 4. Einen Major-Durchlauf spielen, an einem Gate Feedback eintragen → **„💾 Feedback speichern"**. Bei aktivem Endpoint erscheint kein Fehler in der Konsole; in `feedback-data/feedback.jsonl` taucht eine neue Zeile auf. Ohne Endpoint: Header **„⇩ Feedback"** lädt die Backup-Datei. ## Updates - Bei neuem Stand: `git pull`. Wenn sich App-Assets geändert haben, in `sw.js` die Konstante `CACHE` hochzählen (z. B. `slc-companion-v1` → `-v2`), damit der Service Worker den Cache erneuert. ## Noch offen (nicht Teil dieses Deploys) - **YAML→Inhaltspipeline:** Inhalte sind aktuell in `index.html` eingebettet. Später aus den Blueprint-`service-lifecycle_*.yaml` generieren (braucht Zugriff aufs Blueprint-Repo). Bis dahin werden Inhalte direkt in `index.html` gepflegt. - **Companion-Chatbot** (optionaler Nachschlage-Bot) — siehe `../README.md` §8; *braucht* ein LLM-Backend und ist daher **nicht** Teil der statischen App.