307 lines
11 KiB
Go
307 lines
11 KiB
Go
|
|
package handler
|
||
|
|
|
||
|
|
import (
|
||
|
|
"net/http"
|
||
|
|
"strconv"
|
||
|
|
|
||
|
|
"eslogad-be/internal/appcontext"
|
||
|
|
"eslogad-be/internal/constants"
|
||
|
|
"eslogad-be/internal/contract"
|
||
|
|
"eslogad-be/internal/logger"
|
||
|
|
|
||
|
|
"github.com/gin-gonic/gin"
|
||
|
|
"github.com/google/uuid"
|
||
|
|
)
|
||
|
|
|
||
|
|
type UserHandler struct {
|
||
|
|
userService UserService
|
||
|
|
userValidator UserValidator
|
||
|
|
}
|
||
|
|
|
||
|
|
func NewUserHandler(userService UserService, userValidator UserValidator) *UserHandler {
|
||
|
|
return &UserHandler{
|
||
|
|
userService: userService,
|
||
|
|
userValidator: userValidator,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (h *UserHandler) CreateUser(c *gin.Context) {
|
||
|
|
var req contract.CreateUserRequest
|
||
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||
|
|
logger.FromContext(c).WithError(err).Error("UserHandler::CreateUser -> request binding failed")
|
||
|
|
h.sendValidationErrorResponse(c, "Invalid request body", constants.MissingFieldErrorCode)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
validationError, validationErrorCode := h.userValidator.ValidateCreateUserRequest(&req)
|
||
|
|
if validationError != nil {
|
||
|
|
logger.FromContext(c).WithError(validationError).Error("UserHandler::CreateUser -> request validation failed")
|
||
|
|
h.sendValidationErrorResponse(c, validationError.Error(), validationErrorCode)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
userResponse, err := h.userService.CreateUser(c.Request.Context(), &req)
|
||
|
|
if err != nil {
|
||
|
|
logger.FromContext(c).WithError(err).Error("UserHandler::CreateUser -> Failed to create user from service")
|
||
|
|
h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
logger.FromContext(c).Infof("UserHandler::CreateUser -> Successfully created user = %+v", userResponse)
|
||
|
|
c.JSON(http.StatusCreated, userResponse)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (h *UserHandler) UpdateUser(c *gin.Context) {
|
||
|
|
userIDStr := c.Param("id")
|
||
|
|
userID, err := uuid.Parse(userIDStr)
|
||
|
|
if err != nil {
|
||
|
|
logger.FromContext(c).WithError(err).Error("UserHandler::UpdateUser -> 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::UpdateUser -> user ID validation failed")
|
||
|
|
h.sendValidationErrorResponse(c, validationError.Error(), validationErrorCode)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
var req contract.UpdateUserRequest
|
||
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||
|
|
logger.FromContext(c).WithError(err).Error("UserHandler::UpdateUser -> request binding failed")
|
||
|
|
h.sendValidationErrorResponse(c, "Invalid request body", constants.MissingFieldErrorCode)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
validationError, validationErrorCode = h.userValidator.ValidateUpdateUserRequest(&req)
|
||
|
|
if validationError != nil {
|
||
|
|
logger.FromContext(c).WithError(validationError).Error("UserHandler::UpdateUser -> request validation failed")
|
||
|
|
h.sendValidationErrorResponse(c, validationError.Error(), validationErrorCode)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
userResponse, err := h.userService.UpdateUser(c.Request.Context(), userID, &req)
|
||
|
|
if err != nil {
|
||
|
|
logger.FromContext(c).WithError(err).Error("UserHandler::UpdateUser -> Failed to update user from service")
|
||
|
|
h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
logger.FromContext(c).Infof("UserHandler::UpdateUser -> Successfully updated user = %+v", userResponse)
|
||
|
|
c.JSON(http.StatusOK, userResponse)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (h *UserHandler) DeleteUser(c *gin.Context) {
|
||
|
|
userIDStr := c.Param("id")
|
||
|
|
userID, err := uuid.Parse(userIDStr)
|
||
|
|
if err != nil {
|
||
|
|
logger.FromContext(c).WithError(err).Error("UserHandler::DeleteUser -> 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::DeleteUser -> user ID validation failed")
|
||
|
|
h.sendValidationErrorResponse(c, validationError.Error(), validationErrorCode)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
err = h.userService.DeleteUser(c.Request.Context(), userID)
|
||
|
|
if err != nil {
|
||
|
|
logger.FromContext(c).WithError(err).Error("UserHandler::DeleteUser -> Failed to delete user from service")
|
||
|
|
h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
logger.FromContext(c).Info("UserHandler::DeleteUser -> Successfully deleted user")
|
||
|
|
c.JSON(http.StatusOK, &contract.SuccessResponse{Message: "User deleted successfully"})
|
||
|
|
}
|
||
|
|
|
||
|
|
func (h *UserHandler) GetUser(c *gin.Context) {
|
||
|
|
userIDStr := c.Param("id")
|
||
|
|
userID, err := uuid.Parse(userIDStr)
|
||
|
|
if err != nil {
|
||
|
|
logger.FromContext(c).WithError(err).Error("UserHandler::GetUser -> 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::GetUser -> user ID validation failed")
|
||
|
|
h.sendValidationErrorResponse(c, validationError.Error(), validationErrorCode)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
userResponse, err := h.userService.GetUserByID(c.Request.Context(), userID)
|
||
|
|
if err != nil {
|
||
|
|
logger.FromContext(c).WithError(err).Error("UserHandler::GetUser -> Failed to get user from service")
|
||
|
|
h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
logger.FromContext(c).Infof("UserHandler::GetUser -> Successfully retrieved user = %+v", userResponse)
|
||
|
|
c.JSON(http.StatusOK, userResponse)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (h *UserHandler) ListUsers(c *gin.Context) {
|
||
|
|
ctx := c.Request.Context()
|
||
|
|
|
||
|
|
req := &contract.ListUsersRequest{
|
||
|
|
Page: 1,
|
||
|
|
Limit: 10,
|
||
|
|
}
|
||
|
|
|
||
|
|
if page := c.Query("page"); page != "" {
|
||
|
|
if p, err := strconv.Atoi(page); err == nil {
|
||
|
|
req.Page = p
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if limit := c.Query("limit"); limit != "" {
|
||
|
|
if l, err := strconv.Atoi(limit); err == nil {
|
||
|
|
req.Limit = l
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if role := c.Query("role"); role != "" {
|
||
|
|
req.Role = &role
|
||
|
|
}
|
||
|
|
|
||
|
|
if isActiveStr := c.Query("is_active"); isActiveStr != "" {
|
||
|
|
if isActive, err := strconv.ParseBool(isActiveStr); err == nil {
|
||
|
|
req.IsActive = &isActive
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
validationError, validationErrorCode := h.userValidator.ValidateListUsersRequest(req)
|
||
|
|
if validationError != nil {
|
||
|
|
logger.FromContext(c).WithError(validationError).Error("UserHandler::ListUsers -> request validation failed")
|
||
|
|
h.sendValidationErrorResponse(c, validationError.Error(), validationErrorCode)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
usersResponse, err := h.userService.ListUsers(ctx, req)
|
||
|
|
if err != nil {
|
||
|
|
logger.FromContext(c).WithError(err).Error("UserHandler::ListUsers -> Failed to list users from service")
|
||
|
|
h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
logger.FromContext(c).Infof("UserHandler::ListUsers -> Successfully listed users = %+v", usersResponse)
|
||
|
|
c.JSON(http.StatusOK, contract.BuildSuccessResponse(usersResponse))
|
||
|
|
}
|
||
|
|
|
||
|
|
func (h *UserHandler) ChangePassword(c *gin.Context) {
|
||
|
|
userIDStr := c.Param("id")
|
||
|
|
userID, err := uuid.Parse(userIDStr)
|
||
|
|
if err != nil {
|
||
|
|
logger.FromContext(c).WithError(err).Error("UserHandler::ChangePassword -> 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::ChangePassword -> user ID validation failed")
|
||
|
|
h.sendValidationErrorResponse(c, validationError.Error(), validationErrorCode)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
var req contract.ChangePasswordRequest
|
||
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||
|
|
logger.FromContext(c).WithError(err).Error("UserHandler::ChangePassword -> request binding failed")
|
||
|
|
h.sendValidationErrorResponse(c, "Invalid request body", constants.MissingFieldErrorCode)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
validationError, validationErrorCode = h.userValidator.ValidateChangePasswordRequest(&req)
|
||
|
|
if validationError != nil {
|
||
|
|
logger.FromContext(c).WithError(validationError).Error("UserHandler::ChangePassword -> request validation failed")
|
||
|
|
h.sendValidationErrorResponse(c, validationError.Error(), validationErrorCode)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
err = h.userService.ChangePassword(c.Request.Context(), userID, &req)
|
||
|
|
if err != nil {
|
||
|
|
logger.FromContext(c).WithError(err).Error("UserHandler::ChangePassword -> Failed to change password from service")
|
||
|
|
h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
logger.FromContext(c).Info("UserHandler::ChangePassword -> Successfully changed password")
|
||
|
|
c.JSON(http.StatusOK, &contract.SuccessResponse{Message: "Password changed successfully"})
|
||
|
|
}
|
||
|
|
|
||
|
|
func (h *UserHandler) GetProfile(c *gin.Context) {
|
||
|
|
appCtx := appcontext.FromGinContext(c.Request.Context())
|
||
|
|
if appCtx.UserID == uuid.Nil {
|
||
|
|
h.sendErrorResponse(c, "Unauthorized", http.StatusUnauthorized)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
profile, err := h.userService.GetProfile(c.Request.Context(), appCtx.UserID)
|
||
|
|
if err != nil {
|
||
|
|
logger.FromContext(c).WithError(err).Error("UserHandler::GetProfile -> Failed to get profile")
|
||
|
|
h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
c.JSON(http.StatusOK, contract.BuildSuccessResponse(profile))
|
||
|
|
}
|
||
|
|
|
||
|
|
func (h *UserHandler) UpdateProfile(c *gin.Context) {
|
||
|
|
appCtx := appcontext.FromGinContext(c.Request.Context())
|
||
|
|
if appCtx.UserID == uuid.Nil {
|
||
|
|
h.sendErrorResponse(c, "Unauthorized", http.StatusUnauthorized)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
var req contract.UpdateUserProfileRequest
|
||
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||
|
|
h.sendValidationErrorResponse(c, "Invalid request body", constants.MissingFieldErrorCode)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
updated, err := h.userService.UpdateProfile(c.Request.Context(), appCtx.UserID, &req)
|
||
|
|
if err != nil {
|
||
|
|
logger.FromContext(c).WithError(err).Error("UserHandler::UpdateProfile -> Failed to update profile")
|
||
|
|
h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
c.JSON(http.StatusOK, contract.BuildSuccessResponse(updated))
|
||
|
|
}
|
||
|
|
|
||
|
|
func (h *UserHandler) ListTitles(c *gin.Context) {
|
||
|
|
resp, err := h.userService.ListTitles(c.Request.Context())
|
||
|
|
if err != nil {
|
||
|
|
h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||
|
|
}
|
||
|
|
|
||
|
|
func (h *UserHandler) sendErrorResponse(c *gin.Context, message string, statusCode int) {
|
||
|
|
errorResponse := &contract.ErrorResponse{
|
||
|
|
Error: message,
|
||
|
|
Code: statusCode,
|
||
|
|
Details: map[string]interface{}{},
|
||
|
|
}
|
||
|
|
c.JSON(statusCode, errorResponse)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (h *UserHandler) sendValidationErrorResponse(c *gin.Context, message string, errorCode string) {
|
||
|
|
statusCode := constants.HttpErrorMap[errorCode]
|
||
|
|
if statusCode == 0 {
|
||
|
|
statusCode = http.StatusBadRequest
|
||
|
|
}
|
||
|
|
|
||
|
|
errorResponse := &contract.ErrorResponse{
|
||
|
|
Error: message,
|
||
|
|
Code: statusCode,
|
||
|
|
Details: map[string]interface{}{
|
||
|
|
"error_code": errorCode,
|
||
|
|
"entity": constants.UserValidatorEntity,
|
||
|
|
},
|
||
|
|
}
|
||
|
|
c.JSON(statusCode, errorResponse)
|
||
|
|
}
|