api change user password

This commit is contained in:
Efril 2026-01-14 14:08:27 +07:00
parent 057cccfef9
commit c8ea680e05
11 changed files with 89 additions and 23 deletions

7
go.mod
View File

@ -15,7 +15,6 @@ require (
github.com/bytedance/sonic v1.10.2 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
@ -38,16 +37,14 @@ require (
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/novuhq/go-novu v0.1.2 // indirect
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/spf13/afero v1.9.5 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/stretchr/testify v1.10.0 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
@ -65,8 +62,8 @@ require (
require (
github.com/aws/aws-sdk-go v1.55.7
github.com/golang-jwt/jwt/v5 v5.2.3
github.com/novuhq/go-novu v0.1.2
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.10.0
go.uber.org/zap v1.21.0
golang.org/x/crypto v0.28.0
gorm.io/driver/postgres v1.5.0

2
go.sum
View File

@ -239,8 +239,6 @@ github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1Fof
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=

View File

@ -29,6 +29,12 @@ type SuccessResponse struct {
Data interface{} `json:"data,omitempty"`
}
type NewSuccessResponse struct {
Success bool `json:"success"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
}
type PaginationRequest struct {
Page int `json:"page" validate:"min=1"`
Limit int `json:"limit" validate:"min=1,max=100"`

View File

@ -28,6 +28,10 @@ type ChangePasswordRequest struct {
NewPassword string `json:"new_password" validate:"required,min=6"`
}
type ChangeUserPasswordRequest struct {
NewPassword string `json:"new_password" validate:"required,min=6"`
}
type UpdateUserOutletRequest struct {
OutletID uuid.UUID `json:"outlet_id" validate:"required"`
}

View File

@ -249,6 +249,40 @@ func (h *UserHandler) ChangePassword(c *gin.Context) {
c.JSON(http.StatusOK, &contract.SuccessResponse{Message: "Password changed successfully"})
}
func (h *UserHandler) ChangeUserPassword(c *gin.Context) {
userIDStr := c.Param("id")
userID, err := uuid.Parse(userIDStr)
if err != nil {
logger.FromContext(c).WithError(err).Error("UserHandler::ChangeUserPassword -> Invalid user ID")
h.sendValidationErrorResponse(c, "Invalid user ID", constants.MalformedFieldErrorCode)
return
}
validationError, validationErrorCode := h.userValidator.ValidateUserID(userID)
if validationError != nil {
logger.FromContext(c).WithError(validationError).Error("UserHandler::ChangeUserPassword -> user ID validation failed")
h.sendValidationErrorResponse(c, validationError.Error(), validationErrorCode)
return
}
var req contract.ChangeUserPasswordRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c).WithError(err).Error("UserHandler::ChangeUserPassword -> request binding failed")
h.sendValidationErrorResponse(c, "Invalid request body", constants.MissingFieldErrorCode)
return
}
err = h.userService.ChangeUserPassword(c.Request.Context(), userID, &req)
if err != nil {
logger.FromContext(c).WithError(err).Error("UserHandler::ChangeUserPassword -> Failed to change password from service")
h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
return
}
logger.FromContext(c).Info("UserHandler::ChangeUserPassword -> Successfully changed password")
c.JSON(http.StatusOK, &contract.NewSuccessResponse{Success: true, Message: "Password changed successfully"})
}
func (h *UserHandler) GetProfile(c *gin.Context) {
appCtx := appcontext.FromGinContext(c.Request.Context())
if appCtx.UserID == uuid.Nil {

View File

@ -15,6 +15,7 @@ type UserService interface {
GetUserByEmail(ctx context.Context, email string) (*contract.UserResponse, error)
ListUsers(ctx context.Context, req *contract.ListUsersRequest) (*contract.ListUsersResponse, error)
ChangePassword(ctx context.Context, userID uuid.UUID, req *contract.ChangePasswordRequest) error
ChangeUserPassword(ctx context.Context, userID uuid.UUID, req *contract.ChangeUserPasswordRequest) error
GetProfile(ctx context.Context, userID uuid.UUID) (*contract.UserProfileResponse, error)
UpdateProfile(ctx context.Context, userID uuid.UUID, req *contract.UpdateUserProfileRequest) (*contract.UserProfileResponse, error)

View File

@ -125,7 +125,7 @@ func (p *UserProcessorImpl) UpdateUser(ctx context.Context, id uuid.UUID, req *c
return nil, fmt.Errorf("failed to update user: %w", err)
}
if(req.Name != nil) {
if req.Name != nil {
profile, err := p.profileRepo.GetByUserID(ctx, updated.ID)
if err != nil {
return nil, fmt.Errorf("failed to get user profile: %w", err)
@ -293,6 +293,25 @@ func (p *UserProcessorImpl) ChangePassword(ctx context.Context, userID uuid.UUID
return nil
}
func (p *UserProcessorImpl) ChangeUserPassword(ctx context.Context, userID uuid.UUID, req *contract.ChangeUserPasswordRequest) error {
_, err := p.userRepo.GetByID(ctx, userID)
if err != nil {
return fmt.Errorf("user not found: %w", err)
}
newPasswordHash, err := bcrypt.GenerateFromPassword([]byte(req.NewPassword), bcrypt.DefaultCost)
if err != nil {
return fmt.Errorf("failed to hash new password: %w", err)
}
err = p.userRepo.UpdatePassword(ctx, userID, string(newPasswordHash))
if err != nil {
return fmt.Errorf("failed to update password: %w", err)
}
return nil
}
func (p *UserProcessorImpl) ActivateUser(ctx context.Context, userID uuid.UUID) error {
_, err := p.userRepo.GetByID(ctx, userID)
if err != nil {

View File

@ -15,6 +15,7 @@ type UserHandler interface {
GetUserProfile(c *gin.Context)
UpdateProfile(c *gin.Context)
ChangePassword(c *gin.Context)
ChangeUserPassword(c *gin.Context)
ListTitles(c *gin.Context)
GetActiveUsersForMention(c *gin.Context)
}

View File

@ -103,6 +103,7 @@ func (r *Router) addAppRoutes(rg *gin.Engine) {
users.GET("/:id/profile", r.userHandler.GetUserProfile)
users.PUT("/profile", r.userHandler.UpdateProfile)
users.PUT("/:id/password", r.userHandler.ChangePassword)
users.PUT("/:id/user-password", r.userHandler.ChangeUserPassword)
users.GET("/titles", r.userHandler.ListTitles)
users.GET("/mention", r.userHandler.GetActiveUsersForMention)
users.POST("/profile/avatar", r.fileHandler.UploadProfileAvatar)

View File

@ -17,6 +17,7 @@ type UserProcessor interface {
GetUserByEmail(ctx context.Context, email string) (*contract.UserResponse, error)
GetUserEntityByEmail(ctx context.Context, email string) (*entities.User, error)
ChangePassword(ctx context.Context, userID uuid.UUID, req *contract.ChangePasswordRequest) error
ChangeUserPassword(ctx context.Context, userID uuid.UUID, req *contract.ChangeUserPasswordRequest) error
GetUserRoles(ctx context.Context, userID uuid.UUID) ([]contract.RoleResponse, error)
GetUserPermissionCodes(ctx context.Context, userID uuid.UUID) ([]string, error)

View File

@ -79,6 +79,10 @@ func (s *UserServiceImpl) ChangePassword(ctx context.Context, userID uuid.UUID,
return s.userProcessor.ChangePassword(ctx, userID, req)
}
func (s *UserServiceImpl) ChangeUserPassword(ctx context.Context, userID uuid.UUID, req *contract.ChangeUserPasswordRequest) error {
return s.userProcessor.ChangeUserPassword(ctx, userID, req)
}
func (s *UserServiceImpl) GetProfile(ctx context.Context, userID uuid.UUID) (*contract.UserProfileResponse, error) {
prof, err := s.userProcessor.GetUserProfile(ctx, userID)
if err != nil {