api change user password
This commit is contained in:
parent
057cccfef9
commit
c8ea680e05
7
go.mod
7
go.mod
@ -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
2
go.sum
@ -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=
|
||||
|
||||
@ -3,11 +3,11 @@ package contract
|
||||
import "time"
|
||||
|
||||
const (
|
||||
SettingIncomingLetterPrefix = "INCOMING_LETTER_PREFIX"
|
||||
SettingIncomingLetterSequence = "INCOMING_LETTER_SEQUENCE"
|
||||
SettingIncomingLetterPrefix = "INCOMING_LETTER_PREFIX"
|
||||
SettingIncomingLetterSequence = "INCOMING_LETTER_SEQUENCE"
|
||||
SettingIncomingLetterDepartmentRecipients = "INCOMING_LETTER_DEPARTMENT_RECIPIENTS"
|
||||
SettingOutgoingLetterPrefix = "OUTGOING_LETTER_PREFIX"
|
||||
SettingOutgoingLetterSequence = "OUTGOING_LETTER_SEQUENCE"
|
||||
SettingOutgoingLetterPrefix = "OUTGOING_LETTER_PREFIX"
|
||||
SettingOutgoingLetterSequence = "OUTGOING_LETTER_SEQUENCE"
|
||||
)
|
||||
|
||||
type ErrorResponse struct {
|
||||
@ -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"`
|
||||
|
||||
@ -7,17 +7,17 @@ import (
|
||||
)
|
||||
|
||||
type CreateUserRequest struct {
|
||||
Name string `json:"name" validate:"required,min=1,max=255"`
|
||||
Email string `json:"email" validate:"required,email"`
|
||||
Password string `json:"password" validate:"required,min=6"`
|
||||
RoleID *uuid.UUID `json:"role_id,omitempty" validate:"required"`
|
||||
Name string `json:"name" validate:"required,min=1,max=255"`
|
||||
Email string `json:"email" validate:"required,email"`
|
||||
Password string `json:"password" validate:"required,min=6"`
|
||||
RoleID *uuid.UUID `json:"role_id,omitempty" validate:"required"`
|
||||
DepartmentIDs []uuid.UUID `json:"department_ids,omitempty"`
|
||||
}
|
||||
|
||||
type UpdateUserRequest struct {
|
||||
Name *string `json:"name,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
Email *string `json:"email,omitempty" validate:"omitempty,email"`
|
||||
Role *uuid.UUID `json:"role,omitempty"`
|
||||
Role *uuid.UUID `json:"role,omitempty"`
|
||||
IsActive *bool `json:"is_active,omitempty"`
|
||||
Permissions *map[string]interface{} `json:"permissions,omitempty"`
|
||||
DepartmentIDs *[]uuid.UUID `json:"department_ids,omitempty"`
|
||||
@ -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"`
|
||||
}
|
||||
@ -105,9 +109,9 @@ type CreateDepartmentRequest struct {
|
||||
}
|
||||
|
||||
type UpdateDepartmentRequest struct {
|
||||
Name *string `json:"name,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
Code *string `json:"code,omitempty" validate:"omitempty,min=1,max=50"`
|
||||
ParentID *uuid.UUID `json:"parent_id,omitempty"`
|
||||
Name *string `json:"name,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
Code *string `json:"code,omitempty" validate:"omitempty,min=1,max=50"`
|
||||
ParentID *uuid.UUID `json:"parent_id,omitempty"`
|
||||
}
|
||||
|
||||
type GetDepartmentResponse struct {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -52,7 +52,7 @@ func (s *UserServiceImpl) ListUsers(ctx context.Context, req *contract.ListUsers
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
|
||||
|
||||
limit := req.Limit
|
||||
if limit <= 0 {
|
||||
limit = 10
|
||||
@ -60,7 +60,7 @@ func (s *UserServiceImpl) ListUsers(ctx context.Context, req *contract.ListUsers
|
||||
if limit > 100 {
|
||||
limit = 100 // Max limit to prevent performance issues
|
||||
}
|
||||
|
||||
|
||||
offset := (page - 1) * limit
|
||||
|
||||
// Pass calculated offset and limit to processor
|
||||
@ -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 {
|
||||
@ -114,6 +118,6 @@ func (s *UserServiceImpl) GetActiveUsersForMention(ctx context.Context, search *
|
||||
if limit > 100 {
|
||||
limit = 100 // Max limit to prevent performance issues
|
||||
}
|
||||
|
||||
|
||||
return s.userProcessor.GetActiveUsersForMention(ctx, search, limit)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user