feat(accounts): outgoing mail over SMTP (Resend), email verification, fail-closed password reset

- mail/ module (lettre, ru/en templates), links built only from PUBLIC_BASE_URL
- migration 0006: accounts.email_verified_at, shared email_tokens table (verify + reset), existing accounts marked verified
- reset mailer no longer logs tokens; RESET_MAIL_MODE=log is dev-only and refused with COOKIE_SECURE=true; Disabled by default answers 503
- forgot-password and resend-verification do their work in a background task (no timing oracle)
- device linking requires a verified email; email_verified exposed via /me and gRPC
- gateway rate limits, SMTP_* in compose and .env.example
- frontend: verify, forgot-password, reset-password pages, verify banner, ru/en strings
This commit is contained in:
loki5512344 2026-10-09 21:08:33 +02:00
parent 12ce6216b6
commit c57f851a8b
Signed by: boba
GPG key ID: 253067914055423B
48 changed files with 1810 additions and 120 deletions

View file

@ -20,6 +20,9 @@ export const routes: RouteObject[] = [
{ path: '/', element: <HomePage /> },
{ path: '/login', lazy: () => import('@pages/public/auth/LoginPage').then(toRoute) },
{ path: '/register', lazy: () => import('@pages/public/auth/RegisterPage').then(toRoute) },
{ path: '/verify', lazy: () => import('@pages/public/auth/VerifyPage').then(toRoute) },
{ path: '/forgot-password', lazy: () => import('@pages/public/auth/ForgotPasswordPage').then(toRoute) },
{ path: '/reset-password', lazy: () => import('@pages/public/auth/ResetPasswordPage').then(toRoute) },
{ path: '/download', lazy: () => import('@pages/download/DownloadRoute').then(toRoute) },
{ path: '/themes', lazy: () => import('@pages/themes/ThemesRoute').then(toRoute) },
{ path: '/u/:id', lazy: () => import('@pages/public/ProfilePage').then(toRoute) },