Restructure deploy guide: generate admin credentials from the built image

The ad-hoc `npm install bcryptjs` in a throwaway container hit npm's known
"Tracker 'idealTree' already exists" bug. Reorder Teil B so the admin
service is built first (a proper `npm ci`, the same path already used for
the real deployment), then generate the password hash and session secret
via `docker compose run --rm admin ...` against that image instead.
This commit is contained in:
Pascal Scholz 2026-09-21 22:00:02 +02:00
parent ab7d506eea
commit 6478e87fb3

View file

@ -72,34 +72,15 @@ cd ~/sites/larpaso-website
git pull git pull
``` ```
### B2. Login-Zugangsdaten erzeugen ### B2. Platzhalter für `~/stack/admin.env` anlegen (NICHT ins Git-Repo!)
Der Server selbst braucht dafür kein Node/npm installiert zu haben – das läuft in einem Erstmal nur eine leere Datei, damit Docker Compose in B3/B5 nicht meckert, dass sie fehlt.
kurzlebigen Docker-Container (Node steckt nur in den Containern, nicht auf dem Host): Die echten Werte kommen in B6 rein.
```bash ```bash
docker run --rm -e PW='dein-neues-passwort' node:22-alpine sh -c \ touch ~/stack/admin.env
"npm install --no-save bcryptjs >/dev/null 2>&1 && \
node -e \"console.log(require('bcryptjs').hashSync(process.env.PW, 12))\""
```
Gibt einen bcrypt-Hash aus (beginnt mit `$2a$` oder `$2b$`) – merken/kopieren.
Zufälliges Session-Secret erzeugen:
```bash
docker run --rm node:22-alpine node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
```
Auch kopieren. (Beide Befehle laden einmalig das `node:22-alpine`-Image, das ohnehin für
den Website-Build gebraucht wird – geht beim zweiten Mal deutlich schneller.)
### B3. `~/stack/admin.env` anlegen (NICHT ins Git-Repo!)
```bash
cat > ~/stack/admin.env <<'EOF'
NODE_ENV=production
ADMIN_USERNAME=pascal
ADMIN_PASSWORD_HASH=<bcrypt-Hash aus B2 hier einfügen>
SESSION_SECRET=<Hex-String aus B2 hier einfügen>
EOF
chmod 600 ~/stack/admin.env chmod 600 ~/stack/admin.env
``` ```
### B4. Dienste in `~/stack/docker-compose.yml` ergänzen ### B3. Dienste in `~/stack/docker-compose.yml` ergänzen
```bash ```bash
nano ~/stack/docker-compose.yml nano ~/stack/docker-compose.yml
``` ```
@ -139,7 +120,7 @@ volumes:
admin-uploads: admin-uploads:
``` ```
### B5. Caddy-Block ergänzen ### B4. Caddy-Block ergänzen
```bash ```bash
nano ~/stack/caddy/Caddyfile nano ~/stack/caddy/Caddyfile
``` ```
@ -150,22 +131,58 @@ admin.larpaso.de {
} }
``` ```
### B6. DNS bei IONOS ### B5. Admin-Image bauen
- Neuer A-Eintrag: Hostname `admin` → Server-IPv4 (gleiche IP wie `@`).
- Kurz warten/prüfen: `nslookup admin.larpaso.de` muss die Server-IP liefern, bevor B7
läuft (sonst bekommt Caddy kein Zertifikat).
### B7. Bauen & starten
```bash ```bash
cd ~/stack cd ~/stack
docker compose build website admin 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 hash-password.js "dein-neues-passwort"
```
Gibt einen bcrypt-Hash aus (beginnt mit `$2a$` oder `$2b$`) – merken/kopieren.
Zufälliges Session-Secret erzeugen:
```bash
docker compose run --rm admin node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
```
Auch kopieren.
### B7. `~/stack/admin.env` mit echten Werten befüllen
```bash
cat > ~/stack/admin.env <<'EOF'
NODE_ENV=production
ADMIN_USERNAME=pascal
ADMIN_PASSWORD_HASH=<bcrypt-Hash aus B6 hier einfügen>
SESSION_SECRET=<Hex-String aus B6 hier einfügen>
EOF
chmod 600 ~/stack/admin.env
```
### 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 up -d website admin
docker compose restart caddy docker compose restart caddy
docker compose logs -f caddy # auf "certificate obtained successfully" für admin.larpaso.de warten, dann Strg+C 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.)
### B8. Prüfen ### B10. Prüfen
1. `https://admin.larpaso.de/admin/login` öffnen, mit den Zugangsdaten aus B2/B3 anmelden. 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 2. Eine Kleinigkeit ändern (z. B. Telefonnummer im Abschnitt „Kontakt" oder eine Farbe im
Abschnitt „Design – Farben"), **Speichern & veröffentlichen** klicken. Abschnitt „Design – Farben"), **Speichern & veröffentlichen** klicken.
3. Build-Ausgabe sollte mit „Complete!" enden. 3. Build-Ausgabe sollte mit „Complete!" enden.
@ -191,7 +208,8 @@ cd ~/stack && docker compose build admin && docker compose up -d admin
## Bekannte Grenzen ## Bekannte Grenzen
- Nur ein Login (kein Mehrbenutzer-System, keine Passwort-Reset-Funktion in der - Nur ein Login (kein Mehrbenutzer-System, keine Passwort-Reset-Funktion in der
Oberfläche – neues Passwort = neuer Hash in `admin.env` + `docker compose up -d admin`). Oberfläche – neues Passwort: `docker compose run --rm admin node hash-password.js "..."`
ausführen (wie B6), neuen Hash in `admin.env` eintragen, dann `docker compose up -d admin`).
- Sessions sind In-Memory: Ein Neustart des `admin`-Containers meldet alle ab. - 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 - 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 Server und kann bei Bedarf manuell gesichert werden (z. B. regelmäßiges Backup der