feat: Passwortschutz im Server statt im Browser

Ohne gueltiges Passwort gibt der Server weder Seite noch Skript heraus.
Das Passwort kommt aus APP_PASSWORD; fehlt die Variable, startet der Server
absichtlich nicht. /healthz bleibt passwortfrei, damit die Ueberwachung den
geschuetzten Dienst nicht faelschlich fuer ausgefallen haelt.
This commit is contained in:
2026-07-29 10:32:41 +02:00
parent d3be44e5d3
commit 8858ab2f88
5 changed files with 159 additions and 4 deletions
+35
View File
@@ -144,6 +144,8 @@ Oberfläche laufen nur im echten Browser und werden dort manuell geprüft
## Produktion
Zum lokalen Nachschauen ohne Passwort:
```bash
npm run build
npm run preview
@@ -152,6 +154,37 @@ npm run preview
`preview` hört auf `PORT` aus der Umgebung (Vorgabe `4173`, falls `PORT`
nicht gesetzt ist).
Im Betrieb läuft stattdessen `server.py` — siehe nächster Abschnitt.
## Passwortschutz
Die veröffentlichte Seite ist passwortgeschützt. Geprüft wird **im Server**,
nicht im Browser: Ohne gültiges Passwort gibt `server.py` nichts heraus —
weder die Seite noch das gebündelte Skript. Ein Passwortschutz allein im
Browser wäre wirkungslos, weil das Passwort dann im ausgelieferten Skript
stünde und dort mitgelesen werden könnte.
```bash
APP_PASSWORD=dein-passwort python3 server.py
```
- Das Passwort kommt ausschließlich aus der Umgebungsvariable `APP_PASSWORD`
(siehe `.env.example`). Es steht **nicht** im Quelltext.
- **Fehlt die Variable, startet der Server nicht** und meldet das im Klartext.
Das ist Absicht: Ein vergessener Eintrag soll auffallen, statt stillschweigend
eine ungeschützte Seite ins Netz zu stellen.
- Abgefragt wird per HTTP-Basic-Auth. Der Browser fragt **einmal pro Sitzung**,
nicht bei jedem Seitenaufruf — beim Sortieren wäre das sonst unbrauchbar.
- Der **Benutzername ist beliebig**; geprüft wird nur das Passwort. Beim
Vergleich wird eine zeitkonstante Prüfung verwendet.
- `/healthz` antwortet **ohne** Passwort mit `ok`. Das braucht die Überwachung
der Hosting-Umgebung, die den geschützten Dienst sonst für ausgefallen hielte.
Der Pfad gibt nichts über die Anwendung preis.
Das Passwort wandert bei einer Basic-Auth-Anmeldung mit jeder Anfrage über die
Leitung. Das ist unbedenklich, solange die Seite über HTTPS ausgeliefert wird —
worauf du bei der Veröffentlichung achten solltest.
## Technik
- Vanilla JavaScript (ES Modules), kein Framework
@@ -182,6 +215,8 @@ nicht gesetzt ist).
| `src/ui/describe-spec.js` | Gemeinsame Kurzbeschreibung eines Specs (Kapazität, Rank, Geschwindigkeit, Bauform), wahlweise mit Platzhaltern für fehlende Felder; von Treffer-Rückmeldung, „Zuletzt"-Zeile und Sitzungsliste gemeinsam genutzt |
| `src/main.js` | Verdrahtung aller Module zur lauffähigen App |
| `src/styles.css` | Farbvariablen (Grün/Gelb/Rot der Ampel-Rückmeldung), Layout des Kamera-Vollbilds samt Zielrahmen, Stapel-Leiste und Aktionsknöpfe sowie die Overlays für Treffer-Rückmeldung, Rot-Dialog und Sitzungsliste |
| `server.py` | Auslieferung im Betrieb: statischer Server für `dist/` mit Passwortabfrage vor jeder Datei, plus passwortfreier Gesundheitspfad `/healthz`. Nur Python-Standardbibliothek, keine zusätzliche Abhängigkeit |
| `.vch/deploy.yaml` | Bau- und Startbefehl sowie Gesundheitspfad für die Hosting-Umgebung |
`spec`, `pn-decoder`, `ocr-extract`, `session`, `pipeline` und `storage` sind
reine Funktionen ohne Browser-Zugriff (kein `window`, `document` oder