Übernimmt je Owner den Namen seiner Legacy-Agentur-Firma(en) ins neue Profilfeld (A2 des Umbaus Agentur-Attribution → User-Profil). Regeln: zuletzt aktualisierter Datensatz gewinnt bei Mehrfach-Namen (alle Varianten im Report), bereits gesetzte Werte werden nie überschrieben, Owner-lose Agentur-Firmen werden nur reportet. --dry-run + JSON-Report nach storage/app/private/migration/; im Deploy-Runbook (Erst-Einrichtung) nach dem Firmen-/User-Import eingereiht. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
6.5 KiB
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)
- 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. - Agentur-Attribution (läuft NACH dem Firmen-/User-Import aus Schritt 1):
legacy:migrate-agency-names— erst mit--dry-run(Report unterstorage/app/private/migration/prüfen, insbes. Mehrfach-Namen und Owner-lose Agentur-Firmen), dann scharf. Nie überschreibend. - Ersten Admin-User anlegen (Prod hat keine Demo-User): Registrierung + Rolle via Tinker/DB oder dedizierter Befehl.
- Favicons je Domain deployen (Platzhalter-Generator reicht nicht) [EXTERN R5.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.xmlje Domain liefern hostkorrekte Inhalte- Login am Portal funktioniert;
/adminerreichbar - 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.logauf 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:rollbackin Prod — im Fehlerfall DB-Backup einspielen (Backup vor jedem Deploy mit Migrationen!).