presseportale/dev/migration 2026/03-MIGRATION-PLAN.md
Kevin Adametz 0a3e52d603 19-05-2026 Rebrand Pressekonto, Hub-Flux UI und Legacy-Media-Migration
Umbenennung presseportale → pressekonto in Domains, Themes und Dokumentation.
Design-Tokens, Portal-Shell, Customer-Dashboard, Auth- und Admin-PM-Views.
Artisan-Befehl migrate:legacy-media mit Tests und Hub-Flux-Entwicklungsdocs.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-19 16:36:13 +00:00

19 KiB
Raw Blame History

03 Migrations-Plan (Phasenweise)

Jede Phase hat einen Satz klar abgegrenzter Aufgaben, ein greifbares Deliverable und Exit-Kriterien. Prioritäten: 🔴 hoch · 🟡 mittel · 🟢 niedrig.

Legende Status: offen · 🔄 in Arbeit · fertig · ⏸️ blockiert · verworfen

Status-Abgleich 2026-04-29: Diese Datei wurde gegen den realen Code-Stand abgeglichen. 08-PROGRESS.md bleibt das chronologische Log; die Tabellen hier bilden den operativen Phasenstatus ab.

Scope-Anpassung 2026-04-23: Nach den Entscheidungen des Auftraggebers (siehe 00-OVERVIEW.md §5) wurde der Plan deutlich geschärft:

  • P2 enthält jetzt Magic-Link-Auth, keine Legacy-Passwort-Übernahme.
  • P4 (Customer-Portal) ist neu eingezogen worden.
  • P7 (API) macht einen sauberen Cut-over, keine Legacy-Key-Kompatibilität.
  • P8 (Billing) wird komplett neu gebaut keine Übernahme alter Rechnungen in den neuen Rechnungskreis; Legacy-Rechnungen werden vollständig als DB-basiertes Archiv importiert, Grandfathering bleibt separat.
  • Promotion Links und Coupons entfallen in Phase 1.

Phase 0 Vorbereitung (weitgehend erledigt)

# Aufgabe Status
0.1 Entscheidungen in 00-OVERVIEW.md §5 mit Auftraggeber klären 2026-04-23
0.2 Legacy-DB-Dumps beschaffen → dev/migration 2026/sql/
0.3 .env anpassen: DB-Connections für mysql, legacy_presseecho, legacy_businessportal lokal/testfähig, da P6-Import gelaufen; Staging/Prod-Credentials bleiben P11-Thema
0.4 Lokale MySQL-Schemas legacy_pe_snapshot / legacy_bp_snapshot aus Dumps erstellen für bisherigen Vollimport erledigt
0.5 Stripe-Test-Account + API-Keys vom Auftraggeber
0.6 Liste der neuen Stripe-Produkte (Preisliste) vom Auftraggeber
0.7 Liste der aktiven Alt-Abos für Grandfathering (oder Kriterien) vom Auftraggeber

Exit: DB-Dumps lokal importierbar, .env konfiguriert, Stripe-Zugang vorhanden.


Phase 1 Fundament (12 d) 🔴

Ziel: Alle Eloquent-Models + Migrations + Factories + Seeders existieren und php artisan migrate:fresh --seed läuft durch.

