10 KiB
M3 SaaS-Basis Vorbereitung
Stand: 2026-07-13
M3 führt Mandanten, Benutzer, Mitgliedschaften und Grundeinstellungen additiv ein. Die bestehende Legacy-App bleibt dabei lauffähig und wird noch nicht auf das neue Modell umgestellt.
Status: abgeschlossen für den M3-Scope. M4 startet mit der additiven
Migration von Einzahlungen und Kaffeeverbrauch nach ledger_entries.
Ziel
- Einen Default-Tenant für den aktuellen Bestand erzeugen.
- Login-Benutzer und Kaffee-Teilnehmer fachlich trennen.
- Rollen tenant-spezifisch vorbereiten.
- Bestehende
kl_Mitarbeiterohne Datenverlust inparticipantsspiegeln. - Die Grundlage für Registrierung und Login schaffen, ohne die Legacy-Auth sofort zu ersetzen.
Nicht-Ziele für den ersten M3-Schritt
- Kein kompletter Login-Umbau in einem Schritt.
- Keine Migration von Einzahlungen und Kaffeeverbrauch nach
ledger_entries; das gehört zu M4. - Kein Design- oder Layout-Umbau.
- Kein Billing und keine Tarife im ersten Schritt.
- Kein produktiver Tenant-Wechsel in bestehenden Legacy-Seiten.
Umgesetzter erster Schritt
Umgesetzte Migrationen:
database/migrations/0002_saas_identity_tenants.sql
database/migrations/0003_saas_auth_account_fields.sql
database/migrations/0004_saas_auth_tokens.sql
database/migrations/0005_saas_tenant_resolution.sql
Tabellen:
tenantstenant_settingsuserstenant_membershipsparticipantstenant_domains
Ergänzende Skripte:
scripts/backfill-default-tenant.php
scripts/check-m3-saas-basis.php
scripts/check-m3-auth-flow.php
scripts/check-m3-settings-flow.php
scripts/check-m3-password-email-flow.php
scripts/check-m3-tenant-resolution-flow.php
scripts/check-m3-mail-flow.php
Wichtige Regeln:
userssind Login-Konten.participantssind Kaffeelisten-Teilnehmer.- Ein
participantkann optional auf einenuserzeigen. - Rollen liegen in
tenant_memberships, nicht inparticipants. participants.legacy_mitarbeiter_idspeichert die alte ID auskl_Mitarbeiter.- Geldwerte in neuen Settings als Cent-Integer speichern.
- Die Migration ist rein additiv; bestehende Legacy-Seiten fragen weiter die
bisherigen
kl_*-Tabellen ab.
Default-Tenant
Für den Bestand wird ein erster Tenant angelegt:
slug: default
name: Kaffeeliste Bestand
status: active
timezone: Europe/Berlin
locale: de-DE
currency_code: EUR
Die Werte können später in einer Tenant-Einstellungsseite angepasst werden.
Backfill
Ein separates Skript spiegelt die bestehenden Mitarbeiter:
scripts/backfill-default-tenant.php
Vorgang:
- Default-Tenant anlegen oder laden.
tenant_settingsauskl_configerzeugen.- Für jede Zeile aus
kl_Mitarbeitereinenparticipantanlegen oder aktualisieren. - Für aktive Admins optional einen
userund einetenant_membershiperzeugen. admin = 1als Rolleadminabbilden; ein konfigurierter erster Benutzer kann Rolleownererhalten.- Verwaiste
participantsmit nicht mehr vorhandenerlegacy_mitarbeiter_idaus dem Default-Tenant entfernen.
Der Backfill muss idempotent sein und darf keine bestehenden Legacy-Daten
löschen. Gelöscht werden nur SaaS-Spiegelungen, deren Legacy-Mitarbeiter in
kl_Mitarbeiter nicht mehr existiert.
Ausgeführter Dev-Stand:
- Default-Tenant
defaultwurde angelegt. - 9 Legacy-Mitarbeiter wurden als
participantsgespiegelt. - 2 aktive Admin-/Owner-Logins wurden als
usersundtenant_membershipsangelegt. - Ein zweiter Backfill-Lauf blieb idempotent.
Registrierung und Login
Umgesetzte Dateien:
app/database.php
app/saas-auth.php
app/saas-mail.php
login.php
register.php
konto.php
logout.php
mandant-auswahl.php
passwort-vergessen.php
passwort-zuruecksetzen.php
email-verifikation-senden.php
email-verifizieren.php
landing.php
assets/images/landing-hero.png
assets/css/public.css
Umgesetzter Umfang:
- Registrierung legt Tenant, Owner-User, Default-Settings, Membership und Owner-Participant in einer Transaktion an.
- Login prüft
users.password_hash, aktive Membership und aktiven Tenant. - Hat ein User genau einen aktiven Mandanten, wird dieser direkt in die Session gelegt.
- Hat ein User mehrere aktive Mandanten, führt der Login zur Mandantenauswahl.
- Logout beendet die neue PHP-Login-Session.
konto.phpzeigt den aktuellen SaaS-Kontext für den angemeldeten User.functions.phpakzeptiert eine SaaS-Login-Session als erste Identitätsquelle, lässtDEV_AUTH_EMAILundAUTH_USERaber als Legacy-Fallback bestehen.footer.phpbleibt App-Sidebar und zeigt keine Public-Login- oder Registrierungslinks mehr.- Login, Registrierung, Passwort-Reset und E-Mail-Verifikation nutzen das Public-Layout im Stil der Landingpage.
- Passwort-Reset erzeugt Single-Use-Tokens und speichert nur Token-Hashes.
- E-Mail-Verifikation erzeugt Single-Use-Tokens und setzt
users.email_verified_at. - Reset- und Verifikationslinks werden über
app/saas-mail.phpversendet. Der Dev-Standard schreibt Mails nachvar/mail; produktiv kann auf PHPmail()umgestellt werden. - Dev-Links werden weiterhin nur im Dev-Modus angezeigt, damit lokale Checks ohne echten Postausgang reproduzierbar bleiben.
Noch offen im M3-Auth-Scope:
- Weitergehende Rollenmatrix für spätere SaaS-Seiten.
- Produktive SMTP-/Provider-Anbindung inklusive Bounce-/Fehlerprotokoll.
Public-Landingpage
Umgesetzte Dateien:
landing.php
assets/images/landing-hero.png
assets/css/public.css
Umgesetzter Umfang:
- Öffentliche Landingpage ohne Legacy-DB-Zugriff.
- Hero mit generiertem Bild-Asset, bestehender Typografie und grünem Akzent.
- CTA zu Login und Registrierung.
- Login, Registrierung und öffentliche Auth-Hilfsseiten sind optisch in den Public-Bereich integriert.
- Kurzabschnitt zu Stricherfassung, Mandantenfähigkeit und Webspace-Betrieb.
- Die geschützte App-Sidebar bleibt von der Public-Seite getrennt.
Tenant-Auflösung
Entscheidung für den Webspace-Betrieb:
- Keine Wildcard-Subdomains im ersten Schritt.
- Primäre App-Adresse ist eine zentrale App-Domain wie
app.kaffeeliste.de. - Die Public-Seite kann über
kaffeeliste.debeziehungsweisewww.kaffeeliste.delaufen. - Der aktive Mandant wird nach Login über
tenant_membershipsund die PHP- Session gesetzt. - Bei genau einem Mandanten wird automatisch weitergeleitet.
- Bei mehreren Mandanten nutzt der User
mandant-auswahl.php. tenant_domainsist vorbereitet für später gezielt eingerichtete feste Domains oder Subdomains, aber nicht Voraussetzung für den Start.
Noch offen:
APP_PRIMARY_HOSTin der Zielumgebung setzen.- Optional
APP_PUBLIC_HOSTbeziehungsweise Host-Rewrite für die Landingpage in der Zielumgebung definieren. - Produktive Domain-/Zertifikatsprüfung für einzelne feste Domains definieren.
Rollen und Grundeinstellungen
Umgesetzte Dateien:
mandant-einstellungen.php
Umgesetzter Umfang:
saas_user_has_role(),saas_require_role()undsaas_can_manage_tenant_settings()zentralisieren die erste Rollenprüfung.- Owner und Admin dürfen Tenant-Grundeinstellungen bearbeiten.
- Die Seite bearbeitet Kundenname, Zeitzone, Locale, Währung, Preis pro Strich, Web-Striche, PayPal-Link, Listenfenster und Schulden-Warnschwelle.
- Geldwerte werden in der UI als Euro-Werte erfasst und weiterhin als Cent- Integer gespeichert.
konto.phpund die Sidebar verlinken die Einstellungen nur für passende Rollen.
Erste Akzeptanzkriterien
- Migrationen laufen mehrfach ohne Fehler: erfüllt.
- Default-Tenant existiert genau einmal: erfüllt.
tenant_settingsenthalten Preis pro Strich und bestehende PayPal-Optionen: erfüllt.- Jeder bestehende
kl_Mitarbeiterhat genau einenparticipant: erfüllt. participants.legacy_mitarbeiter_idist gesetzt: erfüllt.- Admins werden als tenant-scoped Rolle abgebildet: erfüllt.
- Registrierung und Login funktionieren für einen neuen Test-Tenant: erfüllt.
- Owner-/Admin-Grundeinstellungen können aktualisiert werden: erfüllt.
- Passwort-Reset und E-Mail-Verifikation funktionieren mit Single-Use-Tokens: erfüllt.
- Reset- und Verifikationslinks werden im Dev-/Testmodus als Mail-Log erzeugt: erfüllt.
- Mandantenauswahl funktioniert für User mit mehreren Mandanten: erfüllt.
- Öffentliche Landingpage ist per HTTP-Smoke erreichbar: erfüllt.
- Golden-Master und HTTP-Smoke bleiben grün: erfüllt.
Risiken
| Risiko | Gegenmaßnahme |
|---|---|
| Login-User und Kaffee-Teilnehmer werden vermischt | users und participants strikt getrennt halten |
| Mehrfacher Backfill erzeugt Duplikate | Eindeutige Constraints und Upsert-Logik |
| Legacy-App bricht durch neue Tabellen | M3 nur additiv, keine Legacy-Queries umstellen |
| Rollen werden global statt tenant-scoped | Rollen ausschließlich in tenant_memberships speichern |
| Falsche Owner-Zuordnung | Owner per Env-Konfiguration oder manuell dokumentierter Entscheidung setzen |
Empfohlene Reihenfolge
- Migration
0002_saas_identity_tenants.sqlerstellen: erledigt. - Migration
0003_saas_auth_account_fields.sqlerstellen: erledigt. scripts/backfill-default-tenant.phperstellen: erledigt.- Backfill gegen die Dev-Datenbank ausführen: erledigt.
- Kontrollskript für Tenant/Participant/Role-Counts schreiben: erledigt.
- Login-/Registrierungsrouten bauen: erledigt.
- Auth-Flow-Kontrollskript schreiben: erledigt.
- Zentrale Rollenprüfung und Mandant-Einstellungen bauen: erledigt.
- Settings-Flow-Kontrollskript schreiben: erledigt.
- Migration
0004_saas_auth_tokens.sqlerstellen: erledigt. - Passwort-Reset und E-Mail-Verifikation bauen: erledigt.
- Password-/E-Mail-Flow-Kontrollskript schreiben: erledigt.
- Migration
0005_saas_tenant_resolution.sqlerstellen: erledigt. - Mandantenauswahl und zentrale Session-Auflösung bauen: erledigt.
- Tenant-Resolution-Kontrollskript schreiben: erledigt.
- Golden-Master und HTTP-Smoke ausführen: erledigt.
- Mail-Transport-Abstraktion und Mail-Flow-Kontrollskript bauen: erledigt.
- Public-Landingpage mit erstem Hero-Asset vorbereiten: erledigt.
- M3-Abschlusscheck dokumentieren und M4-Datenmigration starten: erledigt.
Übergabe an M4
M4 ist in docs/m4-data-migration.md dokumentiert. Der erste M4-Schritt ist
bewusst additiv:
ledger_entrieswird als neue tenant-sichere Buchungstabelle angelegt.- Legacy-Einzahlungen und Legacy-Kaffeeverbrauch werden gespiegelt.
- Bestehende Seiten bleiben vorerst auf den
kl_*-Tabellen. - Summen werden gegen den Golden Master verglichen.