From 2b40254024ba3aa255eda2a2b2dc0aaf611f4c33 Mon Sep 17 00:00:00 2001 From: Samuel Zielke Date: Tue, 13 Oct 2026 13:37:12 +0200 Subject: [PATCH] Erste Version --- .gitignore | 15 + README.md | 226 ++++++ TD-Pult.html | 1404 +++++++++++++++++++++++++++++++++++++ deploy/Dockerfile | 20 + deploy/README.md | 170 +++++ deploy/docker-compose.yml | 40 ++ deploy/install.sh | 42 ++ deploy/nginx.conf | 48 ++ deploy/site/.gitkeep | 0 9 files changed, 1965 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 TD-Pult.html create mode 100644 deploy/Dockerfile create mode 100644 deploy/README.md create mode 100644 deploy/docker-compose.yml create mode 100644 deploy/install.sh create mode 100644 deploy/nginx.conf create mode 100644 deploy/site/.gitkeep diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6746436 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +# Eigene Programme, Anweisungen und ältere Stände – gehören nicht ins Repo +privat/ +td-programm-*.json +td-anweisungen-*.json + +# Server-Geheimnisse +deploy/htpasswd +*.pem +*.key + +# Die ausgelieferte Seite entsteht auf dem Server aus TD-Pult.html +deploy/site/* +!deploy/site/.gitkeep + +.DS_Store diff --git a/README.md b/README.md new file mode 100644 index 0000000..953c17d --- /dev/null +++ b/README.md @@ -0,0 +1,226 @@ +# TD-Pult + +**Ein Regiepult im Browser für die Technische Direktion bei Live-Veranstaltungen.** + +TD-Pult ist eine einzelne HTML-Datei. Sie zeigt den Programmablauf einer Schicht, +den gerade laufenden Punkt, das nächste Video und eure eigenen Anweisungen an +das Team. Es gibt keinen Server, keine Datenbank und keine Installation: +Datei öffnen, Programm einpflegen, loslegen. + +``` +┌──────────────────────────────────────────────────────────────────────────┐ +│ 14:32:07 Jahrestagung 2026 · Stadthalle [Schicht 1] ◀ ▶ Jetzt Dateien ▾│ +├──────────────┬───────────────────────────────┬───────────────────────────┤ +│ ABLAUF │ 14:30 VORTRAG · NR. 4 │ Disposition │ Ablaufplan │ +│ 14:00 Musik │ Titel des Vortrags │ │ +│▶14:30 Vortrag│ ☐ Medien & Cams vorbereitet │ Gliederung des Vortrags │ +│ 14:55 Video │ ☐ Punkt erledigt │ mit Video-Cues und │ +│ 15:10 Pause │ │ Lesungen │ +│ │ NÄCHSTES VIDEO · in 6 Min. │ │ +│ │ ① vorbereitet ② sync ③ weiter │ │ +│ │ │ │ +│ │ MEINE ANWEISUNGEN │ │ +│ │ [Wichtig] Cam 2 auf Bühne │ │ +└──────────────┴───────────────────────────────┴───────────────────────────┘ +``` + +--- + +## Was das Tool kann + +- **Ablauf und Jetzt-Ansicht.** Die Uhr läuft live mit. Mit *Jetzt* springt die + Ansicht zum Punkt, der laut Uhrzeit gerade dran ist. +- **Nächstes Video.** Aus Uhrzeit und Zeitleiste jedes Punkts wird geschätzt, + wann welches Video kommt. Für jedes Video gibt es drei Schritte: + *vorbereitet → sync → abgespielt*. +- **Eigene Anweisungen** pro Programmpunkt oder für die ganze Schicht, mit + Kategorien (Normal, Wichtig, Kritisch …), Abhaken und Sortieren. Bausteine und + Teamnamen lassen sich per Klick einfügen. +- **Rechte Spalte** mit Disposition (Gliederung eines Vortrags), Ablaufplan + (Medienliste und offizieller Anweisungstext) und allgemeinen Absprachen. +- **Mehrere Schichten** (z. B. Vormittag und Nachmittag), jeweils mit eigener + Besetzung. +- **Optional:** eine angeschlossene zweite Sprache (Sync-Schritt) und + übertragene Programmteile (Stream). Ist beides aus, verschwinden die + zugehörigen Kennzeichen und Schritte. +- **Läuft offline.** Nach dem Laden braucht die Seite kein Netz mehr. + +--- + +## Schnellstart + +1. `TD-Pult.html` herunterladen und im Browser öffnen (Chrome, Edge, Firefox + oder Safari). +2. Oben auf **Programm bearbeiten** klicken. +3. Unter **Einstellungen** Veranstaltung, Ort, Kategorien und Bausteine + eintragen. +4. Unter **Schicht & Besetzung** Schichten anlegen und das Team eintragen. +5. Programmpunkte links mit **+ Neuer Programmpunkt** anlegen und rechts + Disposition und Ablaufplan füllen. +6. **Programm bearbeiten** wieder ausschalten. Jetzt seid ihr im Live-Betrieb. +7. Vor der Veranstaltung **Dateien → Programm sichern** und die JSON-Datei an + alle Geräte verteilen. + +Mitgeliefert ist nur ein Beispielpunkt. Alle Inhalte pflegt ihr selbst ein. + +--- + +## Die drei Modi + +| Modus | Schalter | Wofür | +|---|---|---| +| **Editieren** | `Editieren` / Taste `E` | Live-Betrieb, in dem man Anweisungen anlegen, ändern und sortieren kann | +| **Ansicht** | `Ansicht` / Taste `E` | Live-Betrieb, nur lesen und abhaken, für den ruhigen Blick während der Schicht | +| **Programm bearbeiten** | `Programm bearbeiten` | Programm, Schichten, Besetzung und Einstellungen pflegen | + +### Tastenkürzel + +| Taste | Wirkung | +|---|---| +| `←` `↑` / `→` `↓` | vorheriger / nächster Programmpunkt | +| `E` | zwischen Editieren und Ansicht wechseln | +| `Enter` | Anweisung speichern (`Shift`+`Enter` für einen Zeilenumbruch) | +| `Esc` | Bearbeitung einer Anweisung abbrechen | + +--- + +## Einstellungen + +Alles, was sich von Veranstaltung zu Veranstaltung unterscheidet, steht unter +**Programm bearbeiten → Einstellungen**: + +| Bereich | Inhalt | +|---|---| +| Veranstaltung und Ort | Name des Tools, Veranstaltung, Ort, Stadt, Sprache | +| Angeschlossene Sprache | an/aus, Name und Kürzel (für den Sync-Schritt) | +| Stream / Übertragung | an/aus, Quelle bzw. Ort | +| Bezeichnungen | Texte der Status-Knöpfe und Medien-Schritte sowie die Begriffe *Ablaufplan*, *Vorsitz*, *Tagesmotto* und *Redner*. So passt ihr das Tool an eure Sprache an, z. B. „Moderation“ statt „Vorsitz“ | +| Kategorien | Name und Farbe der Anweisungs-Kategorien. Die Sonderrolle *Vorbereitung* koppelt eine Kategorie an den Status „vorbereitet“ | +| Bausteine | Gruppen von Stichwörtern, die als Chips unter dem Eingabefeld erscheinen (Kameras, Quellen, Schnittarten …) | +| Auswahllisten | Arten von Programmpunkten, Medienarten, Zusatzaufgaben für die Vorbereitung | +| Automatische Hinweise | „Wenn der Titel X enthält, zeige Hinweis Y.“ Der Hinweis erscheint gelb in der Jetzt-Ansicht | + +--- + +## Eingabeformate + +### Zeitleiste (Ablaufplan → Zeitleiste) + +Eine Zeile pro Abschnitt, in der Reihenfolge des Vortrags: + +``` +Redner 2 +Block 8: 1=1, 2=6 +Redner 5 +``` + +`Redner 2` sind 2 Minuten ohne Medien. `Block 8: 1=1, 2=6` ist ein Abschnitt +von 8 Minuten, in dem Video 1 (ca. 1 Min.) und Video 2 (ca. 6 Min.) laufen. +Die Nummern beziehen sich auf die Spalte *Nr.* in der Medienliste. Daraus +rechnet das Tool die ungefähren Uhrzeiten der Videos aus. + +### Disposition und allgemeine Anweisungen + +``` +# EINLEITUNG (5 Min.) +Erster Gedanke + Unterpunkt + Unter-Unterpunkt +> [Das Video wird abgespielt (3:33)] +! [Zitat vorlesen] +``` + +| Zeilenanfang | Darstellung | +|---|---| +| `#` | Abschnittsüberschrift | +| `>` | Video-Cue, hervorgehoben | +| `!` | Lesung / Zitat | +| zwei Leerzeichen je Ebene | Einrückung | + +In der Ansicht *kompakt* sind nur die obersten Gliederungspunkte zu sehen. + +--- + +## Daten und Speicherung + +Es gibt zwei getrennte Datenbestände. Beide liegen im **`localStorage` des +Browsers** und werden bei jeder Änderung sofort gespeichert. + +| Bestand | Inhalt | Sichern / Laden | +|---|---|---| +| **Programm** | Schichten, Programmpunkte, Medien, Dispositionen, Besetzung, Einstellungen | *Dateien → Programm sichern / laden* | +| **Anweisungen** | eure Anweisungen und alle Häkchen | *Dateien → Anweisungen sichern / laden* | + +So kann eine Person das Programm vorbereiten und an alle verteilen, während +jeder am Pult seine eigenen Anweisungen behält. + +Gut zu wissen: + +- **Jedes Gerät und jeder Browser hat seinen eigenen Stand.** Übertragen wird + per JSON-Datei. +- **Der Stand hängt an der Adresse.** Eine lokal geöffnete Datei und dieselbe + Datei auf einem Server sind für den Browser zwei verschiedene Orte. +- **Wer die Website-Daten löscht, löscht auch die Anweisungen.** Vor jeder + Schicht einmal sichern. +- **Auf Auslieferungsstand zurücksetzen** stellt den Inhalt der HTML-Datei + wieder her. Eure Anweisungen bleiben dabei erhalten. + +Die exportierten Dateien heißen `td-programm-JJJJ-MM-TT.json` und +`td-anweisungen-JJJJ-MM-TT.json`. Die `.gitignore` hält sie aus dem Repo, +damit keine echten Programme oder Namen veröffentlicht werden. + +### Eigenes Programm fest einbauen (optional) + +Soll das Tool schon beim ersten Öffnen euer Programm zeigen, könnt ihr es in +die Datei einbetten. In `TD-Pult.html` steht in der ersten Zeile des Skripts: + +```js +const SEED = { … }; +``` + +Ersetzt das Objekt durch den Inhalt eurer exportierten `td-programm-….json`. +Speichert diese Fassung außerhalb des Repos, zum Beispiel im Ordner `privat/`, +den Git ignoriert. + +--- + +## Auf einem Server bereitstellen + +Im Ordner [`deploy/`](deploy/README.md) liegt alles, um TD-Pult per Docker und +nginx mit Passwortschutz auszuliefern, z. B. hinter dem Nginx Proxy Manager: + +```bash +cd deploy +docker run --rm -it httpd:alpine htpasswd -Bn td > htpasswd +sh install.sh +``` + +Die ausführliche Anleitung steht in [`deploy/README.md`](deploy/README.md). + +--- + +## Projektstruktur + +``` +td-pult/ +├── TD-Pult.html # das komplette Tool (HTML, CSS, JavaScript) +├── README.md +├── .gitignore +├── deploy/ # optionaler Server-Betrieb mit Docker + nginx +│ ├── README.md +│ ├── docker-compose.yml +│ ├── Dockerfile +│ ├── nginx.conf +│ ├── install.sh +│ └── site/ # hierhin kommt auf dem Server index.html +└── privat/ # eigene Programme und Sicherungen (nicht im Repo) +``` + +--- + +## Technik + +- Eine Datei, reines HTML, CSS und JavaScript, ohne Abhängigkeiten und ohne Build +- Keine externen Aufrufe, keine Schriften oder Skripte aus dem Netz +- Daten nur im Browser (`localStorage`, Schlüssel `td-programm-v1` und `td-notizen-v1`) +- Ausgelegt für Laptops und Monitore am Regieplatz ab ca. 1280 px Breite diff --git a/TD-Pult.html b/TD-Pult.html new file mode 100644 index 0000000..28cb130 --- /dev/null +++ b/TD-Pult.html @@ -0,0 +1,1404 @@ + + + + + +TD-Pult + + + + +
+
--:--:--TD-PULT
+
+
+ + + + + +
+
+
+ + +
+
+ +
+
+
+ +
+ + + + diff --git a/deploy/Dockerfile b/deploy/Dockerfile new file mode 100644 index 0000000..0a4e8d4 --- /dev/null +++ b/deploy/Dockerfile @@ -0,0 +1,20 @@ +# Optional: eigenständiges Image, falls du es in eine Registry schieben +# oder ohne Bind-Mounts betreiben willst. +# +# docker build -f deploy/Dockerfile -t td-pult:latest . +# docker run -d -p 8087:80 --name td-pult td-pult:latest +# +# Vor dem Bauen müssen deploy/htpasswd und deploy/site/index.html vorhanden sein. + +FROM nginx:1.27-alpine + +COPY deploy/nginx.conf /etc/nginx/conf.d/default.conf +COPY deploy/htpasswd /etc/nginx/.htpasswd +COPY deploy/site/ /usr/share/nginx/html/ + +RUN chmod 644 /etc/nginx/.htpasswd + +HEALTHCHECK --interval=30s --timeout=3s \ + CMD wget -qO- http://127.0.0.1/healthz || exit 1 + +EXPOSE 80 diff --git a/deploy/README.md b/deploy/README.md new file mode 100644 index 0000000..ce6b57a --- /dev/null +++ b/deploy/README.md @@ -0,0 +1,170 @@ +# TD-Pult im Container bereitstellen + +Nginx liefert die einzelne HTML-Datei aus und schützt sie mit Basic Auth. +TLS und Domain macht dein Nginx Proxy Manager. + +Alle Befehle im Ordner `deploy/` ausführen. Der Ordner `deploy/site/` enthält +die auszuliefernde Seite — dort muss `index.html` liegen, das ist die +umbenannte `TD-Pult.html`. + +--- + +## 1 · Passwortdatei anlegen + +Die Datei `htpasswd` enthält Benutzername und Passwort-Hash. Sie liegt bewusst +**nicht** im Repo und wird einmalig erzeugt: + +```bash +docker run --rm httpd:alpine htpasswd -Bbn td 'DEIN-PASSWORT' > htpasswd +chmod 600 htpasswd +``` + +`td` ist der Benutzername, `-B` erzwingt bcrypt. Passwort in einfachen +Anführungszeichen, damit Sonderzeichen von der Shell nicht angefasst werden. +Wenn es nicht im Shell-Verlauf landen soll: + +```bash +docker run --rm -it httpd:alpine htpasswd -Bn td > htpasswd # fragt interaktiv +``` + +Weiteren Benutzer ergänzen: + +```bash +docker run --rm httpd:alpine htpasswd -Bbn regie 'ANDERES-PASSWORT' >> htpasswd +``` + +Passwort später ändern: Datei neu erzeugen, dann `docker compose restart`. + +--- + +## 2 · Seite bereitstellen + +```bash +mkdir -p site +cp ../TD-Pult.html site/index.html # oder die hochgeladene Datei dorthin kopieren +``` + +Anschließend die Rechte setzen — nginx läuft im Container als eigener Benutzer +und kann Dateien mit Modus 600 nicht lesen. Das äußert sich als **403 Forbidden** +nach erfolgreichem Login: + +```bash +chmod 755 site +chmod 644 site/index.html +``` + +Gemountet wird der **Ordner** `site/`, nicht die einzelne Datei. Einzeldatei-Mounts +brechen, sobald ein Editor oder eine Synchronisierung die Datei ersetzt statt sie +zu beschreiben — und wenn die Datei beim Start fehlt, legt Docker an ihrer Stelle +stillschweigend ein Verzeichnis an. Genau das erzeugt die Meldung +*„not a directory: Are you trying to mount a directory onto a file“*. + +Wenn das schon passiert ist, den leeren Ordner entfernen und neu starten: + +```bash +docker rm -f td-pult +rmdir ../TD-Pult.html # der versehentlich angelegte Ordner +mkdir -p site && cp /pfad/zu/TD-Pult.html site/index.html +docker compose up -d +``` + +Alternativ macht `sh install.sh` diese Schritte inklusive Aufräumen und Prüfung. + +Ausgeliefert wird die leere Vorlage. Euer Programm kommt anschließend im Browser +über *Dateien → Programm laden* hinein (JSON-Datei aus *Programm sichern*). + +--- + +## 3 · Container starten + +```bash +docker compose up -d +docker compose logs -f # bei Bedarf +curl -I http://localhost:8087/ # muss 401 liefern – dann greift der Schutz +``` + +Der Container hört auf Port **8087**. + +--- + +## 4 · Nginx Proxy Manager einrichten + +Neuer *Proxy Host*: + +| Feld | Wert | +|---|---| +| Domain Names | z. B. `td.example.com` | +| Scheme | `http` | +| Forward Hostname / IP | IP des Docker-Hosts (bei Portfreigabe) oder `td-pult` (bei gemeinsamem Netz) | +| Forward Port | `8087` bzw. `80` | +| Cache Assets | **aus** – sonst siehst du Änderungen verzögert | +| Block Common Exploits | an | +| Websockets Support | aus | + +Reiter *SSL*: Let's-Encrypt-Zertifikat anfordern, **Force SSL**, **HTTP/2** +und **HSTS** aktivieren. Basic Auth überträgt das Passwort sonst im Klartext. + +Läuft der Proxy Manager selbst als Container, erreicht er `127.0.0.1` des Hosts +nicht. Dann entweder die Host-IP eintragen oder beide Container ins selbe +Docker-Netz hängen (siehe Kommentare in `docker-compose.yml`) und auf +`td-pult:80` zeigen. + +Optional zusätzlich: in NPM unter *Access Lists* eine Liste mit Passwortschutz +anlegen und dem Proxy Host zuweisen. Der Schutz im Container bleibt trotzdem +aktiv — dann fragt es zweimal, deshalb entweder das eine oder das andere. + +--- + +## 5 · Aktualisieren + +Neue Version des Tools einspielen: + +```bash +cp /pfad/zur/neuen/TD-Pult.html site/index.html +chmod 644 site/index.html +``` + +Mehr ist nicht nötig — der Ordner ist gemountet, nginx liest die Datei bei jeder +Anfrage neu. Ein `docker compose restart` braucht es nur, wenn du `nginx.conf` +oder `htpasswd` geändert hast. + +Da `Cache-Control: no-store` gesetzt ist, lädt der Browser beim nächsten Aufruf +die neue Fassung. Wenn du eine ältere Version testen willst, einfach die Datei +tauschen — das Image bleibt unverändert. + +--- + +## 6 · Was du wissen solltest + +**Deine Daten liegen im Browser, nicht auf dem Server.** Anweisungen, Haken und +das bearbeitete Programm stehen im `localStorage` der jeweiligen Domain. Daraus +folgt: + +- Jedes Gerät hat seinen eigenen Stand. Übertragung über *Dateien → Programm + sichern* bzw. *Anweisungen sichern* und auf dem anderen Gerät laden. +- Der Stand hängt an der Domain. Rufst du das Tool später unter einer anderen + Adresse auf, ist es leer — vorher sichern. +- Wer im Browser die Website-Daten löscht, löscht auch deine Anweisungen. + Vor der Schicht einmal sichern. + +**Ausfall am Platz.** Die Seite läuft nach dem Laden vollständig im Browser. +Bricht das Netz weg, arbeitet der bereits geöffnete Tab normal weiter — nur ein +Neuladen geht dann nicht mehr. Halte die Datei zusätzlich lokal vor, dann kannst +du sie im Notfall per Doppelklick öffnen. + +**Basic Auth** ist nur so sicher wie die Leitung. Ausschließlich über HTTPS +betreiben und `Force SSL` im Proxy setzen. + +--- + +## Dateien + +| Datei | Zweck | +|---|---| +| `docker-compose.yml` | Container-Definition, Ports, Mounts | +| `site/index.html` | Die ausgelieferte Seite — hierhin `TD-Pult.html` kopieren | +| `install.sh` | Richtet ein bzw. repariert einen fehlgeschlagenen Start | +| `nginx.conf` | Auslieferung, Basic Auth, Header, Healthcheck | +| `htpasswd` | Benutzer und Passwort-Hash — selbst erzeugen, nicht teilen | +| `Dockerfile` | Optional: eigenständiges Image für eine Registry | +| `../.gitignore` | Hält `htpasswd` und `site/index.html` aus der Versionierung | diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml new file mode 100644 index 0000000..03d72f7 --- /dev/null +++ b/deploy/docker-compose.yml @@ -0,0 +1,40 @@ +services: + td-pult: + image: nginx:1.27-alpine + container_name: td-pult + restart: unless-stopped + + volumes: + # Ordner-Mount statt Einzeldatei: robuster beim Austauschen der Datei + # und Docker kann nicht versehentlich einen Ordner anlegen. + # In ./site muss eine index.html liegen (= TD-Pult.html umbenannt). + - ./site:/usr/share/nginx/html:ro + - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro + - ./htpasswd:/etc/nginx/.htpasswd:ro + + # Variante A – Portfreigabe (funktioniert immer). + # Nginx Proxy Manager zeigt dann auf http://:8087 + ports: + - "8087:80" + + # Variante B – gemeinsames Docker-Netz mit dem Proxy. + # Dann oben "ports" auskommentieren, hier das Netz des Proxys eintragen + # (Name mit docker network ls nachsehen) und im Proxy auf td-pult : 80 zeigen. + # networks: + # - proxy + + healthcheck: + test: ["CMD", "wget", "-qO-", "http://127.0.0.1/healthz"] + interval: 30s + timeout: 3s + retries: 3 + + logging: + driver: json-file + options: + max-size: "5m" + max-file: "3" + +# networks: +# proxy: +# external: true diff --git a/deploy/install.sh b/deploy/install.sh new file mode 100644 index 0000000..f4d6320 --- /dev/null +++ b/deploy/install.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env sh +# Einrichtung / Reparatur auf dem Server. Im Ordner deploy/ ausführen: sh install.sh +set -e +cd "$(dirname "$0")" + +# 1 · Reste eines fehlgeschlagenen Einzeldatei-Mounts aufräumen +docker rm -f td-pult 2>/dev/null || true +for p in ../TD-Pult.html ./TD-Pult.html; do + if [ -d "$p" ]; then echo "Räume leeren Ordner $p auf"; rmdir "$p" 2>/dev/null || true; fi +done + +# 2 · Website-Ordner vorbereiten +mkdir -p site +if [ -f ../TD-Pult.html ]; then + cp ../TD-Pult.html site/index.html +fi +if [ ! -f site/index.html ]; then + echo "FEHLER: site/index.html fehlt. Lade TD-Pult.html hoch und kopiere sie:" + echo " cp /pfad/zu/TD-Pult.html site/index.html" + exit 1 +fi + +# 3 · Rechte setzen – nginx im Container liest als eigener Benutzer +chmod 755 site +chmod 644 site/index.html + +# 4 · Passwortdatei prüfen +if [ ! -s htpasswd ]; then + echo "FEHLER: htpasswd fehlt. Einmalig anlegen:" + echo " docker run --rm httpd:alpine htpasswd -Bbn td 'DEIN-PASSWORT' > htpasswd" + exit 1 +fi +chmod 600 htpasswd + +# 5 · Starten und prüfen +docker compose up -d +sleep 2 +echo +echo "Erwartet wird 401 Unauthorized:" +docker run --rm --network host curlimages/curl:latest -s -o /dev/null -w 'HTTP %{http_code}\n' http://127.0.0.1:8087/ \ + || curl -s -o /dev/null -w 'HTTP %{http_code}\n' http://127.0.0.1:8087/ \ + || echo "(Prüfung übersprungen – bitte manuell: curl -I http://127.0.0.1:8087/)" diff --git a/deploy/nginx.conf b/deploy/nginx.conf new file mode 100644 index 0000000..259e7a0 --- /dev/null +++ b/deploy/nginx.conf @@ -0,0 +1,48 @@ +server { + listen 80; + server_name _; + + root /usr/share/nginx/html; + index index.html; + + # Echte Besucher-IP aus dem Reverse Proxy übernehmen (Docker-Netze) + set_real_ip_from 10.0.0.0/8; + set_real_ip_from 172.16.0.0/12; + set_real_ip_from 192.168.0.0/16; + real_ip_header X-Forwarded-For; + real_ip_recursive on; + + # ---- Passwortschutz ------------------------------------------------- + auth_basic "TD-Pult"; + auth_basic_user_file /etc/nginx/.htpasswd; + + # ---- Kein Caching: eine neue HTML-Datei ist sofort aktiv ------------ + add_header Cache-Control "no-store, must-revalidate" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header Referrer-Policy "no-referrer" always; + add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), interest-cohort=()" always; + + gzip on; + gzip_types text/plain application/json; # text/html macht nginx immer + gzip_min_length 1024; + + client_max_body_size 2m; + + # Statusabfrage für den Healthcheck – ohne Passwort + location = /healthz { + auth_basic off; + access_log off; + default_type text/plain; + return 200 "ok\n"; + } + + location / { + try_files $uri $uri/ /index.html; + } + + # Versteckte Dateien nie ausliefern + location ~ /\. { + deny all; + } +} diff --git a/deploy/site/.gitkeep b/deploy/site/.gitkeep new file mode 100644 index 0000000..e69de29