diff --git a/dto/userpin_dto.go b/dto/userpin_dto.go index 97c176b..d8e333a 100644 --- a/dto/userpin_dto.go +++ b/dto/userpin_dto.go @@ -37,6 +37,31 @@ func (r *RequestUserPinDTO) Validate() (map[string][]string, bool) { return nil, true } +type UpdateUserPinDTO struct { + OldPin string `json:"old_pin"` + NewPin string `json:"new_pin"` +} + +func (r *UpdateUserPinDTO) Validate() (map[string][]string, bool) { + errors := make(map[string][]string) + + if strings.TrimSpace(r.OldPin) == "" { + errors["old_pin"] = append(errors["old_pin"], "Old pin is required") + } + + if strings.TrimSpace(r.NewPin) == "" { + errors["new_pin"] = append(errors["new_pin"], "New pin is required") + } else if len(r.NewPin) < 6 { + errors["new_pin"] = append(errors["new_pin"], "New pin must be at least 6 digits") + } + + if len(errors) > 0 { + return errors, false + } + + return nil, true +} + func isNumeric(s string) bool { re := regexp.MustCompile(`^[0-9]+$`) return re.MatchString(s) diff --git a/internal/handler/userpin_handler.go b/internal/handler/userpin_handler.go index 1d8b39a..aee4e16 100644 --- a/internal/handler/userpin_handler.go +++ b/internal/handler/userpin_handler.go @@ -77,3 +77,24 @@ func (h *UserPinHandler) CreateUserPin(c *fiber.Ctx) error { return utils.LogResponse(c, userPinResponse, "User pin created successfully") } + +func (h *UserPinHandler) UpdateUserPin(c *fiber.Ctx) error { + var requestUserPinDTO dto.UpdateUserPinDTO + if err := c.BodyParser(&requestUserPinDTO); err != nil { + return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}}) + } + + errors, valid := requestUserPinDTO.Validate() + if !valid { + return utils.ValidationErrorResponse(c, errors) + } + + userID := c.Locals("userID").(string) + + userPinResponse, err := h.UserPinService.UpdateUserPin(userID, requestUserPinDTO.OldPin, requestUserPinDTO.NewPin) + if err != nil { + return utils.GenericErrorResponse(c, fiber.StatusBadRequest, err.Error()) + } + + return utils.LogResponse(c, userPinResponse, "User pin updated successfully") +} diff --git a/internal/repositories/userpin_repo.go b/internal/repositories/userpin_repo.go index d6bf2d3..7c10f67 100644 --- a/internal/repositories/userpin_repo.go +++ b/internal/repositories/userpin_repo.go @@ -9,6 +9,7 @@ type UserPinRepository interface { FindByUserID(userID string) (*model.UserPin, error) FindByPin(userPin string) (*model.UserPin, error) Create(userPin *model.UserPin) error + Update(userPin *model.UserPin) error } type userPinRepository struct { @@ -44,3 +45,11 @@ func (r *userPinRepository) Create(userPin *model.UserPin) error { } return nil } + +func (r *userPinRepository) Update(userPin *model.UserPin) error { + err := r.DB.Save(userPin).Error + if err != nil { + return err + } + return nil +} \ No newline at end of file diff --git a/internal/services/userpin_service.go b/internal/services/userpin_service.go index 3cbc873..240deb0 100644 --- a/internal/services/userpin_service.go +++ b/internal/services/userpin_service.go @@ -15,6 +15,7 @@ type UserPinService interface { CreateUserPin(userID, pin string) (*dto.UserPinResponseDTO, error) VerifyUserPin(userID, pin string) (*dto.UserPinResponseDTO, error) CheckPinStatus(userID string) (string, *dto.UserPinResponseDTO, error) + UpdateUserPin(userID, oldPin, newPin string) (*dto.UserPinResponseDTO, error) } type userPinService struct { @@ -115,3 +116,49 @@ func (s *userPinService) CreateUserPin(userID, pin string) (*dto.UserPinResponse return userPinResponse, nil } + +func (s *userPinService) UpdateUserPin(userID, oldPin, newPin string) (*dto.UserPinResponseDTO, error) { + + userPin, err := s.UserPinRepo.FindByUserID(userID) + if err != nil { + return nil, fmt.Errorf("user pin not found") + } + + err = bcrypt.CompareHashAndPassword([]byte(userPin.Pin), []byte(oldPin)) + if err != nil { + return nil, fmt.Errorf("incorrect old pin") + } + + hashedPin, err := bcrypt.GenerateFromPassword([]byte(newPin), bcrypt.DefaultCost) + if err != nil { + return nil, fmt.Errorf("error hashing the new pin: %v", err) + } + + userPin.Pin = string(hashedPin) + err = s.UserPinRepo.Update(userPin) + if err != nil { + return nil, fmt.Errorf("error updating user pin: %v", err) + } + + createdAt, _ := utils.FormatDateToIndonesianFormat(userPin.CreatedAt) + updatedAt, _ := utils.FormatDateToIndonesianFormat(userPin.UpdatedAt) + + userPinResponse := &dto.UserPinResponseDTO{ + ID: userPin.ID, + UserID: userPin.UserID, + Pin: userPin.Pin, + CreatedAt: createdAt, + UpdatedAt: updatedAt, + } + + cacheKey := fmt.Sprintf("userpin:%s", userID) + cacheData := map[string]interface{}{ + "data": userPinResponse, + } + err = utils.SetJSONData(cacheKey, cacheData, time.Hour*24) + if err != nil { + fmt.Printf("Error caching updated user pin to Redis: %v\n", err) + } + + return userPinResponse, nil +} diff --git a/presentation/user_route.go b/presentation/user_route.go index d9befc6..d847432 100644 --- a/presentation/user_route.go +++ b/presentation/user_route.go @@ -16,6 +16,6 @@ func UserProfileRouter(api fiber.Router) { api.Get("/user", middleware.AuthMiddleware, userProfileHandler.GetUserProfile) api.Put("/user/update-user", middleware.AuthMiddleware, userProfileHandler.UpdateUserProfile) - api.Post("/user/update-user-password", middleware.AuthMiddleware, userProfileHandler.UpdateUserPassword) - api.Put("/user/upload-photoprofile", middleware.AuthMiddleware, userProfileHandler.UpdateUserAvatar) + api.Patch("/user/update-user-password", middleware.AuthMiddleware, userProfileHandler.UpdateUserPassword) + api.Patch("/user/upload-photoprofile", middleware.AuthMiddleware, userProfileHandler.UpdateUserAvatar) } diff --git a/presentation/userpin_route.go b/presentation/userpin_route.go index 335cd43..3f1a8ee 100644 --- a/presentation/userpin_route.go +++ b/presentation/userpin_route.go @@ -20,5 +20,5 @@ func UserPinRouter(api fiber.Router) { api.Post("/user/set-pin", middleware.AuthMiddleware, userPinHandler.CreateUserPin) api.Post("/user/verif-pin", middleware.AuthMiddleware, userPinHandler.VerifyUserPin) api.Get("/user/cek-pin-status", middleware.AuthMiddleware, userPinHandler.CheckPinStatus) - + api.Patch("/user/update-pin", middleware.AuthMiddleware, userPinHandler.UpdateUserPin) }