package controller import ( "net/http" // "github.com/Caknoooo/go-gin-clean-starter/database/entities" authDto "github.com/Caknoooo/go-gin-clean-starter/modules/auth/dto" "github.com/Caknoooo/go-gin-clean-starter/modules/user/dto" "github.com/Caknoooo/go-gin-clean-starter/modules/user/query" "github.com/Caknoooo/go-gin-clean-starter/modules/user/service" "github.com/Caknoooo/go-gin-clean-starter/pkg/constants" "github.com/Caknoooo/go-gin-clean-starter/pkg/utils" "github.com/Caknoooo/go-pagination" "github.com/gin-gonic/gin" "github.com/samber/do" "gorm.io/gorm" ) type ( UserController interface { Register(ctx *gin.Context) Login(ctx *gin.Context) Me(ctx *gin.Context) GetUserById(ctx *gin.Context) Refresh(ctx *gin.Context) GetAllUser(ctx *gin.Context) SendVerificationEmail(ctx *gin.Context) VerifyEmail(ctx *gin.Context) Update(ctx *gin.Context) Delete(ctx *gin.Context) } userController struct { userService service.UserService db *gorm.DB } ) func NewUserController(injector *do.Injector, us service.UserService) UserController { db := do.MustInvokeNamed[*gorm.DB](injector, constants.DB) return &userController{ userService: us, db: db, } } // Register godoc // @Summary Register a new user // @Description Create a new user under the authenticated client. Validates input and returns created user. // @Tags Users // @Accept json // @Produce json // @Param body body dto.UserCreateRequest true "Register payload" // @Success 200 {object} utils.Response // @Failure 400 {object} map[string]interface{} // @Failure 500 {object} map[string]interface{} // @Router /users/register [post] func (c *userController) Register(ctx *gin.Context) { var user dto.UserCreateRequest if err := ctx.ShouldBind(&user); err != nil { res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_DATA_FROM_BODY, err.Error(), nil) ctx.AbortWithStatusJSON(http.StatusBadRequest, res) return } result, err := c.userService.Register(ctx.Request.Context(), user) if err != nil { res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_REGISTER_USER, err.Error(), nil) ctx.JSON(http.StatusBadRequest, res) return } res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_REGISTER_USER, result) ctx.JSON(http.StatusOK, res) } // GetAllUser godoc // @Summary Get list of users // @Description Get paginated list of users for the current client. Supports filtering by name and including related roles. // @Tags Users // @Accept json // @Produce json // @Param name query string false "Filter by name (partial match)" // @Param page query int false "Page number (default: 1)" // @Param page_size query int false "Page size (default: 10)" // @Param sort query string false "Sort expression, e.g. created_at desc" // @Security ApiKeyAuth // @Success 200 {array} utils.ResponseWithPagination // @Failure 400 {object} map[string]interface{} // @Router /users [get] func (c *userController) GetAllUser(ctx *gin.Context) { clientId := ctx.MustGet("client_id").(string) var filter = &query.UserFilter{ ClientID: clientId, Name: ctx.Query("name"), Includes: []string{"Roles"}, } filter.BindPagination(ctx) ctx.ShouldBindQuery(filter) users, total, err := pagination.PaginatedQueryWithIncludable[query.M_User](c.db, filter) if err != nil { res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_USER, err.Error(), nil) ctx.JSON(http.StatusBadRequest, res) return } userResponses := ToUserResponses(users) paginationResponse := pagination.CalculatePagination(filter.Pagination, total) response := pagination.NewPaginatedResponse(http.StatusOK, dto.MESSAGE_SUCCESS_GET_LIST_USER, userResponses, paginationResponse) ctx.JSON(http.StatusOK, response) } // Me godoc // @Summary Get current user // @Description Return the authenticated user's profile. // @Tags Users // @Accept json // @Produce json // @Security ApiKeyAuth // @Success 200 {object} utils.Response // @Failure 400 {object} map[string]interface{} // @Router /users/me [get] func (c *userController) Me(ctx *gin.Context) { userId := ctx.MustGet("user_id").(string) result, err := c.userService.GetUserById(ctx.Request.Context(), userId) if err != nil { res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_USER, err.Error(), nil) ctx.JSON(http.StatusBadRequest, res) return } res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_GET_USER, result) ctx.JSON(http.StatusOK, res) } // GetUserById godoc // @Summary Get user by ID // @Description Get details of a user by their ID. Requires appropriate permissions. // @Tags Users // @Accept json // @Produce json // @Param id path string true "User ID" // @Security ApiKeyAuth // @Success 200 {object} utils.Response // @Failure 400 {object} map[string]interface{} // @Router /users/{id} [get] func (c *userController) GetUserById(ctx *gin.Context) { userId := ctx.Param("id") result, err := c.userService.GetUserById(ctx.Request.Context(), userId) if err != nil { res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_USER, err.Error(), nil) ctx.JSON(http.StatusBadRequest, res) return } res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_GET_USER, result) ctx.JSON(http.StatusOK, res) } // Login godoc // @Summary User login // @Description Authenticate user and return access & refresh tokens. // @Tags Auth // @Accept json // @Produce json // @Param body body dto.UserLoginRequest true "Login payload" // @Success 200 {object} map[string]interface{} // @Failure 400 {object} map[string]interface{} // @Router /auth/login [post] func (c *userController) Login(ctx *gin.Context) { var req dto.UserLoginRequest if err := ctx.ShouldBind(&req); err != nil { response := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_DATA_FROM_BODY, err.Error(), nil) ctx.AbortWithStatusJSON(http.StatusBadRequest, response) return } result, err := c.userService.Verify(ctx.Request.Context(), req) if err != nil { res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_LOGIN, err.Error(), nil) ctx.JSON(http.StatusBadRequest, res) return } res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_LOGIN, result) ctx.JSON(http.StatusOK, res) } // SendVerificationEmail godoc // @Summary Send verification email // @Description Send email with verification code/link to a user's email address. // @Tags Users // @Accept json // @Produce json // @Param body body dto.SendVerificationEmailRequest true "Email request payload" // @Success 200 {object} map[string]interface{} // @Failure 400 {object} map[string]interface{} // @Router /users/send-verification-email [post] func (c *userController) SendVerificationEmail(ctx *gin.Context) { var req dto.SendVerificationEmailRequest if err := ctx.ShouldBind(&req); err != nil { res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_DATA_FROM_BODY, err.Error(), nil) ctx.AbortWithStatusJSON(http.StatusBadRequest, res) return } err := c.userService.SendVerificationEmail(ctx.Request.Context(), req) if err != nil { res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_PROSES_REQUEST, err.Error(), nil) ctx.AbortWithStatusJSON(http.StatusBadRequest, res) return } res := utils.BuildResponseSuccess(dto.MESSAGE_SEND_VERIFICATION_EMAIL_SUCCESS, nil) ctx.JSON(http.StatusOK, res) } // VerifyEmail godoc // @Summary Verify user email // @Description Verify a user's email using code or token sent via email. // @Tags Users // @Accept json // @Produce json // @Param body body dto.VerifyEmailRequest true "Verify email payload" // @Success 200 {object} utils.Response // @Failure 400 {object} map[string]interface{} // @Router /users/verify-email [post] func (c *userController) VerifyEmail(ctx *gin.Context) { var req dto.VerifyEmailRequest if err := ctx.ShouldBind(&req); err != nil { res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_DATA_FROM_BODY, err.Error(), nil) ctx.AbortWithStatusJSON(http.StatusBadRequest, res) return } result, err := c.userService.VerifyEmail(ctx.Request.Context(), req) if err != nil { res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_VERIFY_EMAIL, err.Error(), nil) ctx.AbortWithStatusJSON(http.StatusBadRequest, res) return } res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_VERIFY_EMAIL, result) ctx.JSON(http.StatusOK, res) } // Update godoc // @Summary Update current user // @Description Update profile of the authenticated user. Use multipart/form-data if uploading files (photo). // @Tags Users // @Accept json // @Produce json // @Param body body dto.UserUpdateRequest true "Update payload" // @Security ApiKeyAuth // @Success 200 {object} utils.Response // @Failure 400 {object} map[string]interface{} // @Router /users [put] func (c *userController) Update(ctx *gin.Context) { var req dto.UserUpdateRequest if err := ctx.ShouldBind(&req); err != nil { res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_DATA_FROM_BODY, err.Error(), nil) ctx.AbortWithStatusJSON(http.StatusBadRequest, res) return } userId := ctx.MustGet("user_id").(string) result, err := c.userService.Update(ctx.Request.Context(), req, userId) if err != nil { res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_UPDATE_USER, err.Error(), nil) ctx.JSON(http.StatusBadRequest, res) return } res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_UPDATE_USER, result) ctx.JSON(http.StatusOK, res) } // Delete godoc // @Summary Delete current user // @Description Delete the authenticated user's account (soft/hard depends on implementation). // @Tags Users // @Accept json // @Produce json // @Security ApiKeyAuth // @Success 200 {object} map[string]interface{} // @Failure 400 {object} map[string]interface{} // @Router /users [delete] func (c *userController) Delete(ctx *gin.Context) { userId := ctx.MustGet("user_id").(string) if err := c.userService.Delete(ctx.Request.Context(), userId); err != nil { res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_DELETE_USER, err.Error(), nil) ctx.AbortWithStatusJSON(http.StatusBadRequest, res) return } res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_DELETE_USER, nil) ctx.JSON(http.StatusOK, res) } // Refresh godoc // @Summary Refresh auth token // @Description Exchange a refresh token for a new access token (and optionally a new refresh token). // @Tags Auth // @Accept json // @Produce json // @Param request body object true "Refresh token payload" example:{"refresh_token":"string"} // @Success 200 {object} map[string]interface{} // @Failure 401 {object} map[string]interface{} // @Failure 400 {object} map[string]interface{} // @Router /auth/refresh [post] func (c *userController) Refresh(ctx *gin.Context) { var req authDto.RefreshTokenRequest if err := ctx.ShouldBind(&req); err != nil { res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_DATA_FROM_BODY, err.Error(), nil) ctx.AbortWithStatusJSON(http.StatusBadRequest, res) return } result, err := c.userService.RefreshToken(ctx.Request.Context(), req) if err != nil { res := utils.BuildResponseFailed(authDto.MESSAGE_FAILED_REFRESH_TOKEN, err.Error(), nil) ctx.JSON(http.StatusUnauthorized, res) return } res := utils.BuildResponseSuccess(authDto.MESSAGE_SUCCESS_REFRESH_TOKEN, result) ctx.JSON(http.StatusOK, res) } // Function untuk convert dari M_User ke UserResponse func ToUserResponse(user query.M_User) dto.UserResponse { var roles []dto.UserRolesResponse for _, role := range user.Roles { roles = append(roles, dto.UserRolesResponse{ ID: role.ID.String(), // pastikan role.ID bertipe uuid/atau string Name: role.Name, }) } return dto.UserResponse{ ID: user.ID, Name: user.Name, Username: user.Username, Gender: user.Gender, Address: user.Address, Phone: user.Phone, Email: user.Email, PhotoUrl: user.PhotoUrl, Roles: roles, } } // Function untuk convert slice of M_User func ToUserResponses(users []query.M_User) []dto.UserResponse { var responses []dto.UserResponse for _, user := range users { responses = append(responses, ToUserResponse(user)) } return responses }