feat: password reset flow with welcome emails
All checks were successful
CI / ci (pull_request) Successful in 27s
CI / e2e (pull_request) Successful in 1m0s

- POST /auth/forgot-password with welcome/reset email templates
- POST /auth/reset-password with Zod validation, 4-hour tokens
- Per-email rate limiting (3/hr) via Valkey, no user enumeration
- Login page "Forgot password?" toggle with inline form
- /reset-password page for setting new password from email link
- Initial user seed sends welcome email instead of requiring password
- CLI script for force-resetting passwords via kubectl exec
- APP_URL env var in chart, removed INITIAL_USER_PASSWORD

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
ryan
2026-04-05 17:09:23 +00:00
parent a1dc4b0e47
commit bc8613bbbc
10 changed files with 491 additions and 59 deletions

View File

@@ -9,6 +9,7 @@
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
import { Route as rootRouteImport } from './routes/__root'
import { Route as ResetPasswordRouteImport } from './routes/reset-password'
import { Route as PosRouteImport } from './routes/pos'
import { Route as LoginRouteImport } from './routes/login'
import { Route as AuthenticatedRouteImport } from './routes/_authenticated'
@@ -58,6 +59,11 @@ import { Route as AuthenticatedAccountsAccountIdMembersRouteImport } from './rou
import { Route as AuthenticatedAccountsAccountIdEnrollmentsRouteImport } from './routes/_authenticated/accounts/$accountId/enrollments'
import { Route as AuthenticatedLessonsScheduleInstructorsInstructorIdRouteImport } from './routes/_authenticated/lessons/schedule/instructors/$instructorId'
const ResetPasswordRoute = ResetPasswordRouteImport.update({
id: '/reset-password',
path: '/reset-password',
getParentRoute: () => rootRouteImport,
} as any)
const PosRoute = PosRouteImport.update({
id: '/pos',
path: '/pos',
@@ -337,6 +343,7 @@ export interface FileRoutesByFullPath {
'/': typeof AuthenticatedIndexRoute
'/login': typeof LoginRoute
'/pos': typeof PosRoute
'/reset-password': typeof ResetPasswordRoute
'/help': typeof AuthenticatedHelpRoute
'/profile': typeof AuthenticatedProfileRoute
'/settings': typeof AuthenticatedSettingsRoute
@@ -385,6 +392,7 @@ export interface FileRoutesByFullPath {
export interface FileRoutesByTo {
'/login': typeof LoginRoute
'/pos': typeof PosRoute
'/reset-password': typeof ResetPasswordRoute
'/help': typeof AuthenticatedHelpRoute
'/profile': typeof AuthenticatedProfileRoute
'/settings': typeof AuthenticatedSettingsRoute
@@ -435,6 +443,7 @@ export interface FileRoutesById {
'/_authenticated': typeof AuthenticatedRouteWithChildren
'/login': typeof LoginRoute
'/pos': typeof PosRoute
'/reset-password': typeof ResetPasswordRoute
'/_authenticated/help': typeof AuthenticatedHelpRoute
'/_authenticated/profile': typeof AuthenticatedProfileRoute
'/_authenticated/settings': typeof AuthenticatedSettingsRoute
@@ -487,6 +496,7 @@ export interface FileRouteTypes {
| '/'
| '/login'
| '/pos'
| '/reset-password'
| '/help'
| '/profile'
| '/settings'
@@ -535,6 +545,7 @@ export interface FileRouteTypes {
to:
| '/login'
| '/pos'
| '/reset-password'
| '/help'
| '/profile'
| '/settings'
@@ -584,6 +595,7 @@ export interface FileRouteTypes {
| '/_authenticated'
| '/login'
| '/pos'
| '/reset-password'
| '/_authenticated/help'
| '/_authenticated/profile'
| '/_authenticated/settings'
@@ -635,10 +647,18 @@ export interface RootRouteChildren {
AuthenticatedRoute: typeof AuthenticatedRouteWithChildren
LoginRoute: typeof LoginRoute
PosRoute: typeof PosRoute
ResetPasswordRoute: typeof ResetPasswordRoute
}
declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/reset-password': {
id: '/reset-password'
path: '/reset-password'
fullPath: '/reset-password'
preLoaderRoute: typeof ResetPasswordRouteImport
parentRoute: typeof rootRouteImport
}
'/pos': {
id: '/pos'
path: '/pos'
@@ -1112,6 +1132,7 @@ const rootRouteChildren: RootRouteChildren = {
AuthenticatedRoute: AuthenticatedRouteWithChildren,
LoginRoute: LoginRoute,
PosRoute: PosRoute,
ResetPasswordRoute: ResetPasswordRoute,
}
export const routeTree = rootRouteImport
._addFileChildren(rootRouteChildren)