# Deployment-Plan — andreknie.de > Ablauf für den Produktiv-Gang. **Voraussetzung:** Die funktionalen Lücken und die 🔴-Punkte aus `SICHERHEITS-AUDIT.md` sind geschlossen. ## Architektur im Überblick ``` Internet ──HTTPS──> Caddy (Reverse Proxy, Auto-TLS) │ ├── /api/* ──> Express-Backend (Docker, Port 3003) │ └── server/data/*.json (Volume) │ └── /* ──> statische SPA-Dateien (/opt/andreknie/site) ``` - **Frontend:** statisches Build-Artefakt (`dist/`), von Caddy direkt ausgeliefert. - **Backend:** Express-Container, nur für Formulare (Kontakt, Vortrag, Newsletter, Resource). - **Datenhaltung:** JSON-Dateien im gemounteten Volume `server/data/`. --- ## Voraussetzungen (vor dem ersten Deploy) ### Funktionale Lücken geschlossen - [ ] Confirmation-Landingpages im SPA (siehe Erläuterung unten) + Routen `/bestaetigung/...` - [ ] Backend leitet Bestätigungs-Klick auf eine SPA-Seite weiter (statt rohem JSON) - [ ] Security-🔴: Rate-Limiting, Honeypot, resource_id-Validierung ### Infrastruktur - [ ] Server (VM/VPS) mit Docker + Docker Compose - [ ] Caddy installiert (oder als Container) - [ ] DNS: A/AAAA-Record `andreknie.de` (+ `www`) zeigt auf die Server-IP - [ ] Ports 80 + 443 offen (für Let's Encrypt / TLS) - [ ] SMTP-Zugangsdaten vorhanden (Versand-Postfach bei d-hive) --- ## Schritt-für-Schritt ### 1. Secrets konfigurieren `.env` auf dem Server anlegen (nicht im Repo!): ``` PORT=3003 NODE_ENV=production BASE_URL=https://andreknie.de SMTP_HOST= SMTP_PORT=587 SMTP_USER= SMTP_PASS= STAKEHOLDER_EMAIL=kontakt@d-hive.de ``` ### 2. Frontend bauen Lokal oder im CI: ``` npm ci npm run build ``` Ergebnis: `dist/`. Inhalt nach `/opt/andreknie/site/` auf den Server kopieren (rsync/scp). ### 3. Backend starten Auf dem Server im Projektverzeichnis: ``` docker compose up -d --build ``` - Container lauscht auf `localhost:3003`. - Volume `./server/data` persistiert Tokens/Subscriber/Leads über Neustarts. ### 4. Caddy aktivieren `Caddyfile` deployen, dann `caddy reload` (bzw. Container neu starten). Caddy holt automatisch das TLS-Zertifikat. ### 5. Smoke-Tests (Produktion) - [ ] `https://andreknie.de` lädt, alle Seiten navigierbar - [ ] `GET https://andreknie.de/api/health` → `{ status: "ok" }` - [ ] Kontaktformular absenden → Bestätigungsmail kommt an - [ ] Bestätigungslink klicken → Landingpage „bestätigt", Weiterleitung an Stakeholder kommt an - [ ] Newsletter-Doppel-Opt-In komplett durchspielen - [ ] Rate-Limit greift (mehrfaches Absenden → 429) - [ ] Security-Header prüfen (z.B. securityheaders.com) --- ## Wichtige Erläuterung: die „funktionalen Lücken" Zwei zusammenhängende Punkte, die vor dem Deploy zu klären sind: **A) Confirmation-Landingpages.** Aktuell zeigt die Bestätigungsmail auf den Backend-Endpunkt direkt: `https://andreknie.de/api/contact/confirm/`. Klickt der Nutzer, antwortet Express mit **rohem JSON** (`{ ok: true, ... }`) — keine gestaltete Seite. Für eine saubere UX braucht es: 1. Eine SPA-Seite (z.B. `/bestaetigung`) mit Erfolg-/Abgelaufen-Zustand. 2. Entweder der Backend-Endpunkt antwortet mit einem Redirect (302) auf diese SPA-Seite, oder die Mail verlinkt direkt die SPA-Seite, die dann per fetch den Token beim Backend bestätigt. **B) ContentFilter / Pagination / SEOHead.** Geplante, aber noch nicht gebaute Komponenten: - *ContentFilter* — Tag-Filter für lange Listen (Kniepunkt, Posts). - *Pagination* — Blättern bei >10 Einträgen. - *SEOHead* — pro Seite eigene ``/Meta-Tags (wichtig fürs Teilen auf LinkedIn). Diese sind **kein harter Go-Live-Blocker**, verbessern aber Auffindbarkeit und Bedienbarkeit deutlich. Empfehlung: SEOHead vor Go-Live (wegen Social-Sharing), Filter/Pagination danach. --- ## Rollback - Frontend: vorheriges `dist/` zurückkopieren (alte Builds versioniert aufbewahren). - Backend: `docker compose down` + vorheriges Image-Tag starten. - Daten: `server/data/` regelmäßig sichern (Cron + Backup off-site). ## Wartung - [ ] Backup-Cron für `server/data/` - [ ] Log-Rotation für Container-Logs - [ ] `npm audit` + Dependency-Updates im Quartalsrhythmus - [ ] Zertifikats-Erneuerung: automatisch durch Caddy (kein manueller Eingriff)