Kompakte Go-Live-Checkliste (Live-.env, Migrationen, einmalige Setups, Stripe-Webhook, Cache-Warmup, Scheduler/Worker, Post-Deploy-Kontrollen). WS-6 (Auth/Verifizierung/Google) und WS-7 im Detailplan als erledigt markiert. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
16 KiB
Version: Juni 2026 · Datum: 15.06.2026 · Status: Abgestimmt – in Umsetzung
Scope: Detaillierter Umsetzungsplan für die nächste Phase. Bündelt die Launch-Anteile der beiden Decision-Updates (Phase-2/Magic-Link, Duplicate-Content) mit den offenen Punkten aus weiteres/Merkliste.md sowie den Rest-Zweigen Login-Flow und Code-Optimierung.
0. Ausgangslage – was bereits im Code existiert
Die Kartierung der betroffenen Subsysteme hat ergeben, dass einiges bereits halb gebaut ist. Der reale Restaufwand ist dadurch kleiner als die Konzept-Dokumente vermuten lassen – vieles ist „verifizieren + scharf schalten + UI" statt „von null bauen".
| Thema | Ist-Stand |
|---|---|
| Magic-Link | Basis vorhanden (app/Models/MagicLink.php, MagicLinkGenerator, MagicLinkConsumeController) – aber nur Login registrierter User + Read-only-PM-Vorschau. Pressekontakt-per-E-Mail-Zugang fehlt. |
| Abo-/Bestandsschutz-Gate | Bereits implementiert: User::hasActiveBooking() zählt grandfathered mit; app/Services/PressRelease/PressReleaseService.php wirft BookingRequiredException. Aber hinter Launch-Schalter billing.enforce_booking=false deaktiviert. |
| Bestandsschutz | Abgebildet als UserPaymentOption.status='grandfathered' + legacy_conditions (JSON). Einmal-Import GrandfatherLegacySubscriptions + täglicher MAN-Worker GenerateManualInvoices (04:30) existieren. |
| Boost / Proof-PDF / Extra-PM | Enum-Cases existieren (SinglePurchaseType), aber keine Credit-Wallet → bleibt Phase 2. |
| Canonical-Tags | Fehlen in resources/views/web/layouts/web-master.blade.php (<head> hat nur meta_description). |
| „Melden"-Button | Platzhalter (href="#") in resources/views/web/release-detail.blade.php, kein Backend. |
| Google-Login / Socialite | Nicht installiert – Greenfield (hiermit freigegeben). |
| E-Mail-Verifizierung | Fortify-Feature emailVerification() aktuell nicht aktiv; Registrierung setzt weder is_active noch Rolle. |
1. Verbindliche Entscheidungen (15.06.2026)
- Legacy-Posten bleiben unverändert. Bestandsschutz-Posten werden nicht auf ein Tarif-Abo (z. B. Agentur) gemappt. Die Positionen bleiben als eigene Legacy-Konditionen erhalten, so wie sie sind.
- Registrierung mit Pflicht-Verifizierung. Neu registrierte Benutzer müssen sich einmalig per E-Mail verifizieren. Erst nach Verifizierung:
customer-Rolle +is_active=true. Ausnahme: Magic-Link, Google-Login & vergleichbare Wege – dort findet die Verifizierung über den Login-Kanal selbst statt (keine zusätzliche E-Mail-Verifizierung nötig). - Laravel Socialite freigegeben als neue Abhängigkeit (für Google-Login, später ggf. weitere Provider).
2. Bewusst NICHT in dieser Phase (Phase 2, credit-abhängig)
Der Großteil des Phase-2/Magic-Link-Dokuments hängt an der Credit-Wallet, die noch nicht existiert. Diese Punkte werden bewusst zurückgestellt, bis die Wallet gebaut ist:
- Pfade C (Inhaltliche Korrektur), D (Update/Ergänzung), G (Depublizieren)
- Boost (Platzierung), Veröffentlichungsnachweis/PDF, Extra-PM (Kontingent-Nachkauf)
- Prüfzähler & höheres Prüfkontingent, Vorab-Prüfung/Redigieren
Vom Duplicate-Content-Dokument ist die Entscheidung ohnehin „nichts bauen"; einziger umsetzbarer Launch-Punkt dort: Canonical-Tags (siehe WS-1). Hash-/Shingle-Vergleich bleibt Phase-3-Option.
3. Arbeitspakete
WS-1 — SEO Canonical-Tags (Duplicate-Doc §5) · klein, isoliert
Scope: Jede öffentliche PM bekommt ihre kanonische URL; pro Portal getrennt (kein Cross-Portal → kein System-Duplicate).
<link rel="canonical">+ OpenGraph-Tags (og:title/description/image) inweb-master.blade.phpvia@yield/@stack.release-detail.blade.phpsetzt die kanonische URL der PM; Listen-/Übersichtsseiten Self-Canonical.- Tests: Feature-Test prüft
rel="canonical"mit korrekter URL auf veröffentlichter PM. - Aufwand: ~½ Tag. Keine Migration, kein Risiko.
WS-2 — Magic-Link-Pressekontaktzugang (Phase-2-Doc §3.1) · Fundament
Scope: Zugangsbrücke „eine Verwaltung, zwei Eintrittswege" für unregistrierte Pressekontakte.
- Neuer Magic-Link-
purpose=contact_accessim bestehendenMagicLinkGenerator(TTL 30 Min). - Dezenter Link auf jeder öffentlichen PM: „Sie sind als Pressekontakt hinterlegt? → verwalten".
- E-Mail-Eingabe + Captcha; enumeration-sichere identische Antwort unabhängig vom Match.
- Bei Match → Mail mit Token → authentifizierte Session, die alle PMs zu dieser E-Mail listet (
Contact::where('email',…)->pressReleases(), firmen-/jahresübergreifend). - Rate-Limit pro E-Mail/IP; Audit-Log (IP/User-Agent) je Aktion.
- Account-Konvertierung: „Permanenten Account anlegen" aus der Magic-Link-Session (Passwort vergeben) → Verifizierung gilt über den Magic-Link-Kanal als erfolgt (siehe Entscheidung 2).
- Tests: Token-Lifecycle, Enumeration-Sicherheit, abgelaufener/verbrauchter Token, PM-Liste nur eigene E-Mail.
- Aufwand: ~2–3 Tage. Fundament für WS-3.
WS-3 — Änderungs-/Lösch-Pfade A · B · E · F (Phase-2-Doc §3.2/3.3) · Launch-Pflicht
Status (16.06.): WS-3 launch-fertig.
- E · F + Melden + Admin-Queue – erledigt (Commit
95007da): Tabellelegal_requests, öffentliches Formular/release/{slug}/rechtliches(DSGVO/Persönlichkeitsrecht/Meldung, enumeration-neutral, zwei Honeypots, Rate-Limit, Bremse „1 offene Anfrage/PM+Typ", typabhängiger Hinweistext), „Melden"-/E&F-Einstieg auf der PM-Detailseite, Admin-Bereich „Recht & Compliance" (Nav-Badge mit Offen-Zähler, Queue index/show mit erledigt/abgelehnt/wieder öffnen + interner Notiz). Routen entkoppelt inroutes/legal.php. Tests grün. Hinweis: Regeltexte je Typ sind Entwurf (TODO im Formular) – vor Launch mit Recht/Legal finalisieren. - B — Kontaktdaten ändern – abgedeckt (über WS-2-Firmen-Scope):
responsible-Pressekontakte bearbeiten die Kontakt-Datensätze im Firmen-Bereich; die öffentliche PM-Detailseite lädt Kontakte aus der Relation, daher spiegeln sich Änderungen auf veröffentlichten PMs. Keine separate Hintergrund-Versionierung (direkte Übernahme). - A — Tippfehler/Grammatik – bewusst zurückgestellt (Phase 2 / Pfad C). Veröffentlichte PMs sind im Kundenbereich nicht editierbar (nur
draft/rejected). A würde veröffentlichte Inhalte automatisch ändern (Integritätsrisiko), ist nicht launch-blockierend und überlappt mit Pfad C. Entscheidung 16.06.: nach Phase 2.
Alle hinter WS-2 oder regulärem Login.
- B — Kontaktdaten ändern (einfachster Pfad zuerst): Formular, direkt übernommen, Versionierung im Hintergrund. Kostenfrei.
- A — Tippfehler/Grammatik: Inline-Editor mit Levenshtein-Diff (kein KI-Call). Kleine Änderung ohne Zahlen/Namen → automatisch; Graubereich → Hinweis „bitte als Korrektur (Pfad C, Phase 2) einreichen". Bremse: 1 Einreichung/PM/24 h + Account-Tages-Cap.
- E — DSGVO-Anonymisierung & F — Persönlichkeitsrecht: rechtlich zwingend ab Launch. Formular → Eintrag in neuer Tabelle
legal_requests→ Admin-Queue (keine KI). Bremse: „1 offene Anfrage pro PM" (nicht hart gesperrt). - Öffentlicher „Melden"-Button (§3.5) verdrahten → speist dieselbe Queue wie F.
- Neuer Admin-Bereich „Recht & Compliance": Nav in
components/layouts/app/sidebar.blade.php, Routen inroutes/admin.php, Volt-Komponenten unterlivewire/admin/legal-requests/nach Musteradmin/contacts/index. - Migration:
legal_requests(press_release_id, type[dsgvo/personal_rights/report], status, payload, requester_email, resolved_at, …). - Tests: je Pfad ein Feature-Test; „1 offene Anfrage/PM"-Bremse; Admin-Queue-Sichtbarkeit; Melden→F-Queue.
- Aufwand: ~4–5 Tage (E/F + Queue der Großteil).
WS-4 — Bestandsschutz-Abrechnung & -Übersicht (Merkliste 1·2·3·5)
Scope: Vorhandene Mechanik (GrandfatherLegacySubscriptions, GenerateManualInvoices) verifizieren + Lücken schließen. Legacy-Posten bleiben unverändert (Entscheidung 1) – kein Tarif-Mapping.
Status (16.06.):
- M1 (portalübergreifend) – verifiziert:
hasActiveBooking()/duePaymentOptions()filtern nicht nachlegacy_portal; Beweis-Test ergänzt (grandfathered Posten zählt unabhängig vom aktiven Portal-Kontext). - M5 (Import + 04:30-Worker) – verifiziert:
GenerateManualInvoicesläuft täglich 04:30 (routes/console.php), berücksichtigt Active + Grandfathered ohne Stripe. - M2 (gemeinsam abrechnen, keine Einzelkündigung) – umgesetzt (Entscheidung 16.06.: getrennte Rechnungen, gekoppelte Kündigung): kein Rechnungs-Schema-Umbau; jede Kondition behält ihre eigene MAN-Rechnung. Neuer
LegacySubscriptionService::cancelBundleFor()kündigt das gesamte Bestandsschutz-Bündel eines Kunden gemeinsam – es gibt bewusst keine Einzelkündigung. - M3 (Admin-Sichtbarkeit) – umgesetzt: Admin-Payments-Ansicht zeigt eine eigene „Bestandsschutz-Posten"-Tabelle (Badge, Portal, Netto, nächste Fälligkeit, „Bündel (N)"-Indikator) inkl. Bündel-Kündigung mit Bestätigung.
- Tests:
tests/Feature/Billing/LegacyBundleTest.php(M1/M2/M3). Offen optional: kombinierte Sammel-Rechnung (eine Rechnung mit Positionen) wurde bewusst nicht gebaut. - M1 — Beide Portale ohne Unterscheidung: Bestandsschutz gilt portalübergreifend, auch wenn nur für ein Portal gebucht → sicherstellen, dass
legacy_portalim Import kein Filter ist. - M2 — Zwei Posten zusammen abrechnen, nicht einzeln kündbar: Zwei Legacy-Posten desselben Kunden (je Portal, z. B. connektar 2×1000 € netto) werden gemeinsam abgerechnet und gelten zusammen (Gesamtsumme 2000 € netto bleibt). Die einzelnen Positionen bleiben als eigene Legacy-Konditionen erhalten – keine Zusammenführung zu einem Tarif, keine Einzelkündigung.
- M5 — Einmal-Import + täglicher Worker: Verifizieren, dass alle aktiven Abrechnungen erfasst werden und der 04:30-Worker fällige Rechnungen korrekt erzeugt; ggf. Gesamtübersicht ergänzen.
- M3 — Ansicht deutlicher: Im Admin-Payments-Bereich Bestandsschutz-Posten optisch klar kennzeichnen/zuordnen (Badge „Bestandsschutz", Portal-Zuordnung, gebündelte Posten sichtbar).
- Tests: gemeinsame Abrechnung zweier Posten; portalübergreifende Geltung; Worker erzeugt Rechnung zum Periodenende.
- Aufwand: ~2–3 Tage. Parallel zu WS-1/2 möglich.
WS-5 — Abo-/Bestandsschutz-Gate scharf schalten (Merkliste 6) — ✅ erledigt 16.06.2026
Scope: „User ohne aktives Abo oder Bestandsschutz dürfen keine PM schreiben."
- Mechanik existiert (
hasActiveBooking()inkl.grandfathered,BookingRequiredException) und ist mitenforce_booking=truevoll getestet:- Backend-Gate:
PressReleaseService::submitForReview()wirftBookingRequiredException/QuotaExceededException. - API:
POST /api/v1/press-releases/{id}/submit→402(keine Buchung) bzw.422(Kontingent erschöpft). - Block-UX am Einreichpunkt:
press-release-submit-modal.blade.phpzeigt ohne Buchung statt des Prüf-Flows den Hinweis „Buchung erforderlich" + Button zur Buchungsseite; alle Volt-Caller (show/edit/create/index) fangen die Exceptions und zeigen einen Toast. - Proaktiver Hinweis: Kunden-Dashboard zeigt bei scharfem Gate ohne aktive Buchung einen Banner „Buchung erforderlich, um zu veröffentlichen" →
me.bookings.index. Bestandskunden (grandfathered) sehen ihn nicht.
- Backend-Gate:
- Launch-Flip: Kein Code-Default ändern — der Schalter bleibt in
config/billing.phpauffalse(Dev-Default). Scharfschalten ausschließlich per Prod-EnvBILLING_ENFORCE_BOOKING=true(→ WS-7-Checkliste). - Tests:
HasActiveBookingTest,PressReleaseSubmitApiTest(402/422),DashboardTest(Banner an/aus + grandfathered-Ausnahme),LegacyBundleTest(grandfathered zählt portalübergreifend). - Aufwand: erledigt. Abhängigkeit: WS-4 (erledigt).
WS-6 — Login/Registrierung: Flow + E-Mail-Verifizierung + Google-Login (Merkliste 7 + Zweig 3) — ✅ erledigt 17.06.2026
Scope nach Entscheidung 2 & 3 — vollständig umgesetzt (Audit 17.06., 65 Auth-Tests grün):
- E-Mail-Verifizierung scharf:
User implements MustVerifyEmail;verified-Middleware greift real auf den Panel-Routen (routes/admin.php,routes/customer.php). Eigener Volt-Flow statt Fortify-Built-in (Features::emailVerification()bewusst aus, wieregistration()/resetPasswords()): Notice-Volt +VerifyEmailController+ Resend-Route. Lockout der Bestands-User durch Backfill-Migration verhindert. - Rolle + Aktivierung erst nach Bestätigung:
CreateNewUserlegt inaktiv & rollenlos an;App\Listeners\ActivateUserAfterVerification(aufVerified-Event) setztis_active=true+customer. - Ausnahme-Kanäle: Magic-Link & Google gelten als verifiziert →
SocialAuthService/Magic-Link setzenemail_verified_at+ Rolle direkt. - Google-Login:
laravel/socialite ^5.27,config/services.php,GoogleController(redirect/callback),oauth_provider/oauth_provider_id-Migration, De-Dup über E-Mail. - Flow-Audit: Login (2FA-Bypass behoben), Passwort-Reset, Verify-Email, 2FA-Challenge, Magic-Link – alle als Volt-Komponenten, gehärtet (Security-Review 16.06.).
- Tests:
tests/Feature/Auth/(10 Dateien): Registrierung→Verifizierung→Aktivierung, unverifiziert blockiert, Socialite gemockt, Account-Verknüpfung, Magic-Link/Google ohne E-Mail-Schritt. - Operativ offen → WS-7: Google-Keys + Redirect-URI in der Prod-
.env. - Doku: Sicherheit & Deployment-Hinweise (Auth, Rollen, Verifizierung).md.
WS-7 — Live-Deployment-Checkliste (Merkliste 4) — ✅ erledigt 17.06.2026
- Kompakte, abarbeitbare Go-Live-Checkliste erstellt: Live-Deployment-Checkliste (WS-7).md. Deckt ab: Live-
.env(Domains, MySQL + Legacy-DBs, Session/Cookies,BILLING_ENFORCE_BOOKING=true,BILLING_OWN_VAT_ID, Stripe-Live-Keys, Google-/OpenAI-Keys, echter Mailer), Code/Assets-Build, Migrationen (Reihenfolge), einmalige Setups (billing:sync-stripe-plans,legacy:grandfather-subscriptionsfürnet_cents,domains:generate-favicons), Stripe-Webhook, Cache-Warmup, Scheduler-Cron + Queue-Worker, Post-Deploy-Kontrollen. - Aufwand: erledigt (reine Doku).
WS-8 — Code-Optimierung (Zweig 4)
- Nach Abschluss der Features: gezielte Optimierung (N+1 in Listen, doppelte Query-Logik, Service-Extraktion). Bewusst zuletzt, wenn der Funktionsumfang steht.
4. Empfohlene Reihenfolge
1. WS-1 Canonical-Tags (Quick-Win, isoliert)
2. WS-2 Magic-Link-Kontaktzugang (Fundament)
3. WS-3 Pfade B → A → E/F + Queue (Launch-Pflicht, auf WS-2)
4. WS-4 Bestandsschutz-Abrechnung (parallel möglich)
5. WS-5 Abo-Gate scharf schalten (nach WS-4)
6. WS-6 Login-Flow + E-Mail-Verifizierung + Google
7. WS-7 Deployment-Checkliste
8. WS-8 Code-Optimierung (zuletzt)
── Phase 2 (später): Wallet, C/D/G, Boost, Proof-PDF, Extra-PM, Prüfzähler ──
5. Anti-Zombie-Check
- ✅ Phase-2-Funktionen (Wallet-abhängig) bewusst zurückgestellt – nichts auf Vorrat gebaut.
- ✅ Legacy-Posten bleiben unangetastet – kein riskantes Tarif-Mapping bestehender Konditionen.
- ✅ Rechtlich zwingende Pfade E/F ab Launch erreichbar, kostenfrei, nie hart gesperrt.
- ✅ Vorhandene Mechanik (Abo-Gate, Bestandsschutz-Worker, Magic-Link-Basis) wird verifiziert/erweitert statt neu gebaut.
- ✅ Duplicate-Checking nicht gebaut – nur Canonical-Hygiene, wie im Decision-Update entschieden.