dudi/docs/superpowers/specs/2026-04-21-registration-invites-design.md
Simon Kühn fd473f00af Initial commit: Dudi habit tracker
Symfony 8 SPA with Doctrine ORM, Symfony Security, vanilla JS frontend.
Migrated from plain PHP (delight-im/auth + raw SQL) to full Symfony stack.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 15:40:57 +02:00

3.2 KiB

Design: Registrierung & Einladungssystem

Datum: 2026-04-21
Status: Approved

Ziel

Zieltracker für Freunde und Familie öffnen: Registrierung nur per Einladungslink, Passwort-Reset per E-Mail, Referral-Tracking pro User.


1. Konfiguration (config.js)

Neue Datei config.js, die vor app.js eingebunden wird und die zwei globalen Variablen definiert:

var PB_URL = 'https://pb.kuehn.example.com';
var APP_URL = 'https://zieltracker.kuehn.example.com';
  • PB_URL ersetzt die bisherige Hardcodierung in app.js
  • APP_URL wird verwendet, um Einladungslinks zu bauen (APP_URL + '/?invite=' + token)
  • Wird einmalig auf dem Server angepasst — kein UI

2. PocketBase: invites-Collection

Feld Typ Constraints
token text unique, required
label text required (z.B. "Für Mama")
created_by relation → users required
used_by relation → users optional
used_at date optional

API-Regeln:

  • Lesen (einzelner Token via Filter): public — zum Validieren beim Registrieren
  • Erstellen: nur authentifizierte User (@request.auth.id != "")
  • Update (used_by, used_at): public — wird beim Registrieren gesetzt
  • Löschen: nur der Ersteller

3. PocketBase: E-Mail / SMTP

PocketBase Admin → Settings → Mail settings:

  • Provider: Brevo oder Resend (externer SMTP-Dienst, kostenloser Tier)
  • PocketBase schickt nativ: E-Mail-Verifikation nach Registrierung, Passwort-Reset-Link
  • Kein eigener Code nötig

4. UI-Flows

4.1 Einladung erstellen (eingeloggter User)

Im "Daten verwalten"-Sheet (⋯-Menü):

  • Neuer Button "Einladungen"
  • Öffnet Sheet mit Liste aller eigenen Einladungen:
    • Label + Status ("Ausstehend" oder "✓ Registriert")
  • Button "Neue Einladung":
    1. Eingabe: Label (z.B. "Für Papa")
    2. Generiert Token (12 zufällige alphanumerische Zeichen)
    3. Schreibt Token in invites-Collection via PocketBase API
    4. Zeigt fertigen Link zum Kopieren: APP_URL/?invite=TOKEN

4.2 Registrierung (Eingeladener)

  • User öffnet Einladungslink: https://zieltracker.../?invite=TOKEN
  • App liest ?invite-Parameter aus URL
  • App prüft Token gegen PocketBase (invites-Collection, Filter: token=TOKEN && used_by="")
  • Ist Token gültig und ungenutzt: Login-Sheet zeigt zusätzlichen Tab "Registrieren"
  • Ist Token ungültig/bereits genutzt: nur Login, kein Registrierungs-Tab
  • Registrierungsformular: E-Mail, Passwort, Passwort wiederholen
  • Nach Erfolg:
    1. PocketBase-User wird angelegt (Standard-Auth-Flow)
    2. Token wird als used_by = neuer User, used_at = jetzt markiert
    3. User ist eingeloggt, Ziele werden geladen

4.3 Passwort vergessen

Im Login-Sheet:

  • Kleiner Link "Passwort vergessen?" unter dem Anmelden-Button
  • Öffnet Mini-Sheet mit E-Mail-Eingabe
  • Button "Reset-Link senden"
  • Ruft PocketBase-Endpoint auf: POST /api/collections/users/request-password-reset
  • Zeigt Bestätigung: "E-Mail wurde gesendet" (unabhängig ob Adresse existiert)
  • PocketBase verschickt den Reset-Link — kein eigener Template-Code nötig

5. Nicht im Scope

  • Admin-Funktion zum Deaktivieren von Accounts
  • Invite-Limit pro User
  • Ablaufdatum für Einladungslinks