feat: Go-Live-Pruefung "pnpm preflight"
Die letzten offenen Punkte vor dem Live-Gang sind Inhaber-Aufgaben, keine Code-Aufgaben — und genau die Sorte, die still durchrutscht, weil der Shop mit ihnen trotzdem startet. Das Skript macht daraus eine Pruefliste, die mit Exit-Code 1 abbricht, solange ein Blocker offen ist, und laesst sich damit unveraendert als Schritt in die Deploy-Pipeline haengen. Geprueft wird, was einen oeffentlichen Shop unbemerkt unbrauchbar macht: - SESSION_SECRET/ADMIN_EMAIL/ADMIN_PASSWORD auf Laenge und darauf, dass noch der Beispielwert aus .env.example darin steht (ein bekanntes Signiergeheimnis macht jede Sitzung faelschbar). - NEXT_PUBLIC_SITE_URL auf HTTPS und kein localhost — dieser Wert landet in jeder Bestellbestaetigung, in sitemap.xml und in allen Canonicals. - Stripe-Schluessel und Webhook-Secret vorhanden, und ob versehentlich noch ein sk_test-Schluessel gesetzt ist. Ohne Webhook-Secret wird keine Zahlung bestaetigt: Bestellungen bleiben auf "NEU", ohne Rechnung und ohne Bestaetigungsmail. - SMTP vollstaendig — fehlt es, landen E-Mails nur im Server-Log statt beim Kunden (lib/mail.ts). - Impressum ohne [PLATZHALTER]/[PLACEHOLDER]-Markierungen. Bewusst beide Markierungen: die deutsche Fassung nutzt die eine, die englische die andere — eine Pruefung auf nur eine liesse die jeweils andere Sprachfassung mit Luecken online gehen. Ein unvollstaendiges Impressum nach § 5 TMG ist in Deutschland abmahnfaehig. Bewusst ohne Datenbankzugriff, damit die Pruefung auch von einer Maschine ohne Zugang zur Produktionsdatenbank laeuft. Felder, die zusaetzlich im Admin-Bereich unter "Inhalte" pflegbar sind (Telefonnummer, Anschrift), sind deshalb Warnung statt Blocker. README: neue Abschnitte "Vor dem Live-Gang" und "Health-Endpunkte"; in der Tech-Stack-Tabelle ausserdem die Zeile "GSAP + ScrollTrigger" korrigiert — GSAP wurde laengst durch motion/react ersetzt und steht weder in package.json noch im Quellcode. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
1 parent
c50e5e0e08
commit
c9ec2b4998
2 files changed
+196
-2
No files matched your search
@@ -9,8 +9,7 @@ Regionale Imkerei-Website und Shop für Wabenhain (Altenmünster) — MVP-Stufe.
|
||||
| Sprache | TypeScript |
|
||||
| Framework | Next.js 16 (App Router) + React 19 |
|
||||
| Styling | Tailwind CSS v4 |
|
||||
| Scroll-/Hero-Animationen | GSAP + ScrollTrigger |
|
||||
| UI-Animationen | Motion for React (Menü, Warenkorb, Karten) |
|
||||
| Animationen | Motion for React (Scroll-Reveal, Hero, Menü, Warenkorb, Karten) |
|
||||
| Datenbank | PostgreSQL (Standard) oder MySQL/MariaDB, per `DATABASE_PROVIDER` wählbar — siehe „Datenbank-Provider wechseln" |
|
||||
| ORM | Prisma 7 (`@prisma/adapter-pg` bzw. `@prisma/adapter-mariadb`) |
|
||||
| Payment | Stripe Checkout |
|
||||
@@ -204,6 +203,33 @@ docker compose exec app pnpm db:seed:demo # optional: Beispieldaten zum Auspro
|
||||
|
||||
Die App läuft danach hinter [Caddy](https://caddyserver.com/) unter `http://localhost`, PostgreSQL unter Port `5432`. `app` selbst ist **nicht** direkt vom Host aus erreichbar (kein `ports:`-Mapping mehr, nur `expose`) — Caddy ist bewusst der einzige nach außen offene Dienst, weil er die tatsächliche Client-Verbindung terminiert und `X-Forwarded-For` dabei neu setzt statt einen vom Client selbst mitgeschickten Wert zu übernehmen. Ohne diesen Proxy davor ließe sich jedes IP-basierte Rate-Limit (Login, 2FA-Code, Checkout, Registrierung, siehe `lib/rate-limit.ts`) durch einen gefälschten Header umgehen. Für eine echte Domain mit automatischem HTTPS `SITE_ADDRESS` in `.env` setzen und Port `443` in `docker-compose.yml` freigeben (siehe Kommentar dort und in der `Caddyfile`).
|
||||
|
||||
## Vor dem Live-Gang
|
||||
|
||||
```bash
|
||||
pnpm preflight
|
||||
```
|
||||
|
||||
Prüft die Konfiguration gegen alles, was einen öffentlichen Shop still unbrauchbar machen kann, und beendet sich mit Exit-Code 1, solange ein Blocker offen ist — dadurch lässt sich der Aufruf unverändert als Schritt in die Deploy-Pipeline hängen. Geprüft werden unter anderem:
|
||||
|
||||
- `SESSION_SECRET`, `ADMIN_EMAIL` und `ADMIN_PASSWORD` auf Länge und darauf, dass noch der Beispielwert aus `.env.example` darin steht (ein bekanntes Signiergeheimnis macht jede Sitzung fälschbar).
|
||||
- `NEXT_PUBLIC_SITE_URL` auf HTTPS und darauf, dass dort nicht `localhost` steht — genau dieser Wert landet in Bestellbestätigungen, in `sitemap.xml` und in allen Canonical-Tags.
|
||||
- Stripe: Schlüssel und Webhook-Secret vorhanden, und ob versehentlich noch ein `sk_test`-Schlüssel gesetzt ist. Ohne Webhook-Secret wird keine Zahlung bestätigt: Bestellungen bleiben auf „NEU" stehen, ohne Rechnung und ohne Bestätigungsmail.
|
||||
- SMTP vollständig gesetzt — fehlt es, werden E-Mails nur ins Server-Log geschrieben statt versendet (siehe `lib/mail.ts`), also keine Bestellbestätigung und keine Passwort-Zurücksetzung.
|
||||
- Impressum: ob in `src/i18n/dictionaries/de.ts` bzw. `en.ts` noch `[PLATZHALTER]`/`[PLACEHOLDER]`-Markierungen stehen. Ein unvollständiges Impressum nach § 5 TMG ist in Deutschland abmahnfähig.
|
||||
|
||||
Unabhängig davon bleiben die Rechtstexte (AGB, Datenschutz, Widerrufsbelehrung) Platzhalter und **müssen** vor dem Live-Gang anwaltlich geprüft werden — das kann kein Skript feststellen.
|
||||
|
||||
## Health-Endpunkte
|
||||
|
||||
Zwei getrennte Endpunkte, bewusst mit unterschiedlicher Aufgabe:
|
||||
|
||||
| Endpunkt | Prüft | Verwendung |
|
||||
|---|---|---|
|
||||
| `/api/health` | nur, ob der Node-Prozess antwortet — **kein** Datenbankzugriff | Liveness-Probe |
|
||||
| `/api/health/ready` | zusätzlich `SELECT 1` gegen die Datenbank, antwortet sonst mit 503 | Readiness-Probe, Docker-Healthcheck |
|
||||
|
||||
Die Trennung ist der Grund für die Endpunkte: eine Liveness-Probe, die an der Datenbank hängt, macht aus einer vorübergehenden DB-Störung eine Neustart-Schleife, weil der neu startende Container laut `Dockerfile`-CMD zuerst `prisma migrate deploy` gegen genau diese gestörte Datenbank fährt. Die Readiness-Probe darf dagegen ruhig fehlschlagen — Kubernetes nimmt den Pod dann nur aus dem Load-Balancing, statt ihn zu töten. Vorher zeigten beide Probes auf `/`, also alle 10 bzw. 30 Sekunden auf eine `force-dynamic`-Seite samt DB-Abfragen und vollem React-Rendering.
|
||||
|
||||
## Kubernetes / Gitea CI
|
||||
|
||||
`.gitea/workflows/docker-build.yml` baut bei jedem Push auf `main` automatisch ein Docker-Image und veröffentlicht es in der Gitea-eigenen Container-Registry. Der Container migriert/seedet die Datenbank beim Start selbst (idempotent), kein separates Migrations-Image oder -Job nötig. `k8s/` enthält vollständige, rohe Kubernetes-Manifeste (Namespace, ConfigMap, Secret-Vorlage, Postgres/MySQL-StatefulSet, App-Deployment, Ingress) für den Betrieb dieses Images im Cluster — Einrichtung und Deployment-Ablauf stehen in [`k8s/README.md`](k8s/README.md).
|
||||
@@ -259,6 +285,7 @@ Beim Ausbau der Warenkorb-/Checkout-Tests kam ein echter, vorher unentdeckter Bu
|
||||
pnpm dev # Entwicklungsserver
|
||||
pnpm build # Produktions-Build
|
||||
pnpm lint # ESLint
|
||||
pnpm preflight # Go-Live-Pruefung (siehe "Vor dem Live-Gang")
|
||||
pnpm db:studio # Prisma Studio (DB-GUI)
|
||||
pnpm db:migrate # neue Migration erstellen/anwenden (Dev)
|
||||
pnpm audit # Abhängigkeiten auf bekannte Schwachstellen prüfen
|
||||
|
||||
Reference in new issue
Block a user