# Aufgabe Abhängigkeiten Status
1.1 config/countries.php + config/salutations.php aus Legacy-Daten exportieren 0.4
1.2 Enums anlegen (app/Enums/*) inkl. Portal, PressReleaseStatus, UserPaymentOptionStatus (mit grandfathered), InvoiceStatus (Stripe-nah)
1.3 Migrations schreiben (Tabellen aus 04-DATA-MODEL.md):
magic_links, companies, contacts, profiles, categories, category_translations, press_releases, press_release_images, press_release_contact, newsletter_subscriptions, billing_addresses, invoice_billing_addresses, invoices, payment_options, payment_option_translations, user_payment_options, user_payments, legacy_invoices, legacy_import_map, contact_user, api_usage_logs, admin_presets, footer_codes, category_footer_code.
Scope-Hinweis: blacklists und Coupons sind weiterhin nicht produktiv umgesetzt bzw. vertagt.
1.2 Kernschema (inkl. Footer-Codes 2026-05-04)
1.4 users-Tabelle erweitern: portal, registration_type, gdpr_consent_at, legacy_portal, legacy_id, last_seen_at. password auf NULLABLE.
1.5 Soft-Deletes + portal-Spalte auf allen relevanten Tabellen 1.3
1.6 Models mit Relationships + Casts + Global Scopes anlegen 1.3 Kernmodels
1.7 Factories für alle Models (Faker-DE-Locale) 1.6 Kernfactories
1.8 Seeder: RolesAndPermissionsSeeder (admin/editor/customer/api-only), CategorySeeder (DE+EN), DefaultAdminUserSeeder, SalutationTranslationSeeder 1.6 🔄 Rollen, Kategorien, PaymentOptions, AdminPresets vorhanden; kein separater DefaultAdmin/SalutationSeeder
1.9 php artisan migrate:fresh --seed läuft grün 1.31.8 laut 08-PROGRESS.md
1.10 Erste Pest-Tests (Smoke): alle Factories erzeugen valide Models 1.7 /🔄 Billing/Auth/Admin/API-Featuretests vorhanden; keine zentrale Factory-Smoke-Suite

Exit: DB-Struktur stabil, Test-Daten vorhanden, Tests grün. Keine PromotionLinks/Coupons-Tabellen (D-14/D-16).


Phase 2 Auth & Tenancy (1.5 d) 🔴

# Aufgabe Status
2.1 Spatie-Seeder mit Rollen: admin, editor, customer, api-only + passende Permissions
2.2 User-Model: canAccessAdmin() + canAccessCustomer() + Policies
2.3 Magic-Link Auth (D-10): MagicLinkGenerator + MagicLinkConsume-Controller/Livewire + MagicLoginLink-Mailable
2.4 Fortify-Login-View erweitern um "Login per E-Mail-Link" Toggle
2.5 Middleware SetCurrentPortal + CurrentPortalScope (Global Scope) SetCurrentPortal, PortalScope, CurrentPortalContext
2.6 Portal-Dropdown im Admin-Header (Flux UI Dropdown) admin.portal-switcher in Sidebar
2.7 GoLivePasswordReset-Mailable + Command auth:send-go-live-mails (vor Go-Live, dry-runnable) Command + Mailable vorbereitet; Versand bleibt P11
2.8 Tests: Passwort-Login, Magic-Link-Flow (happy + expired + consumed), Portal-Scope Auth-/MagicLink-/Access-Tests vorhanden

Exit: Neue Nutzer können sich per Passwort + Magic-Link einloggen. Portal-Scope filtert Datensätze.


Phase 3 Admin-UI an Models binden (23 d) 🔴

Die Views existieren bereits als Blade/Livewire-Stubs (resources/views/admin/*, resources/views/livewire/admin/*). Aufgabe: echte Daten-Queries + Actions einbauen.

# Bereich Aufgaben Status
3.1 Dashboard Widgets: Anzahl PMs, neue Companies, neue Newsletter-Abos echte Counts + Review-Queue
3.2 Press Releases Index, Create, Edit, Show, Publish/Reject-Actions, mehrere Bilder 2026-05-04: CRUD/Workflow + Multi-Image-Upload mit Sortierung/Preview-Flag/Löschen via livewire/components/press-release-images-manager (gleicher Component für Admin + Customer), API generiert thumb/medium/large-Varianten
3.3 Categories Index mit DE+EN-Translations, Create/Edit (Slug-Live-Preview) 2026-05-04: Index, Create und Edit (DE+EN Slugs mit Live-Preview, Parent-Hierarchie + Loop-Schutz, Lösch-Schutz bei verknüpften PMs/Sub-Kategorien). Tests (8 Cases).
3.4 Companies CRUD inkl. erweitertem Logo-Upload (Varianten) 2026-05-04: Admin-Logo-Upload nutzt jetzt ImageService::storeCompanyLogo(), generiert sq/wide-Varianten und schreibt logo_variants. Logo-Entfernen-Flow inklusive.
3.5 Contacts CRUD, Verknüpfung zu Company
3.6 Users & Roles CRUD + Role-Assign + Permission-Editor + Legacy-Archivrechnungen-Tab Kern umgesetzt; Legacy-Rechnungs-Tab im Admin separat prüfen
3.7 Newsletter Subscriber-Liste, Ex-/Import CSV ⏸️ Vertagt 2026-05-04 wird nach dem Pressemitteilungs-Veröffentlichungs-Block angegangen. Aktuell vorhanden: Newsletter-Sync-UI für die Legacy-Maillisten.
3.8 Invoices (neu) Liste, Detail, Stripe-Status, PDF-Download Admin-View noch Dummy; wartet auf P8
3.9 Legacy-Invoices (Archiv) Read-only Liste, Filter, PDF-Download 2026-05-04: Admin-Archivübersicht mit Suche, Portal-/Status-/Mapping-/PDF-Filtern, Hinweisen für unzugeordnete Legacy-User und on-demand PDF-Download
3.10 Payments Liste (Filter), Detail Admin-View noch Dummy; wartet auf P8
3.11 Footer-Codes CRUD (blieb im Scope) 2026-05-04: Schema (footer_codes + category_footer_code), Model + Factory, Admin-Volt (Index mit Suche/Filter, Create, Edit, Soft-Delete, Toggle-Active), Sidebar-Link, Tests (9 Cases).

Exit: Alle CRUD-Screens funktionsfähig mit echten Daten. Promotions-Admin entfällt.


Phase 4 Customer-Portal NEU (1.52 d) 🔴

Ziel (D-11): Endkunden (Company-User) können sich selbst bedienen kein Admin nötig für tägliche Aufgaben.

Architektur-Update 2026-05-04 (Panel-Konsolidierung): Customer-Bereich ist als "Mein Bereich" ins gemeinsame Admin-Panel überführt. URL-Prefix /admin/me/*, Routenamen me.*. Alte /customer/*-Pfade als 301-Redirects erhalten. Login-Redirect erfolgt rollenbasiert (Admin/Editor → /dashboard, Customer → /admin/me). Sidebar zeigt "Mein Bereich" auch Admin/Editor; Admin-Bereiche bleiben Admin/Editor vorbehalten.

# Aufgabe Status
4.1 Route-Gruppe me.* unter /admin/me/* mit auth:web + EnsureUserIsCustomer (admin/editor/customer) 2026-05-04: konsolidiert ins Admin-Panel; alte customer.*-Routen als 301-Redirects erhalten.
4.2 Dashboard (Livewire\Customer\Dashboard): aktive Abos, letzte eigene PMs, offene Rechnungen 🔄 Dashboard + eigene PMs fertig; aktive Abos/offene neue Rechnungen warten auf P8
4.3 Press Releases: Index, Create, Edit, Submit-for-Review (Status review)
4.4 Rechnungen: neue + Legacy-Archivrechnungen einsehen, PDF-Download 🔄 Legacy-Archiv + PDF-Download vorbereitet; neue Rechnungen warten auf P8
4.5 API-Tokens: Self-Service Create/Revoke, Scope-Auswahl
4.6 Company-/Contact-Profile pflegen (inkl. Logo-Upload) Customer-Profile editierbar inkl. Logo-Upload mit Varianten
4.7 Passwort / 2FA / E-Mail ändern customer.security mit Passwort-/Mail-Update + Fortify 2FA-Aktivierung/Recovery-Codes
4.8 Policies auf allen Aktionen (Customer darf nur eigenes sehen) PressReleasePolicy, CompanyPolicy, ContactPolicy, LegacyInvoicePolicy
4.9 Livewire-Feature-Tests Token + Legacy-Rechnungen + Profile/Security/Policies abgedeckt

Exit: Ein Test-Customer kann per Magic-Link einsteigen, PM erstellen, Token generieren, Legacy-Rechnung ansehen.


Phase 5 Domain-Services & Geschäftslogik (2 d) 🟡

# Aufgabe Status
5.1 PressReleaseService + PublishPressRelease/RejectPressRelease-Actions + Mailable 2026-05-04: PressReleaseService mit Submit/Publish/Reject (mit Pflicht-Begründung)/BackToDraft/Archive/Auto-Reject bei Blacklist; Audit-Log via press_release_status_logs mit Quelle (admin/customer/blacklist) und Bearbeiter; rollenbasierte Mail-URLs (me.* für Customer, admin.* für Admin); Cache-Invalidation für Stats + Review-Counter.
5.2 BlacklistService (Wort-Prüfung im Text; Integration in Publish-Flow) 2026-05-04: App\Services\PressRelease\BlacklistService + BlacklistViolationException, in submitForReview()/publish() integriert (auto-rejected + Mail an Autor), Wortliste in config/blacklist.php
5.3 NewsletterService neu (Subscribe/Confirm/Unsubscribe mit Token) ⏸️ Vertagt 2026-05-04 wird nach dem Pressemitteilungs-Veröffentlichungs-Block angegangen. Heute genutzt: NewsletterSyncService/Null-Client für Legacy-Listen.
5.4 CompanyService (Aktivierung, Logo-Upload, Footer-Code-Management)
5.5 ImageService (Intervention Image, Varianten, Thumbnail-Redirect) 2026-05-04: Logos + PressRelease-Bilder mit thumb/medium/large via Cover-Resize. Verwendet GD ohne neue Composer-Dep, Aufruf von Customer/Admin/API/Sync. Legacy-Thumbnail-Redirect noch offen, aber kein Blocker.
5.6 SluggableTrait für Models + Unique-per-Language-Logik 2026-05-04: App\Models\Concerns\HasUniqueSlug mit konfigurierbarem Scope; PressRelease (portal+language) und Company (portal) nutzen ihn. Category bleibt aussen vor (Slugs in Translations).
5.7 Domain-Events + Listeners (PressReleasePublishedSendPublishNotification) 🔄 Mailables vorhanden, keine dedizierten Domain-Events
5.8 Tests pro Service (Feature-Tests + Unit-Tests) 🔄 PressRelease/Admin/API-Pfade abgedeckt; keine vollständige Service-Suite

Phase 6 Daten-Migration KERN (34 d) 🔴

Ziel: Alle relevanten Daten aus beiden Legacy-DBs in die neue DB als wiederholbares Skript (D-18).

Stand 2026-04-28: Kern-Import vollständig abgeschlossen

# Aufgabe Status
6.1 Legacy-Zugriff via direkte DB-Connection (kein Legacy-Model nötig)
6.2 legacy_import_map-basierte Upsert-Logik (idempotent)
6.3 --source, --step, --dry-run, --force auf Master-Command
6.4 Import-Commands: categories → users → companies → contacts → press_releases (inkl. Bilder + Kontakt-Pivot) beides Portale
6.5 legacy:archive-invoices: erster Archivlauf mit 864 Rechnungen in legacy_invoices in 6.5d technisch erweitert
6.5b legacy:fix-timestamps: Historische Timestamps auf allen Entitäten korrekt
6.5c legacy:import --step=link-associations: 16.968 User↔Kontakt-Links
6.5d Legacy-Rechnungen Vollimport: alle bestehenden Rechnungen beider Portale inkl. Status, Beträgen, Datumsfeldern, Zahlart, vollständigem Raw-Snapshot, pdf_payload und User-Zuordnung importieren; PDF bleibt DB-basiert/on-demand auf Knopfdruck statt Datei-Archiv 2026-05-04
6.6 legacy:grandfather-subscriptions: aktive Alt-Abos übernehmen warten auf Kriterien
6.7 Konflikt-Handling-Reports via Importer-Fehlerlog
6.8 Bilder-Transfer (Mediendateien) 2026-05-04: legacy:sync-company-logos + legacy:sync-press-release-images decken beide Asset-Typen ab. Generiert Varianten via ImageService, idempotent, Dry-Run + Reports vorhanden.
6.9 legacy:verify: Konsistenz-Checks 0 Fehler / 0 Warnungen
6.10 Rehearsal-Lauf gegen produktiven Snapshot auf Staging

Datenstand (2026-04-28):

  • Users: 66.150 | Companies: 107.836 | Contacts: 16.964 | PMs: 174.688 | Rechnungen: 864 im bisherigen Archivlauf

Klarstellung Legacy-Rechnungen (2026-05-04): legacy:archive-invoices übernimmt jede Legacy-Rechnung idempotent, erhält den Originalstatus unverändert und löst die Zuordnung zum neuen User über legacy_import_map auf. Unzuordenbare Rechnungen bleiben im Archiv sichtbar und werden im Import-Report gezählt. pdf_payload speichert zusätzlich Billing-Adresse und User-Payment-Snapshot für die DB-basierte PDF-Erzeugung. Neue Stripe-Rechnungen werden erst in P8 separat konzipiert.

Exit: Kompletter Import beider Portale auf Staging erfolgreich. Rehearsal-Lauf dokumentiert. legacy:verify grün.


Phase 7 API v1 (1.5 d) 🔴

# Aufgabe Status
7.1 Analyse Legacy-Access-Logs (letzte 90 Tage), Priorisierung Endpoints Tooling fertig; Legacy-App-Logs nicht vorhanden, Entscheidung über migrierte Daten/Codeprüfung getroffen
7.2 routes/api.php: Prefix v1, Middleware api,auth:sanctum
7.3 Controller + Resources für: PressRelease (CRUD), PressReleaseImage (minimal), Company (read), Category (read), Newsletter (subscribe) newsletter/unsubscribe bewusst aus P7 entfernt; neuer Newsletter-Dienst folgt später
7.4 Request-Validation via FormRequests
7.5 Sanctum Token-Abilities (`press-releases:read write, companies:read`, etc.)
7.6 Legacy-Cut-Handler (410 Gone bei api_key-Param)
7.7 Rate-Limiting pro Token 60/min pro Sanctum-Token bzw. Bearer-Token-Fingerprint
7.8 API-Doku (Scribe oder OpenAPI YAML) OpenAPI YAML unter docs/api/v1.yml + Route /docs/api/v1
7.9 Feature-Tests für alle Endpoints Kern-Endpunkte + Images + Token-Freigabe + API-Usage-Logging abgedeckt
7.10 Legacy-API-Kundenkommunikation vorbereiten Kundenreport fertig: api:legacy-customers-report; operative Mailtexte folgen außerhalb P7

Phase 8 Payment & Invoicing (Stripe, neu gebaut) (2.5 d) 🔴

# Aufgabe Status
8.1 Stripe-Setup (.env Keys, Entscheidung Cashier vs. raw stripe-php)
8.2 PaymentOption-Admin mit neuen Produkten + Sync zu Stripe Products/Prices (Command stripe:sync-products)
8.3 Checkout-Flow (Livewire im Customer-Portal): Stripe Checkout Session, Rückkehr-URL
8.4 Webhook-Controller /webhooks/stripe + Event-Handler (checkout.session.completed, invoice.paid, invoice.payment_failed, customer.subscription.deleted)
8.5 InvoiceNumberGenerator (neuer Kreislauf {YYYY}{MM}-{seq})
8.6 PDF-Blade + InvoiceService
8.7 Mahnwesen vereinfacht (invoices:remind nur für Stripe-Fails)
8.8 Grandfathering-Handler : ExpireGrandfatheredSubscriptions-Scheduler + Customer-Benachrichtigung
8.9 Legacy-Rechnungen Archiv-UI im Customer-/Admin-Bereich anbinden Customer- und Admin-Archiv inkl. DB-basierter PDF-Erzeugung vorhanden
8.10 Tests mit Stripe-Test-Mode (Fake-Webhooks)

Phase 9 Scheduler & Queues (0.5 d) 🟡

# Aufgabe Status
9.1 Supervisor/Queue-Worker-Setup (Dev + Prod Docs)
9.2 routes/console.php: daily invoices:remind, magic-links:purge, grandfather:expire, monthly newsletter:campaign-digest (optional) 🔄 magic-links:purge täglich und press-releases:purge-drafts wöchentlich vorhanden; P8/P5-Jobs offen
9.3 Failure-Alerting (Log-Channel + Mail bei fehlgeschlagenem Job)

Phase 10 Testing & Härtung (1 d) 🔴

# Aufgabe Status
10.1 Coverage-Ziel 70 % auf app/Services, app/Actions
10.2 Architecture-Tests (Pest arch())
10.3 php artisan test --parallel grün
10.4 Manuelle QA-Checkliste (siehe 06-FEATURES-SCOPE.md §15) durchgehen
10.5 Security-Review: CSRF, CORS, Rate-Limits, File-Uploads, Magic-Link-TTL

Phase 11 Deployment (1 d) 🔴

# Aufgabe Status
11.1 Produktiv-DB-Server provisionieren, Backups konfigurieren
11.2 Staging-Deployment + Smoke-Tests
11.3 Produktiv-Import-Rehearsal gegen aktuellen Legacy-Snapshot
11.4 Go-Live-Mailing: Passwort-Reset + Sicherheitshinweis an alle User 🔄 Command/Mailable fertig; Versand und finale Texte offen
11.5 API-Kunden-Kommunikation (Token-Migration) 🔄 Kundenreport fertig; Freigabeliste und operative Mailtexte offen
11.6 DNS-Cutover-Plan: pressekonto.de auf neuen Server
11.7 Read-Only-Modus auf Legacy-Systemen während Final-Import
11.8 Abschalten der alten Symfony-Server (nach Review-Periode)

Aufwandsschätzung (grob, nach Scope-Schärfung)

Phase Aufwand (MT)
0 0.25 (Rest)
1 1.5
2 1.5
3 2.5
4 2.0
5 2.0
6 3.5
7 1.5
8 2.5
9 0.5
10 1.0
11 1.0
Σ ~19.75 MT (4 Wochen Vollzeit)

Grund für Erhöhung ggü. ursprünglich 16 MT: Customer-Portal (P4) ist neu, Billing ist komplett Neubau statt Migration, Daten-Migration aufwändiger wegen Wiederholbarkeits-Anforderung.


Abhängigkeitsgraph

P0 → P1 → P2 ┬→ P3 ┐
             ├→ P4 ┤
             │     ├→ P5 ┐
             │     │     ├→ P7 ┐
             │     │     │     ├→ P10 → P11
             │     │     ├→ P8 ┘
             │     │     │
             │     └→ P6 ┘
             │
             └→ P9

P6 (Daten-Migration) ist der kritische Pfad kann parallel zu P3P5 auf einem Staging-Branch vorbereitet werden, blockiert aber P10/P11.