1
0
forked from wrenn/wrenn

Add email activation flow and replace is_active with status column

Email signup now creates inactive users who must activate via a 30-minute
email token before signing in. Team creation is deferred to first login
after activation, while OAuth users continue to get teams immediately.

- Replace boolean is_active with status column (inactive/active/disabled/deleted)
- Add POST /v1/auth/activate endpoint with Redis-backed token consumption
- Signup returns message instead of JWT, sends activation email
- Login differentiates error messages by user status
- Add confirm password field to signup form
- Add /activate frontend page that auto-logs in on success
- Handle inactive user cleanup on re-signup (30-min cooldown) and OAuth collision
This commit is contained in:
2026-04-16 04:05:41 +06:00
parent e8a2217247
commit a3f75300a9
18 changed files with 726 additions and 265 deletions

View File

@ -21,7 +21,7 @@ type AdminUserRow struct {
Email string
Name string
IsAdmin bool
IsActive bool
Status string
CreatedAt time.Time
TeamsJoined int32
TeamsOwned int32
@ -49,7 +49,7 @@ func (s *UserService) AdminListUsers(ctx context.Context, limit, offset int32) (
Email: u.Email,
Name: u.Name,
IsAdmin: u.IsAdmin,
IsActive: u.IsActive,
Status: u.Status,
CreatedAt: u.CreatedAt.Time,
TeamsJoined: u.TeamsJoined,
TeamsOwned: u.TeamsOwned,
@ -58,13 +58,13 @@ func (s *UserService) AdminListUsers(ctx context.Context, limit, offset int32) (
return rows, total, nil
}
// SetUserActive enables or disables a user account.
func (s *UserService) SetUserActive(ctx context.Context, userID pgtype.UUID, active bool) error {
if err := s.DB.SetUserActive(ctx, db.SetUserActiveParams{
ID: userID,
IsActive: active,
// SetUserStatus sets the status of a user account.
func (s *UserService) SetUserStatus(ctx context.Context, userID pgtype.UUID, status string) error {
if err := s.DB.SetUserStatus(ctx, db.SetUserStatusParams{
ID: userID,
Status: status,
}); err != nil {
return fmt.Errorf("set user active: %w", err)
return fmt.Errorf("set user status: %w", err)
}
return nil
}