Improve email verification flow and error handling

- Send verification email before creating user to prevent orphaned accounts
- Detect SMTP authentication errors and fail fast without retries
- Add field-level validation errors for better frontend UX
- Configure docker-compose with explicit environment variables
- Document dollar sign escaping in .env.example (use $$)
- Implement welcome email on successful verification
- Clean up unnecessary comments
This commit is contained in:
2025-11-28 11:32:51 +01:00
parent 00f6b51228
commit f37c1ac19b
24 changed files with 850 additions and 78 deletions
@@ -40,12 +40,17 @@ func setupTestServer(t *testing.T) *TestServer {
habitRepo := sqlite.NewHabitRepository(db)
entryRepo := sqlite.NewHabitEntryRepository(db)
refreshTokenRepo := sqlite.NewRefreshTokenRepository(db)
passwordResetTokenRepo := sqlite.NewPasswordResetTokenRepository(db)
registerHandler := commands.NewRegisterUserHandler(userRepo, passwordHasher, nil, "", "open")
loginHandler := queries.NewLoginUserHandler(userRepo, passwordHasher)
refreshTokenHandler := queries.NewRefreshTokenHandler(refreshTokenRepo, userRepo)
revokeTokenHandler := commands.NewRevokeTokenHandler(refreshTokenRepo)
revokeAllTokensHandler := commands.NewRevokeAllTokensHandler(refreshTokenRepo)
verifyEmailHandler := commands.NewVerifyEmailHandler(userRepo)
resendVerificationEmailHandler := commands.NewResendVerificationEmailHandler(userRepo, nil, "")
requestPasswordResetHandler := commands.NewRequestPasswordResetHandler(userRepo, passwordResetTokenRepo, nil, "")
resetPasswordHandler := commands.NewResetPasswordHandler(userRepo, passwordResetTokenRepo, passwordHasher)
createHandler := commands.NewCreateHabitHandler(habitRepo)
getTodaysHandler := queries.NewGetTodaysHabitsHandler(habitRepo, entryRepo)
getUserHabitsHandler := queries.NewGetUserHabitsHandler(habitRepo)
@@ -59,12 +64,15 @@ func setupTestServer(t *testing.T) *TestServer {
refreshTokenExpiry := 7 * 24 * time.Hour
authHandlers := NewAuthHandlers(registerHandler, loginHandler, refreshTokenHandler, revokeTokenHandler, revokeAllTokensHandler, jwtService, refreshTokenRepo, refreshTokenExpiry)
deleteUserHandler := commands.NewDeleteUserHandler(userRepo)
authHandlers := NewAuthHandlers(registerHandler, loginHandler, refreshTokenHandler, revokeTokenHandler, revokeAllTokensHandler, verifyEmailHandler, resendVerificationEmailHandler, requestPasswordResetHandler, resetPasswordHandler, jwtService, refreshTokenRepo, refreshTokenExpiry)
habitHandlers := NewHabitHandlers(createHandler, getTodaysHandler, getUserHabitsHandler, getHabitByIDHandler, getHabitEntriesHandler, updateHandler, archiveHandler, markHandler, unmarkHandler)
statsHandlers := NewStatsHandlers(getHabitStatsHandler)
healthHandlers := NewHealthHandlers(db)
userHandlers := NewUserHandlers(deleteUserHandler)
router := NewRouter("*", habitHandlers, authHandlers, statsHandlers, healthHandlers, jwtService)
router := NewRouter("http://localhost:3000", habitHandlers, authHandlers, statsHandlers, healthHandlers, userHandlers, jwtService)
handler := http.Handler(router)
return &TestServer{