presseportale/dev/release/deploy-runbook.md
Kevin Adametz e4d154cae5 Deploy: .env.example vervollstaendigt + Deploy-Runbook (R5.1/R5.2)
Alle projektspezifischen Env-Keys dokumentiert (Multi-Domain, Legacy-DBs,
Stripe/Billing, KI, Sanctum, Newsletter, Vite); Runbook fuers neue
Laravel-System mit Prod-.env-Solltabelle, Release-Schritten,
Queue-Worker (classification), Cron-Tasks inkl. Ausfall-Folgen,
Stripe-Webhook, Erst-Einrichtung, Smoke-Test und Rollback.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 12:38:24 +00:00

153 lines
6.2 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.

# Deploy-Runbook — Pressekonto (Laravel-System)
**Stand:** 03.07.2026 · **Gilt für:** das neue Laravel-System (Hub pressekonto + Spokes presseecho/businessportal24).
`FTP-LIVE-DEPLOY.md` betrifft nur das Alt-Symfony-System und ist für dieses System **nicht** anwendbar.
> Referenz-Plan: `dev/release/entwicklungsplan-release.md` (R5.2). Externe Voraussetzungen
> (Stripe-Live-Keys, echte Mail-/Kontaktadressen, Favicons, finale Rechtstexte) siehe dort.
---
## 1. Server-Voraussetzungen
- PHP 8.4 (FPM) mit Extensions: `pdo_mysql`, `mbstring`, `intl`, `gd` (Bildvarianten), `curl`, `zip`
- MySQL/MariaDB (Prod: `DB_CONNECTION=mysql`; SQLite ist nur Dev)
- Node.js (LTS) + npm — nur zum Bauen der Assets (Builds sind **gitignored**, siehe Schritt 3)
- Webserver-vHosts für **alle drei Domains** auf dasselbe `public/` (Host-Routing macht die App)
- HTTPS-Zertifikate für alle drei Domains
- Prozess-Manager (systemd/Supervisor) für den Queue-Worker
**Wichtig (SEO):** Keine statische `public/robots.txt` anlegen — robots.txt und sitemap.xml
sind dynamische, host-aware Laravel-Routen; eine echte Datei würde sie verschatten.
## 2. `.env` (Produktion) — Solltabelle
Basis: `.env.example` kopieren und anpassen. Abweichend von den Dev-Defaults zwingend:
| Key | Prod-Wert |
|---|---|
| `APP_ENV` | `production` |
| `APP_DEBUG` | `false` |
| `APP_KEY` | `php artisan key:generate` (einmalig) |
| `APP_URL` / `APP_PORTAL_*` / `APP_PRESSEECHO_*` / `APP_BUSINESSPORTAL_*` | echte Domains (pressekonto.de, presseecho.de, businessportal24.com) |
| `SANCTUM_STATEFUL_DOMAINS` | echte Domains, kommasepariert |
| `DB_CONNECTION` + `DB_*` | MySQL-Zugangsdaten |
| `MAIL_MAILER` + `MAIL_*` | echter Mailer — **Default `log` versendet nichts!** |
| `MAIL_FROM_ADDRESS` | echte Absenderadresse |
| `SESSION_SECURE_COOKIE` | `true` |
| `BASIC_AUTH_ENABLED` | Staging: `true` + User/Passwort · Live: `false` |
| `BILLING_ENFORCE_BOOKING` | `true` (Default) |
| `STRIPE_KEY/SECRET/WEBHOOK_SECRET` + `STRIPE_PRICE_SINGLE_PM` | **Live**-Werte [EXTERN R1.2] |
| `BILLING_OWN_VAT_ID` | echte USt-IdNr. |
| `OPENAI_API_KEY` | Prod-Key (ohne ihn keine Klassifikation/kein Score) |
| `CONTACT_EMAIL_*` | echte Empfängeradressen [EXTERN R2.1] |
| `GOOGLE_CLIENT_ID/SECRET/REDIRECT_URI` | Live-OAuth-Client (Redirect-URI exakt) |
## 3. Build & Release-Schritte
```bash
# 1) Code auslegen (git pull / Release-Verzeichnis)
composer install --no-dev --optimize-autoloader
# 2) Assets bauen (Builds sind gitignored — auf dem Server oder in CI bauen
# und public/build/ mit ausliefern)
npm ci
npm run build # baut portal + web (public/build/portal, public/build/web)
# 3) Storage-Symlink (Firmenlogos, PM-Bilder)
php artisan storage:link
# 4) Datenbank
php artisan migrate --force
# 5) Seeder — NUR die Stammdaten-Seeder. DatabaseSeeder ist prod-sicher
# (Demo-User nur in local/testing), aber explizit ist besser:
php artisan db:seed --class=RolesAndPermissionsSeeder --force
php artisan db:seed --class=AdminPresetSeeder --force
php artisan db:seed --class=PlanSeeder --force
php artisan db:seed --class=PaymentOptionSeeder --force
# 6) Caches bauen (bei JEDEM Deploy neu)
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan event:cache
# 7) Queue-Worker neu starten (lädt neuen Code)
php artisan queue:restart
```
## 4. Queue-Worker (Pflicht)
Die KI-Jobs (`ClassifyPressRelease`, `ScorePressRelease`) laufen auf der
database-Queue **`classification`** — ohne Worker bleibt jede eingereichte PM
in der Prüfung hängen.
systemd-Beispiel (`/etc/systemd/system/pressekonto-worker.service`):
```ini
[Unit]
Description=Pressekonto Queue Worker
After=network.target
[Service]
User=www-data
Restart=always
ExecStart=/usr/bin/php /var/www/pressekonto/artisan queue:work database \
--queue=classification,default --tries=3 --max-time=3600
[Install]
WantedBy=multi-user.target
```
## 5. Scheduler / Cron (Pflicht)
Crontab des Web-Users:
```cron
* * * * * cd /var/www/pressekonto && php artisan schedule:run >> /dev/null 2>&1
```
Aktive Tasks (`php artisan schedule:list`):
| Zeitplan | Task | Ohne Cron passiert … |
|---|---|---|
| alle 5 Min | `press-releases:publish-scheduled` | **geplante PMs werden nie veröffentlicht** |
| täglich 03:00 | `magic-links:purge --days=30` | Token-Tabelle wächst |
| täglich 04:30 | `billing:generate-manual-invoices` | manuelle Rechnungen fehlen |
| So 04:00 | `press-releases:purge-drafts --days=180` | Alt-Entwürfe bleiben liegen |
| monatl. 1. 00:05 | `press-releases:reset-monthly-quota` | **Kontingente werden nie zurückgesetzt** |
## 6. Stripe-Webhook
Endpoint in Stripe (Live-Mode) registrieren: `https://pressekonto.de/stripe/webhook`
→ Signing-Secret als `STRIPE_WEBHOOK_SECRET` in die `.env`.
Events: die Cashier-Defaults (Subscriptions, Invoices, Payment-Intents).
## 7. Erst-Einrichtung (nur beim allerersten Go-Live)
1. Legacy-Datenimport (Reihenfolge beachten, read-only-Quellen konfiguriert):
`legacy:import` → `legacy:fix-timestamps` → `legacy:migrate-media`
(bzw. `legacy:sync-company-logos` / `legacy:sync-press-release-images`)
→ `legacy:archive-invoices` → `legacy:grandfather-subscriptions`.
2. Ersten Admin-User anlegen (Prod hat keine Demo-User):
Registrierung + Rolle via Tinker/DB oder dedizierter Befehl.
3. Favicons je Domain deployen (Platzhalter-Generator reicht nicht) [EXTERN R5.4].
4. Smoke-Test: siehe Abschnitt 8.
## 8. Smoke-Test nach jedem Deploy
- [ ] Alle drei Domains laden (Hub-Landing, 2× Spoke-Home) über HTTPS
- [ ] `/robots.txt` + `/sitemap.xml` je Domain liefern hostkorrekte Inhalte
- [ ] Login am Portal funktioniert; `/admin` erreichbar
- [ ] Eine Test-PM einreichen → Klassifikation läuft durch (Worker-Check)
- [ ] Kontaktformular + Widerrufs-/Kündigungsformular versenden Mails (echter Mailer!)
- [ ] Stripe-Checkout im Live-Mode mit kleiner Testbuchung + Webhook-Eingang prüfen
- [ ] `storage/logs/laravel.log` auf Fehler prüfen
## 9. Rollback
- Code: vorheriges Release auschecken → Schritte 6–7 aus Abschnitt 3 wiederholen
(`config:cache` … `queue:restart`).
- Migrationen sind additiv gehalten; kein automatisches `migrate:rollback` in Prod —
im Fehlerfall DB-Backup einspielen (Backup **vor** jedem Deploy mit Migrationen!).