feat(auth): account lifecycle — Google linking, passwords, suspension & deletion plumbing

- Link a Google account to a password account, and adopt the Google identity onto a matching
  email account instead of 500ing on a duplicate; set or change a password from Settings.
- Expose has_google/has_password on /api/me for the Sign-in methods UI.
- Mark Google logins email-verified (backfill existing rows, migration 0024); stop a routine
  login from clobbering an admin-assigned role (env ADMIN_EMAILS stays the bootstrap admin).
- Suspension login-gates (password + Google callback + current_user) with a rate-limited
  'suspended' notice; shared purge_user (cascade delete + access-request cleanup + Google-grant
  revoke) behind self- and admin-deletion; single app_base source for user-facing email links.
This commit is contained in:
npeter83 2026-06-19 19:52:02 +02:00
parent c0dde06920
commit 2aa13a6433
9 changed files with 507 additions and 43 deletions

View file

@ -61,7 +61,32 @@
"deleteTitle": "Konto löschen?",
"deleteConfirm": "Dies löscht dein Konto und alle deine Daten (Abos, Tags, angesehen/gespeichert/ausgeblendet, Playlists, Einstellungen) dauerhaft. Es kann nicht rückgängig gemacht werden.",
"deleteConfirmButton": "Alles löschen",
"demoNotice": "Dies ist das gemeinsame Demo-Konto. Es hat keine YouTube-Verbindung — du kannst die gesamte gemeinsame Bibliothek durchsuchen, filtern und sortieren, Playlists erstellen und Dinge ausprobieren, aber nichts berührt ein echtes YouTube-Konto."
"demoNotice": "Dies ist das gemeinsame Demo-Konto. Es hat keine YouTube-Verbindung — du kannst die gesamte gemeinsame Bibliothek durchsuchen, filtern und sortieren, Playlists erstellen und Dinge ausprobieren, aber nichts berührt ein echtes YouTube-Konto.",
"signInMethods": "Anmeldemethoden",
"googleLink": {
"title": "Google-Konto",
"connected": "Verbunden",
"connectedHint": "Dein Google-Konto ist verknüpft. Du kannst dich mit Google anmelden und unten den YouTube-Zugriff erteilen.",
"connectHint": "Verknüpfe ein Google-Konto, um dich mit Google anzumelden und den YouTube-Zugriff für deinen Feed zu aktivieren.",
"connect": "Google verknüpfen",
"linked": "Google-Konto verknüpft.",
"conflict": "Dieses Google-Konto ist bereits mit einem anderen Siftlode-Konto verknüpft.",
"mismatch": "Das ist ein anderes Google-Konto als das bereits hier verknüpfte. Melde dich mit dem verknüpften an.",
"error": "Das Google-Konto konnte nicht verknüpft werden. Bitte versuche es erneut."
},
"password": {
"title": "Passwort",
"setHint": "Ein Passwort ist gesetzt. Du kannst dich mit E-Mail und Passwort anmelden.",
"unsetHint": "Noch kein Passwort — lege eines fest, um dich mit deiner E-Mail anzumelden (zusätzlich zu oder anstelle von Google).",
"set": "Passwort festlegen",
"change": "Passwort ändern",
"current": "Aktuelles Passwort",
"new": "Neues Passwort (min. 10 Zeichen)",
"save": "Passwort speichern",
"saving": "Speichern…",
"saved": "Passwort für {{email}} aktualisiert.",
"failed": "Das Passwort konnte nicht aktualisiert werden."
}
},
"demo": {
"title": "Demo-Zugang",
@ -87,7 +112,7 @@
"addPlaceholder": "E-Mail direkt hinzufügen…",
"add": "Hinzufügen",
"decided": "{{count}} entschieden",
"approved": "Genehmigt — sie können sich jetzt anmelden",
"approved": "{{email}} genehmigt — sie können sich jetzt anmelden",
"approveFailed": "Genehmigung fehlgeschlagen",
"denyFailed": "Ablehnung fehlgeschlagen",
"addedToWhitelist": "Zur Whitelist hinzugefügt",