larpaso-website/DEPLOYMENT_ADMIN.md
Pascal Scholz 7c67ae1ff5 Add WYSIWYG rich-text editor and reorganize admin UI into tabs
Rich-text fields (hero lead, paragraphs, FAQ-style copy, datenschutz
sections, etc.) now use a proper toolbar editor - bold/italic/underline
buttons plus a per-selection font picker drawn from the same curated
font list as the Design tab - instead of typed **markdown** syntax.

Content is stored as a small allowlisted HTML subset (strong/em/u/span
with a validated data-font/br) and sanitized server-side with
sanitize-html on every save (admin/lib/rich-sanitize.js), independent
of what the client sends - verified this rejects script tags, event
handlers, and made-up font names while preserving legitimate markup,
and that re-saving already-sanitized content is a no-op.

The Astro site mirrors the same allowlist at render time for
defense-in-depth (src/lib/rich-sanitize.ts) and only requests Google
Fonts for fonts actually referenced somewhere in content, in addition
to the two Design-tab fonts - verified a font used via a data-font span
gets its own Google Fonts request and [data-font] CSS rule in the
built page, and that unused fonts don't get requested.

Migrated the three existing fields that used the old markdown-lite
syntax (**bold**/*italic*) to real HTML so they keep rendering
correctly under the new system.

Also replaces the single long scrolling admin page with a tabbed
sidebar (grouped "Inhalte"/"Design") to cut down on clutter, per
feedback that the all-in-one-page layout was hard to navigate.
2026-09-21 23:11:19 +02:00

255 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# DEPLOYMENT_ADMIN.md – Login-Editor für larpaso.de
Ergänzt DEPLOYMENT.md um einen eigenen, öffentlich erreichbaren, aber login-geschützten
Editor unter **`https://admin.larpaso.de`**. Dort können Texte, Bilder, Links, Impressum,
Datenschutz sowie Schriftarten/-größen und alle Farben der Seite geändert werden.
„Speichern & veröffentlichen" baut die Seite neu und die Änderung ist sofort auf
`larpaso.de` sichtbar – ohne weiteren Server-Zugriff nötig.
Wie in DEPLOYMENT.md: **Teil A** ist bereits im Repo erledigt (Claude Code) und lokal
getestet. **Teil B** unten führst du selbst per SSH auf dem Server aus – das ist eine
vollständige Schritt-für-Schritt-Anleitung zum Copy-Pasten. Claude Code hat zu keinem
Zeitpunkt Zugriff auf Benutzername/Passwort/Secrets oder den Server selbst.
---
## Was der Editor kann
- **Texte**: alle Überschriften, Absätze, Listen, FAQ, Preis-Punkte, Mappe-Einträge usw.
der Startseite – inklusive Impressum und Datenschutzerklärung. Längere Texte haben einen
Rich-Text-Editor mit Knöpfen für Fett/Kursiv/Unterstrichen sowie eine Schriftart-Auswahl,
mit der einzelne markierte Wörter/Sätze eine eigene Schriftart bekommen können (aus der
gleichen kuratierten Liste wie im Design-Tab) – unabhängig von den zwei Grundschriften.
- **Bilder**: Logo, Mappe-Screenshots, Team-Foto, plus optional eigene Bilder anstelle der
eingebauten Google-Vorschau und des PDF/Tabellen-Vergleichs.
- **Links**: Menüpunkte, E-Mail-Adresse, Kontakt-Angaben.
- **Design**: Schriftart für Überschriften/Fließtext (Auswahl aus gängigen Google Fonts
oder Systemschrift), Schriftgrößen (7 Stufen, per Schieberegler), und *alle* Farben der
Seite einzeln über Hex-Feld, RGB-Schieberegler und Transparenz-Regler.
- **Übersicht**: die Oberfläche ist in Reiter/Tabs gegliedert (linke Seitenleiste, gruppiert
in „Inhalte" und „Design") statt einer langen durchgehenden Seite.
---
## Was sich technisch ändert
- Neuer Dienst `admin` (Node/Express, `admin/`) mit Login, Bearbeitungsoberfläche und
einem „Veröffentlichen"-Knopf, der intern `npm run build` ausführt.
- Der bestehende `website`-Dienst (nginx) serviert nicht mehr aus dem Image gebackene
Dateien, sondern aus einem **gemeinsamen Volume** (`site-dist`), das der `admin`-Dienst
nach jedem „Veröffentlichen" neu befüllt. Kein Neustart von `website` nötig.
- Bearbeitete Inhalte (`content/site.json`, `content/theme.json`) und hochgeladene Bilder
(`public/uploads/`) liegen auf eigenen Volumes, damit sie ein Image-Update überleben.
- Login: ein oder mehrere feste Benutzername/Passwort-Paare (bcrypt-Hash), Session-Cookie
(12h, httpOnly, Secure, SameSite=Strict), Rate-Limit (5 Fehlversuche → 15 Min. Sperre pro IP).
---
## TEIL A – im Repo (bereits erledigt)
- `admin/` – Editor-Backend + -Oberfläche (Login, Formulare, Bild-Upload, Farb-/Font-/
Größen-Editor, Build-Trigger).
- `content/site.json`, `content/theme.json` – alle editierbaren Inhalte bzw. Design-Werte.
- `Dockerfile.admin` – Image für den `admin`-Dienst.
- `Dockerfile` (angepasst) – `website` seedet ein leeres `site-dist`-Volume beim ersten
Start aus dem gebackenen Build, statt Dateien fest einzubacken.
- `docker-entrypoint-seed.sh`, `docker-entrypoint-admin-seed.sh` – Seed-Skripte für die
Volumes.
- Lokal geprüft: `npm run build` läuft fehlerfrei; Login, Rate-Limit-Sperre, Speichern
(Texte, Bilder, Impressum/Datenschutz, Farben, Schriftarten, Schriftgrößen),
Bild-Upload und Veröffentlichen wurden lokal gegen den Admin-Server End-to-End getestet.
---
## TEIL B – Schritt für Schritt per SSH (machst du)
Alles unten in dieser Reihenfolge ausführen. Wo `<...>` steht, durch echte Werte ersetzen.
### B0. Voraussetzung: Code muss auf dem Server ankommen können
Damit `git pull` in B1 etwas findet, muss dieser Stand vorher nach
`git.larpaso.de/pascal/larpaso-website` gepusht sein. Frag im Chat nach, ob das schon
passiert ist, falls du direkt startest.
### B1. Repo aktualisieren
```bash
ssh deploy@<server>
cd ~/sites/larpaso-website
git pull
```
### B2. Platzhalter für `~/stack/admin.env` anlegen (NICHT ins Git-Repo!)
Erstmal nur eine leere Datei, damit Docker Compose in B3/B5 nicht meckert, dass sie fehlt.
Die echten Werte kommen in B6 rein.
```bash
touch ~/stack/admin.env
chmod 600 ~/stack/admin.env
```
### B3. Dienste in `~/stack/docker-compose.yml` ergänzen
```bash
nano ~/stack/docker-compose.yml
```
Beim bestehenden `website`-Dienst ein `volumes:` ergänzen:
```yaml
website:
build:
context: /home/deploy/sites/larpaso-website
restart: unless-stopped
networks: [web]
volumes:
- site-dist:/usr/share/nginx/html
```
Neuen `admin`-Dienst unter `services:` ergänzen:
```yaml
admin:
build:
context: /home/deploy/sites/larpaso-website
dockerfile: Dockerfile.admin
restart: unless-stopped
networks: [web]
env_file:
- /home/deploy/stack/admin.env
volumes:
- site-dist:/site-dist
- admin-content:/app/content
- admin-uploads:/app/public/uploads
```
(Kein `ports:` bei beiden Diensten – Caddy erreicht sie intern über den Container-Namen.)
Im bestehenden **obersten** `volumes:`-Block der Datei ergänzen (nicht neu anlegen):
```yaml
volumes:
site-dist:
admin-content:
admin-uploads:
```
### B4. Caddy-Block ergänzen
```bash
nano ~/stack/caddy/Caddyfile
```
Hinzufügen:
```caddyfile
admin.larpaso.de {
reverse_proxy admin:4321
}
```
### B5. Admin-Image bauen
```bash
cd ~/stack
docker compose build admin
```
Das baut das Image für den Login-Editor – inklusive eines sauberen `npm ci` für die
Admin-Abhängigkeiten (bcryptjs, Express usw.), im selben zuverlässigen Schritt, der auch
gleich für den Live-Betrieb verwendet wird.
### B6. Login-Zugangsdaten erzeugen
Jetzt mit dem gerade gebauten Image statt einem Wegwerf-Container – robuster als ein
Ad-hoc-`npm install` (das kann mit dem bekannten npm-Fehler „Tracker 'idealTree' already
exists" fehlschlagen):
```bash
docker compose run --rm admin node admin/hash-password.js "dein-neues-passwort" | sed 's/\$/$$/g'
```
Gibt einen bcrypt-Hash aus, bei dem jedes `$` bereits verdoppelt ist (`$$2a$$12$$...`) –
**genau so, mit den doppelten `$`, in B7 einfügen.** Docker Compose interpretiert einzelne
`$` in `env_file`-Werten nämlich selbst als Variablen (siehe Warnung weiter unten, falls
das schiefgeht) – `$$` ist Compose' eigene Escape-Regel dafür, kein Tippfehler.
Zufälliges Session-Secret erzeugen (rein hexadezimal, kein `$` enthalten, keine
Verdopplung nötig):
```bash
docker compose run --rm admin node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
```
Auch kopieren.
**Optional – zweiten Benutzer anlegen** (z. B. für Lara): denselben Hash-Befehl nochmal
mit ihrem Passwort ausführen und den (verdoppelten) Hash ebenfalls kopieren. Ein
Session-Secret reicht für alle Benutzer, das muss nicht nochmal erzeugt werden.
```bash
docker compose run --rm admin node admin/hash-password.js "ihr-neues-passwort" | sed 's/\$/$$/g'
```
### B7. `~/stack/admin.env` mit echten Werten befüllen
Am einfachsten mit `nano` (kein Heredoc nötig):
```bash
nano ~/stack/admin.env
```
Inhalt (Beispiel – deine Werte aus B6 einsetzen, die Hashes **mit den doppelten `$`**
genau so wie von `sed` ausgegeben, nicht die ursprüngliche, unverdoppelte Variante):
```
NODE_ENV=production
ADMIN_USERNAME=pascal
ADMIN_PASSWORD_HASH=$$2a$$12$$dein.eigentlicher.hash.hier
SESSION_SECRET=dein-hex-string-hier
```
**Nur falls du einen zweiten Benutzer angelegt hast**, zwei weitere Zeilen ergänzen
(Muster lässt sich mit `_3`, `_4`, ... beliebig fortsetzen):
```
ADMIN_USERNAME_2=lara
ADMIN_PASSWORD_HASH_2=$$2a$$12$$laras.hash.hier
```
Speichern: `Strg+O`, `Enter`, `Strg+X`. Danach:
```bash
chmod 600 ~/stack/admin.env
```
**Kontrolle** (jeder Hash muss inklusive der verdoppelten `$` **63 Zeichen** lang sein –
60 für den eigentlichen bcrypt-Hash + 3 zusätzliche `$`):
```bash
grep HASH ~/stack/admin.env | awk -F= '{print $1": "length($2)" Zeichen"}'
```
### B8. DNS bei IONOS
- Neuer A-Eintrag: Hostname `admin` → Server-IPv4 (gleiche IP wie `@`).
- Kurz warten/prüfen: `nslookup admin.larpaso.de` muss die Server-IP liefern, bevor B9
läuft (sonst bekommt Caddy kein Zertifikat).
### B9. Bauen & starten
```bash
cd ~/stack
docker compose build website
docker compose up -d website admin
docker compose restart caddy
docker compose logs -f caddy # auf "certificate obtained successfully" für admin.larpaso.de warten, dann Strg+C
```
(`admin` wurde schon in B5 gebaut – falls du seitdem nochmal `git pull` gemacht hast,
einmal `docker compose build admin` vorweg wiederholen.)
### B10. Prüfen
1. `https://admin.larpaso.de/admin/login` öffnen, mit den Zugangsdaten aus B6/B7 anmelden.
2. Eine Kleinigkeit ändern (z. B. Telefonnummer im Abschnitt „Kontakt" oder eine Farbe im
Abschnitt „Design – Farben"), **Speichern & veröffentlichen** klicken.
3. Build-Ausgabe sollte mit „Complete!" enden.
4. `https://larpaso.de` neu laden (ggf. Hard-Reload / Cache leeren) – Änderung sollte
sofort da sein.
Fertig. Ab jetzt kannst du alle Texte, Bilder, Links und das Design selbst über
`admin.larpaso.de` pflegen, ohne mich oder SSH zu brauchen.
---
## Künftige Updates am Code (Admin-Backend selbst)
Ändert sich `admin/` oder das Editor-Frontend am Code (nicht über die Editor-Oberfläche,
sondern durch mich im Chat), reicht danach:
```bash
cd ~/sites/larpaso-website && git pull
cd ~/stack && docker compose build admin && docker compose up -d admin
```
`content/site.json`, `content/theme.json` und hochgeladene Bilder bleiben unangetastet
(liegen auf eigenen Volumes, nicht im Image).
## Bekannte Grenzen
- Mehrere feste Logins möglich (`ADMIN_USERNAME`/`ADMIN_PASSWORD_HASH`, `_2`, `_3`, ...),
aber kein echtes Mehrbenutzer-System – alle Logins haben dieselben Rechte, es gibt keine
Rollen und keine Passwort-Reset-Funktion in der Oberfläche. Neues/geändertes Passwort:
`docker compose run --rm admin node admin/hash-password.js "..."` ausführen (wie B6),
Hash in `admin.env` eintragen, dann `docker compose up -d admin`.
- Sessions sind In-Memory: Ein Neustart des `admin`-Containers meldet alle ab.
- Es gibt keine Versionshistorie der Inhalte oder des Designs – beides liegt aber auf dem
Server und kann bei Bedarf manuell gesichert werden (z. B. regelmäßiges Backup der
Volumes `admin-content` und `admin-uploads` einplanen, wie für die anderen Volumes auch).
- Schriftarten sind auf eine kuratierte Liste gängiger Google Fonts + Systemschrift
begrenzt (keine eigenen Font-Dateien hochladbar) – bei Bedarf im Chat erweiterbar.