2 Commits

Author SHA1 Message Date
a9057b25dd Add a copy-paste command list to the staging runbook
The runbook explained every step but had no way to just work through it. Adds a checklist
and all commands in one block up front, with the prose below as the reference for what a
step does and how it fails.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 22:29:35 +02:00
769d06e04e Add runbook for setting up the staging environment
Standalone instructions for whoever sets up testmodul.qrmaster.net on the production
server. Written to be followed without prior context: explicit paths, an upfront list of
what must not be touched, and a stop condition in step 2 if the host URLs point at
production, which would send staging clicks into the live app.

Contains no credentials - .env.test is handed over separately.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 22:27:22 +02:00

View File

@@ -0,0 +1,314 @@
# Anleitung: Testumgebung testmodul.qrmaster.net aufsetzen
Diese Anleitung richtet auf dem Produktionsserver eine **zweite, getrennte Instanz** von
QR Master ein, erreichbar unter `testmodul.qrmaster.net`. Sie läuft auf dem Branch `test`
mit einer eigenen, leeren Datenbank.
Die laufende Produktion wird dabei **nicht angefasst**. Alle Schritte hier legen neue
Container, ein neues Volume und ein neues Verzeichnis an.
## Kurzfassung zum Abhaken
Wer die Begründungen nicht braucht, arbeitet diese Liste ab. Die ausführlichen Abschnitte
darunter erklären jeden Schritt und was schiefgehen kann.
- [ ] **1.** Repo klonen, Branch `test`, **eigenes Verzeichnis** neben der Produktion
- [ ] **2.** `.env.test` von Timo dort ablegen und die vier Kernwerte prüfen
- [ ] **3.** Stack bauen und starten (`-p qrmaster-test`)
- [ ] **4.** Schema aus Prod dumpen und einspielen - **kein** `prisma migrate`
- [ ] **5.** Testaccount per SQL anlegen (Registrierungsformular funktioniert nicht)
- [ ] **6.** Caddy-Block ergänzen und neu laden
- [ ] **7.** Abnahme: DB-Trennung, keine Migrationen, robots.txt, Browser-Test
### Alle Befehle am Stück
```bash
# 1 - Checkout (NICHT im Produktionsverzeichnis)
git clone -b test https://git.bizmatch.net/tknuth/QR-master.git qrmaster-test
cd qrmaster-test
git branch --show-current # muss "test" zeigen
# 2 - .env.test hier ablegen, dann pruefen
grep -E "NEXT_PUBLIC_WWW_URL|NEXT_PUBLIC_APP_URL|AUTH_COOKIE_NAME|POSTGRES_DB" .env.test
# Erwartet: beide URLs auf testmodul, AUTH_COOKIE_NAME=userId_test,
# POSTGRES_DB=qrmaster_test. Steht dort app./www. -> STOPP, siehe Schritt 2.
# 3 - Stack starten
docker compose -p qrmaster-test --env-file .env.test \
-f docker-compose.yml -f docker-compose.test.yml up -d --build
docker ps --filter "name=qrmaster-test" --format "table {{.Names}}\t{{.Status}}"
# 4 - Schema (nur Struktur, keine Kundendaten)
docker exec qrmaster-db pg_dump -U postgres --schema-only qrmaster > schema.sql
docker exec -i qrmaster-test-db psql -U postgres -d qrmaster_test < schema.sql
docker exec qrmaster-test-db psql -U postgres -d qrmaster_test -c "\dt" | head -20
# 5 - Testaccount: erst Hash erzeugen, dann in den INSERT einsetzen
docker exec qrmaster-test-web node -e "console.log(require('bcryptjs').hashSync('DEIN_TESTPASSWORT',12))"
docker exec -i qrmaster-test-db psql -U postgres -d qrmaster_test -c "INSERT INTO \"User\" (id,email,name,password,\"emailVerified\",\"updatedAt\") VALUES ('testuser1','test@qrmaster.net','Test','HIER_DER_HASH',now(),now());"
# 6 - Caddy: Block ergaenzen (siehe Schritt 6), dann
caddy reload --config /etc/caddy/Caddyfile
# 7 - Abnahme
docker exec qrmaster-test-db psql -U postgres -d qrmaster_test -c 'SELECT count(*) FROM "User";'
docker exec qrmaster-db psql -U postgres -d qrmaster -c 'SELECT count(*) FROM "User";'
docker logs qrmaster-test-web 2>&1 | head -20
curl -s https://testmodul.qrmaster.net/robots.txt
```
Die beiden `count(*)` müssen sich unterscheiden, in den Logs darf kein "Applying Prisma
migrations" stehen, und `robots.txt` muss `Disallow: /` liefern.
## Voraussetzungen
- SSH-Zugang zum Server, auf dem QR Master läuft
- Docker und Docker Compose (mindestens v2.24 - wird für `!override` und `!reset` gebraucht;
prüfen mit `docker compose version`)
- Schreibrechte auf die Caddy-Konfiguration
- Die Datei **`.env.test`** - die kommt von Timo und ist nicht im Repository, weil sie
Passwörter enthält
- Der DNS-Eintrag `testmodul.qrmaster.net` existiert bereits (CNAME)
## Was NICHT angefasst wird
- Das bestehende Produktionsverzeichnis: dort **nicht** den Branch wechseln. Ein späterer
Prod-Rebuild würde sonst Testcode bauen.
- Die Produktions-`.env`
- Die bestehenden Caddy-Blöcke für `www.qrmaster.net`, `app.qrmaster.net` und `qrmaster.net`
- Die Produktionsdatenbank. Der einzige Zugriff darauf ist ein `pg_dump --schema-only`,
das ausschließlich liest.
---
## 1. Zweites Checkout anlegen
**Nicht** im Produktionsverzeichnis arbeiten. Ein eigenes Verzeichnis daneben, z.B. im
selben übergeordneten Ordner:
```bash
git clone -b test https://git.bizmatch.net/tknuth/QR-master.git qrmaster-test
```
Danach in dieses Verzeichnis wechseln. **Alle weiteren Befehle laufen von dort**, sofern
nicht anders angegeben.
```bash
cd qrmaster-test
```
Prüfen, dass der richtige Branch ausgecheckt ist - es muss `test` erscheinen:
```bash
git branch --show-current
```
## 2. `.env.test` ablegen
Die von Timo erhaltene Datei als `.env.test` in dieses Verzeichnis legen (also
`qrmaster-test/.env.test`).
Kurz gegenprüfen, dass die vier wichtigsten Werte stimmen:
```bash
grep -E "NEXT_PUBLIC_WWW_URL|NEXT_PUBLIC_APP_URL|AUTH_COOKIE_NAME|POSTGRES_DB" .env.test
```
Erwartet:
```
NEXT_PUBLIC_WWW_URL=https://testmodul.qrmaster.net
NEXT_PUBLIC_APP_URL=https://testmodul.qrmaster.net
AUTH_COOKIE_NAME=userId_test
POSTGRES_DB=qrmaster_test
```
Steht bei einer der URLs `app.qrmaster.net` oder `www.qrmaster.net`, **nicht starten** -
dann würden Klicks in der Testumgebung in die Produktion umleiten.
## 3. Stack bauen und starten
```bash
docker compose -p qrmaster-test --env-file .env.test -f docker-compose.yml -f docker-compose.test.yml up -d --build
```
Der erste Build dauert einige Minuten. Der Projektname `-p qrmaster-test` ist wichtig: er
sorgt dafür, dass eigene Container und ein eigenes Volume entstehen und nichts aus der
Produktion überschrieben wird.
Läuft alles, sollten drei neue Container existieren:
```bash
docker ps --filter "name=qrmaster-test" --format "table {{.Names}}\t{{.Status}}"
```
Erwartet: `qrmaster-test-db`, `qrmaster-test-redis`, `qrmaster-test-web`.
Die Anwendung kann zu diesem Zeitpunkt noch nichts anzeigen - die Datenbank ist leer. Das
ist normal und wird im nächsten Schritt behoben.
## 4. Datenbankschema einspielen
Die Testdatenbank bekommt **nur die Struktur** aus der Produktion, keine Daten. Es werden
also keine Kundendaten kopiert.
Struktur aus der Produktionsdatenbank exportieren (reiner Lesezugriff):
```bash
docker exec qrmaster-db pg_dump -U postgres --schema-only qrmaster > schema.sql
```
In die Testdatenbank einspielen:
```bash
docker exec -i qrmaster-test-db psql -U postgres -d qrmaster_test < schema.sql
```
> **Wichtig:** Nicht `prisma migrate` verwenden. Die Migrationsdateien im Repository sind
> seit April 2026 nicht mehr gepflegt - alle Schemaänderungen seitdem wurden per SQL
> gemacht. Ein `migrate deploy` würde ein veraltetes Schema erzeugen, mit dem die
> Anwendung nicht läuft. Der Container startet deshalb bewusst ohne Migrationsschritt.
Prüfen, dass Tabellen angekommen sind:
```bash
docker exec qrmaster-test-db psql -U postgres -d qrmaster_test -c "\dt" | head -20
```
## 5. Testaccount anlegen
Die Registrierung über das Formular funktioniert hier **nicht**: die Testumgebung
verschickt bewusst keine E-Mails, und ohne Bestätigungsmail wird der Account vom System
wieder gelöscht. Der Account wird deshalb direkt in der Datenbank angelegt.
Zuerst einen Passwort-Hash erzeugen (`DEIN_TESTPASSWORT` durch ein selbst gewähltes
Passwort ersetzen):
```bash
docker exec qrmaster-test-web node -e "console.log(require('bcryptjs').hashSync('DEIN_TESTPASSWORT',12))"
```
Die Ausgabe ist eine Zeichenkette, die mit `$2a$12$` oder `$2b$12$` beginnt. Diese im
folgenden Befehl anstelle von `HIER_DER_HASH` einsetzen:
```bash
docker exec -i qrmaster-test-db psql -U postgres -d qrmaster_test -c "INSERT INTO \"User\" (id,email,name,password,\"emailVerified\",\"updatedAt\") VALUES ('testuser1','test@qrmaster.net','Test','HIER_DER_HASH',now(),now());"
```
Anmeldung erfolgt danach ganz normal über `/login` mit `test@qrmaster.net` und dem
gewählten Passwort.
## 6. Caddy konfigurieren
Einen neuen Block in die Caddy-Konfiguration aufnehmen (Pfad ggf. anpassen). Die
bestehenden Blöcke bleiben unverändert:
```caddyfile
testmodul.qrmaster.net {
reverse_proxy qrmaster-test-web:3000
}
```
Konfiguration neu laden:
```bash
caddy reload --config /etc/caddy/Caddyfile
```
Caddy holt das TLS-Zertifikat automatisch. Das kann eine Minute dauern.
## 7. Abnahme
**a) Datenbanken sind getrennt.** Die beiden Zahlen müssen sich unterscheiden - die
Testdatenbank enthält nur den eben angelegten Account:
```bash
docker exec qrmaster-test-db psql -U postgres -d qrmaster_test -c 'SELECT count(*) FROM "User";'
```
```bash
docker exec qrmaster-db psql -U postgres -d qrmaster -c 'SELECT count(*) FROM "User";'
```
**b) Keine Migrationen gelaufen.** In der Ausgabe darf **nicht** "Applying Prisma
migrations" stehen:
```bash
docker logs qrmaster-test-web 2>&1 | head -20
```
**c) Suchmaschinen ausgesperrt.** Muss `Disallow: /` liefern:
```bash
curl -s https://testmodul.qrmaster.net/robots.txt
```
**d) Im Browser:**
- `https://testmodul.qrmaster.net` lädt mit gültigem Zertifikat
- Anmeldung mit dem Testaccount funktioniert
- `https://testmodul.qrmaster.net/dashboard` **bleibt auf testmodul** und springt nicht auf
`app.qrmaster.net`. Passiert das doch, sind die URLs in der `.env.test` falsch.
- In den Entwicklertools unter Application → Cookies liegen zwei getrennte Cookies:
`userId` mit Domain `.qrmaster.net` (Produktion) und `userId_test` mit Domain
`testmodul.qrmaster.net`
- Die Produktion ist weiterhin erreichbar und man ist dort weiterhin angemeldet
---
## Laufender Betrieb
Neuen Stand deployen, nachdem auf dem Branch `test` etwas gepusht wurde - aus dem
Verzeichnis `qrmaster-test`:
```bash
git pull
```
```bash
docker compose -p qrmaster-test --env-file .env.test -f docker-compose.yml -f docker-compose.test.yml up -d --build
```
Ein Rebuild ist **immer** nötig, ein bloßer Neustart genügt nicht: die Host-URLs und der
Cookie-Name werden beim Bauen fest in die Anwendung kompiliert.
Schemaänderungen werden weiterhin **von Hand per SQL** ausgeführt - erst auf Test, nach
erfolgreicher Prüfung dasselbe Statement auf Produktion. Es gibt keinen automatischen Weg
dazwischen.
## Testumgebung stoppen oder entfernen
Stoppen, Daten bleiben erhalten:
```bash
docker compose -p qrmaster-test --env-file .env.test -f docker-compose.yml -f docker-compose.test.yml down
```
Vollständig entfernen inklusive Testdatenbank - der Projektname `-p qrmaster-test` sorgt
dafür, dass ausschließlich die Test-Volumes gelöscht werden:
```bash
docker compose -p qrmaster-test --env-file .env.test -f docker-compose.yml -f docker-compose.test.yml down -v
```
## Wenn etwas nicht funktioniert
| Symptom | Ursache |
|---|---|
| Build bricht ab mit `set AUTH_COOKIE_NAME in .env.test` | `.env.test` fehlt oder liegt im falschen Verzeichnis |
| `qrmaster-test-db` bleibt `unhealthy`, `web` startet nicht | In der `.env.test` steht nicht `POSTGRES_DB=qrmaster_test` |
| Caddy liefert 502 | Containername im Caddy-Block stimmt nicht, oder der Container läuft nicht - mit `docker ps` prüfen |
| Anwendung meldet `column ... does not exist` | Schema-Import aus Schritt 4 war unvollständig - erneut einspielen |
| `/dashboard` springt auf `app.qrmaster.net` | Die URLs in der `.env.test` zeigen nicht auf testmodul. Korrigieren und **neu bauen**, nicht nur neu starten. |
| Anmeldung wirkt zufällig abgelaufen | `AUTH_COOKIE_NAME` ist nicht gesetzt oder steht auf `userId` - dann kollidiert es mit dem Produktions-Cookie |
| Registrierung über das Formular schlägt fehl | Erwartet - die Testumgebung verschickt keine E-Mails. Account per SQL anlegen, Schritt 5. |
## Bekannte Einschränkungen der Testumgebung
Bewusst deaktiviert, weil die Umgebung nach außen nichts auslösen soll:
- **Kein E-Mail-Versand** - Registrierung, Passwort-Reset und Benachrichtigungen funktionieren nicht
- **Kein Google-Login** - Zugangsdaten sind nicht hinterlegt
- **Keine Datei-Uploads** - der Objektspeicher (R2) ist nicht konfiguriert
- **Kein Stripe** - Checkout und Abo-Verwaltung funktionieren nicht, solange keine Testschlüssel eingetragen sind
- **Keine Analytics** - damit die Produktionszahlen nicht verfälscht werden