From a27c8669fc0a2a75debff97218fcc47ec48a990d Mon Sep 17 00:00:00 2001 From: Sergey Chubaryan Date: Fri, 21 Feb 2025 17:54:09 +0300 Subject: [PATCH] small user service refactoring --- .../handlers/user_send_restore_password.go | 2 +- .../server/handlers/user_send_verify.go | 2 +- internal/core/repos/user_repo.go | 7 +- internal/core/services/user_service.go | 131 ++++++++++-------- 4 files changed, 78 insertions(+), 64 deletions(-) diff --git a/cmd/backend/server/handlers/user_send_restore_password.go b/cmd/backend/server/handlers/user_send_restore_password.go index a73f849..36f4d85 100644 --- a/cmd/backend/server/handlers/user_send_restore_password.go +++ b/cmd/backend/server/handlers/user_send_restore_password.go @@ -16,7 +16,7 @@ type inputSendRestorePassword struct { func NewUserSendRestorePasswordHandler(log logger.Logger, userService services.UserService) gin.HandlerFunc { return httpserver.WrapGin(log, func(ctx context.Context, input inputSendRestorePassword) (interface{}, error) { - err := userService.SendEmailForgotPassword(ctx, input.Email) + err := userService.RequestRestorePassword(ctx, input.Email) return nil, err }, ) diff --git a/cmd/backend/server/handlers/user_send_verify.go b/cmd/backend/server/handlers/user_send_verify.go index 6648b97..0bb5553 100644 --- a/cmd/backend/server/handlers/user_send_verify.go +++ b/cmd/backend/server/handlers/user_send_verify.go @@ -16,7 +16,7 @@ type inputSendVerify struct { func NewUserSendVerifyEmailHandler(log logger.Logger, userService services.UserService) gin.HandlerFunc { return httpserver.WrapGin(log, func(ctx context.Context, input inputSendVerify) (interface{}, error) { - err := userService.SendEmailVerifyUser(ctx, input.Email) + err := userService.RequestVerifyUser(ctx, input.Email) return nil, err }, ) diff --git a/internal/core/repos/user_repo.go b/internal/core/repos/user_repo.go index f66ae11..748606b 100644 --- a/internal/core/repos/user_repo.go +++ b/internal/core/repos/user_repo.go @@ -91,7 +91,9 @@ func (u *userRepo) GetUserById(ctx context.Context, id string) (*models.UserDTO, _, span := u.tracer.Start(ctx, "postgres::GetUserById") defer span.End() - query := `select id, email, secret, full_name, email_verified from users where id = $1;` + query := ` + select id, email, secret, full_name, email_verified + from users where id = $1 and activated;` row := u.db.QueryRowContext(ctx, query, id) dto := &models.UserDTO{} @@ -110,7 +112,8 @@ func (u *userRepo) GetUserByEmail(ctx context.Context, login string) (*models.Us _, span := u.tracer.Start(ctx, "postgres::GetUserByEmail") defer span.End() - query := `select id, email, secret, full_name, email_verified from users where email = $1;` + query := `select id, email, secret, full_name, email_verified + from users where email = $1 and activated;` row := u.db.QueryRowContext(ctx, query, login) dto := &models.UserDTO{} diff --git a/internal/core/services/user_service.go b/internal/core/services/user_service.go index 673b10d..fffe4bb 100644 --- a/internal/core/services/user_service.go +++ b/internal/core/services/user_service.go @@ -32,13 +32,16 @@ type UserService interface { CreateUser(ctx context.Context, params UserCreateParams) (*models.UserDTO, error) AuthenticateUser(ctx context.Context, login, password string) (string, error) ValidateAuthToken(ctx context.Context, tokenStr string) (*models.UserDTO, error) + + // TODO: implement user deactivation flow + // DeactivateUser(ctx context.Context, userId string) error + VerifyEmail(ctx context.Context, actionToken string) error - - SendEmailForgotPassword(ctx context.Context, userId string) error - SendEmailVerifyUser(ctx context.Context, email string) error - ChangePassword(ctx context.Context, userId, oldPassword, newPassword string) error ChangePasswordWithToken(ctx context.Context, actionToken, newPassword string) error + + RequestRestorePassword(ctx context.Context, email string) error + RequestVerifyUser(ctx context.Context, email string) error } func NewUserService(deps UserServiceDeps) UserService { @@ -132,6 +135,16 @@ func (u *userService) AuthenticateUser(ctx context.Context, email, password stri return jwt, nil } +func (u *userService) DeactivateUser(ctx context.Context, userId string) error { + err := u.deps.UserRepo.DeactivateUser(ctx, userId) + if err != nil { + return err + } + + u.deps.UserCache.Del(userId) + return nil +} + func (u *userService) VerifyEmail(ctx context.Context, actionToken string) error { token, err := u.deps.ActionTokenRepo.GetActionToken(ctx, actionToken, models.ActionTokenTargetVerifyEmail) if err != nil { @@ -150,62 +163,6 @@ func (u *userService) VerifyEmail(ctx context.Context, actionToken string) error return nil } -func (u *userService) SendEmailForgotPassword(ctx context.Context, email string) error { - // user, err := u.getUserById(ctx, userId) - user, err := u.deps.UserRepo.GetUserByEmail(ctx, email) - if err != nil { - return err - } - - actionToken, err := u.deps.ActionTokenRepo.CreateActionToken( - ctx, - models.ActionTokenDTO{ - UserId: user.Id, - Value: uuid.New().String(), - Target: models.ActionTokenTargetRestorePassword, - Expiration: time.Now().Add(15 * time.Minute), - }, - ) - if err != nil { - return err - } - - return u.deps.EventRepo.SendEmailForgotPassword(ctx, user.Email, actionToken.Value) -} - -func (u *userService) sendEmailVerifyUser(ctx context.Context, userId, email string) error { - actionToken, err := u.deps.ActionTokenRepo.CreateActionToken( - ctx, - models.ActionTokenDTO{ - UserId: userId, - Value: uuid.New().String(), - Target: models.ActionTokenTargetVerifyEmail, - Expiration: time.Now().Add(1 * time.Hour), - }, - ) - if err != nil { - return err - } - - return u.deps.EventRepo.SendEmailVerifyUser(ctx, email, actionToken.Value) -} - -func (u *userService) SendEmailVerifyUser(ctx context.Context, email string) error { - //user, err := u.getUserById(ctx, userId) - user, err := u.deps.UserRepo.GetUserByEmail(ctx, email) - if err != nil { - return err - } - if user == nil { - return fmt.Errorf("no such user") - } - if user.EmailVerified { - return fmt.Errorf("user already verified") - } - - return u.sendEmailVerifyUser(ctx, user.Id, user.Email) -} - func (u *userService) ChangePasswordWithToken(ctx context.Context, actionToken, newPassword string) error { token, err := u.deps.ActionTokenRepo.GetActionToken(ctx, actionToken, models.ActionTokenTargetRestorePassword) if err != nil { @@ -308,3 +265,57 @@ func (u *userService) ValidateAuthToken(ctx context.Context, tokenStr string) (* return user, nil } + +func (u *userService) RequestRestorePassword(ctx context.Context, email string) error { + user, err := u.deps.UserRepo.GetUserByEmail(ctx, email) + if err != nil { + return err + } + + actionToken, err := u.deps.ActionTokenRepo.CreateActionToken( + ctx, + models.ActionTokenDTO{ + UserId: user.Id, + Value: uuid.New().String(), + Target: models.ActionTokenTargetRestorePassword, + Expiration: time.Now().Add(15 * time.Minute), + }, + ) + if err != nil { + return err + } + + return u.deps.EventRepo.SendEmailForgotPassword(ctx, user.Email, actionToken.Value) +} + +func (u *userService) RequestVerifyUser(ctx context.Context, email string) error { + user, err := u.deps.UserRepo.GetUserByEmail(ctx, email) + if err != nil { + return err + } + if user == nil { + return fmt.Errorf("no such user") + } + if user.EmailVerified { + return fmt.Errorf("user already verified") + } + + return u.sendEmailVerifyUser(ctx, user.Id, user.Email) +} + +func (u *userService) sendEmailVerifyUser(ctx context.Context, userId, email string) error { + actionToken, err := u.deps.ActionTokenRepo.CreateActionToken( + ctx, + models.ActionTokenDTO{ + UserId: userId, + Value: uuid.New().String(), + Target: models.ActionTokenTargetVerifyEmail, + Expiration: time.Now().Add(1 * time.Hour), + }, + ) + if err != nil { + return err + } + + return u.deps.EventRepo.SendEmailVerifyUser(ctx, email, actionToken.Value) +}