fix(admin-users): localized emails, set-password flow, verification & nav #1

Merged
AiBot merged 1 commit from fix/admin-user-emails-and-nav into master 2026-06-13 15:02:13 +02:00
Collaborator

Why

Fixes a batch of issues found while testing admin user creation under /users.

Changes

Transactional emails

  • Localization (DE/EN): Mailer.sendVerification / sendPasswordReset now take the recipient language; bodies are localized instead of hardcoded English. Callers (account.ts, me.ts) pass user.language.
  • Set-password welcome mail: new Mailer.sendSetPassword. Creating a user without a password now sends a welcome email with a first-password link instead of a bare "password reset was requested" mail.
  • Editable email footer (#8): new settings mail.footer.de / mail.footer.en with sensible defaults, appended to every outgoing email by recipient locale. Editable in Admin → Settings (DE/EN textareas). Surfaced via GET/PATCH /admin/settings.
  • Renamed remaining DIY-Avatar email subjects/bodies → wiederdabei.

User creation

  • Respect skip_verification: a password user created with the box unchecked is now created unverified and receives a verification email (and is blocked at login with email_not_verified) instead of being silently auto-verified + welcomed.

Admin nav / auth (the 403 storm)

  • Role-aware landing: added shared landingPathFor(scopes). Login and boot now route users to a page their roles allow. Non-admins are no longer forced onto the admin-only dashboard, whose /admin/* calls 403 for them ("forbidden" screen).
  • Boot-race fix: session starts in a loading state and the SPA router is gated on the initial /auth/me check, so protected routes no longer bounce to the login form on refresh.

Verification

  • Reproduced the bugs in a local stack (browser HAR showed teacher → #/dashboard with repeated 403s) and confirmed each fix:
    • teacher login → lands on their school, 0 × 403; refresh stays put; admin → /dashboard.
    • no-password create → sendSetPassword; pw+skip=false → unverified + verify mail + login 403; pw+skip=true → verified + welcome + login 200; footer GET/PATCH/clear roundtrip.
  • tsc --noEmit clean; svelte-check 0 errors / 0 warnings; backend tests 358/360 (the 2 failing me.test.ts cases are pre-existing on master, unrelated).

Not addressed (not a code bug here)

The reported "reset/verify link wants me to authenticate first" did not reproduce: the reset/verify SPA routes and POST /auth/reset/:token + /auth/verify/:token are public and work logged-out. Most likely deployment-side — check ADMIN_PUBLIC_BASE / CORS-derived base and any reverse-proxy (Basic/SSO) auth on the admin origin.

## Why Fixes a batch of issues found while testing admin user creation under `/users`. ## Changes ### Transactional emails - **Localization (DE/EN):** `Mailer.sendVerification` / `sendPasswordReset` now take the recipient `language`; bodies are localized instead of hardcoded English. Callers (`account.ts`, `me.ts`) pass `user.language`. - **Set-password welcome mail:** new `Mailer.sendSetPassword`. Creating a user **without** a password now sends a *welcome email with a first-password link* instead of a bare "password reset was requested" mail. - **Editable email footer (#8):** new settings `mail.footer.de` / `mail.footer.en` with sensible defaults, appended to **every** outgoing email by recipient locale. Editable in Admin → Settings (DE/EN textareas). Surfaced via `GET/PATCH /admin/settings`. - Renamed remaining `DIY-Avatar` email subjects/bodies → `wiederdabei`. ### User creation - **Respect `skip_verification`:** a password user created with the box **unchecked** is now created **unverified** and receives a *verification email* (and is blocked at login with `email_not_verified`) instead of being silently auto-verified + welcomed. ### Admin nav / auth (the 403 storm) - **Role-aware landing:** added shared `landingPathFor(scopes)`. Login and boot now route users to a page their roles allow. Non-admins are no longer forced onto the admin-only dashboard, whose `/admin/*` calls 403 for them ("forbidden" screen). - **Boot-race fix:** session starts in a `loading` state and the SPA router is gated on the initial `/auth/me` check, so protected routes no longer bounce to the login form on refresh. ## Verification - Reproduced the bugs in a local stack (browser HAR showed teacher → `#/dashboard` with repeated `403`s) and confirmed each fix: - teacher login → lands on their school, **0 × 403**; refresh stays put; admin → `/dashboard`. - no-password create → `sendSetPassword`; pw+skip=false → unverified + verify mail + login `403`; pw+skip=true → verified + welcome + login `200`; footer GET/PATCH/clear roundtrip. - `tsc --noEmit` clean; `svelte-check` 0 errors / 0 warnings; backend tests **358/360** (the 2 failing `me.test.ts` cases are pre-existing on `master`, unrelated). ## Not addressed (not a code bug here) The reported "reset/verify link wants me to authenticate first" did **not** reproduce: the reset/verify SPA routes and `POST /auth/reset/:token` + `/auth/verify/:token` are public and work logged-out. Most likely deployment-side — check `ADMIN_PUBLIC_BASE` / CORS-derived base and any reverse-proxy (Basic/SSO) auth on the admin origin.
Admin user-creation and transactional-email fixes surfaced while testing
/users.

Emails
- Localize verification + password-reset emails (DE/EN) instead of
  hardcoded English; callers pass the recipient's language.
- New sendSetPassword(): password-less create now sends a welcome email
  with a first-password link (not a bare password-reset mail).
- Editable per-locale email footer (settings: mail.footer.de/.en) with a
  sensible default, appended to EVERY outgoing email by recipient locale.
- Rename remaining "DIY-Avatar" email subjects/bodies to "wiederdabei".

User creation
- Respect skip_verification for password users: when unchecked the user
  is created UNVERIFIED and receives a verification email (and cannot log
  in until confirmed) instead of being silently auto-verified.

Admin nav / auth (403 storm)
- Post-login + boot now land users on a role-appropriate page via shared
  landingPathFor(); non-admins are no longer forced onto the admin-only
  dashboard (which 403s every /admin/* call).
- Gate the SPA router on the initial /auth/me check (session starts in a
  loading state) so protected routes don't bounce to login on refresh.

Tests: account-lifecycle updated for the new create semantics.
AiBot merged commit 37178bc872 into master 2026-06-13 15:02:13 +02:00
AiBot deleted branch fix/admin-user-emails-and-nav 2026-06-13 15:02:13 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
public/diy-avatar!1
No description provided.