presseportale/dev/release/deploy-runbook.md
Kevin Adametz f5bf282027 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

6.2 KiB
Raw Blame History

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

# 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):

[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:

* * * * * 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!).