presseportale/docs/weiteres/Detailplan Umsetzung Launch-Slice (Magic-Link, Compliance, Bestandsschutz, Auth).md
Kevin Adametz 7dbc238bdb WS-7: Live-Deployment-Checkliste + WS-6/WS-7 als erledigt dokumentiert
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>
2026-06-17 13:32:35 +00:00

16 KiB
Raw Blame History

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)

  1. 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.
  2. 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).
  3. 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) in web-master.blade.php via @yield/@stack.
  • release-detail.blade.php setzt 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.

Scope: Zugangsbrücke „eine Verwaltung, zwei Eintrittswege" für unregistrierte Pressekontakte.

  • Neuer Magic-Link-purpose = contact_access im bestehenden MagicLinkGenerator (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): Tabelle legal_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 in routes/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 in routes/admin.php, Volt-Komponenten unter livewire/admin/legal-requests/ nach Muster admin/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 nach legacy_portal; Beweis-Test ergänzt (grandfathered Posten zählt unabhängig vom aktiven Portal-Kontext).
  • M5 (Import + 04:30-Worker) – verifiziert: GenerateManualInvoices lä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_portal im 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 mit enforce_booking=true voll getestet:
    • Backend-Gate: PressReleaseService::submitForReview() wirft BookingRequiredException/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.php zeigt 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.
  • Launch-Flip: Kein Code-Default ändern — der Schalter bleibt in config/billing.php auf false (Dev-Default). Scharfschalten ausschließlich per Prod-Env BILLING_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, wie registration()/resetPasswords()): Notice-Volt + VerifyEmailController + Resend-Route. Lockout der Bestands-User durch Backfill-Migration verhindert.
  • Rolle + Aktivierung erst nach Bestätigung: CreateNewUser legt inaktiv & rollenlos an; App\Listeners\ActivateUserAfterVerification (auf Verified-Event) setzt is_active=true + customer.
  • Ausnahme-Kanäle: Magic-Link & Google gelten als verifiziert → SocialAuthService/Magic-Link setzen email_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-subscriptions für net_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.