Init Eslogad
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"eslogad-be/internal/util"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"eslogad-be/internal/constants"
|
||||
"eslogad-be/internal/contract"
|
||||
"eslogad-be/internal/logger"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type AuthHandler struct {
|
||||
authService AuthService
|
||||
}
|
||||
|
||||
func NewAuthHandler(authService AuthService) *AuthHandler {
|
||||
return &AuthHandler{
|
||||
authService: authService,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *AuthHandler) Login(c *gin.Context) {
|
||||
var req contract.LoginRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
logger.FromContext(c.Request.Context()).WithError(err).Error("AuthHandler::Login -> request binding failed")
|
||||
h.sendValidationErrorResponse(c, "Invalid request body", constants.MissingFieldErrorCode)
|
||||
return
|
||||
}
|
||||
|
||||
if strings.TrimSpace(req.Email) == "" {
|
||||
logger.FromContext(c.Request.Context()).Error("AuthHandler::Login -> email is required")
|
||||
h.sendValidationErrorResponse(c, "Email is required", constants.MissingFieldErrorCode)
|
||||
return
|
||||
}
|
||||
|
||||
if strings.TrimSpace(req.Password) == "" {
|
||||
logger.FromContext(c.Request.Context()).Error("AuthHandler::Login -> password is required")
|
||||
h.sendValidationErrorResponse(c, "Password is required", constants.MissingFieldErrorCode)
|
||||
return
|
||||
}
|
||||
|
||||
loginResponse, err := h.authService.Login(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
logger.FromContext(c.Request.Context()).WithError(err).Error("AuthHandler::Login -> Failed to login")
|
||||
h.sendErrorResponse(c, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
logger.FromContext(c.Request.Context()).Infof("AuthHandler::Login -> Successfully logged in user = %s", loginResponse.User.Email)
|
||||
|
||||
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(loginResponse), "AuthHandler::Login")
|
||||
}
|
||||
|
||||
func (h *AuthHandler) Logout(c *gin.Context) {
|
||||
token := h.extractTokenFromHeader(c)
|
||||
if token == "" {
|
||||
logger.FromContext(c.Request.Context()).Error("AuthHandler::Logout -> token is required")
|
||||
h.sendErrorResponse(c, "Token is required", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
err := h.authService.Logout(c.Request.Context(), token)
|
||||
if err != nil {
|
||||
logger.FromContext(c.Request.Context()).WithError(err).Error("AuthHandler::Logout -> Failed to logout")
|
||||
h.sendErrorResponse(c, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
logger.FromContext(c.Request.Context()).Info("AuthHandler::Logout -> Successfully logged out")
|
||||
c.JSON(http.StatusOK, &contract.SuccessResponse{Message: "Successfully logged out"})
|
||||
}
|
||||
|
||||
func (h *AuthHandler) RefreshToken(c *gin.Context) {
|
||||
token := h.extractTokenFromHeader(c)
|
||||
if token == "" {
|
||||
logger.FromContext(c.Request.Context()).Error("AuthHandler::RefreshToken -> token is required")
|
||||
h.sendErrorResponse(c, "Token is required", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
loginResponse, err := h.authService.RefreshToken(c.Request.Context(), token)
|
||||
if err != nil {
|
||||
logger.FromContext(c.Request.Context()).WithError(err).Error("AuthHandler::RefreshToken -> Failed to refresh token")
|
||||
h.sendErrorResponse(c, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
logger.FromContext(c.Request.Context()).Infof("AuthHandler::RefreshToken -> Successfully refreshed token for user = %s", loginResponse.User.Email)
|
||||
c.JSON(http.StatusOK, loginResponse)
|
||||
}
|
||||
|
||||
func (h *AuthHandler) ValidateToken(c *gin.Context) {
|
||||
token := h.extractTokenFromHeader(c)
|
||||
if token == "" {
|
||||
logger.FromContext(c.Request.Context()).Error("AuthHandler::ValidateToken -> token is required")
|
||||
h.sendErrorResponse(c, "Token is required", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
userResponse, err := h.authService.ValidateToken(token)
|
||||
if err != nil {
|
||||
logger.FromContext(c.Request.Context()).WithError(err).Error("AuthHandler::ValidateToken -> Failed to validate token")
|
||||
h.sendErrorResponse(c, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
logger.FromContext(c.Request.Context()).Infof("AuthHandler::ValidateToken -> Successfully validated token for user = %s", userResponse.Email)
|
||||
c.JSON(http.StatusOK, userResponse)
|
||||
}
|
||||
|
||||
func (h *AuthHandler) GetProfile(c *gin.Context) {
|
||||
token := h.extractTokenFromHeader(c)
|
||||
if token == "" {
|
||||
logger.FromContext(c.Request.Context()).Error("AuthHandler::GetProfile -> token is required")
|
||||
h.sendErrorResponse(c, "Token is required", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
userResponse, err := h.authService.ValidateToken(token)
|
||||
if err != nil {
|
||||
logger.FromContext(c.Request.Context()).WithError(err).Error("AuthHandler::GetProfile -> Failed to get profile")
|
||||
h.sendErrorResponse(c, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
logger.FromContext(c.Request.Context()).Infof("AuthHandler::GetProfile -> Successfully retrieved profile for user = %s", userResponse.Email)
|
||||
c.JSON(http.StatusOK, &contract.SuccessResponse{Data: userResponse, Message: "success get user profile"})
|
||||
}
|
||||
|
||||
func (h *AuthHandler) extractTokenFromHeader(c *gin.Context) string {
|
||||
authHeader := c.GetHeader("Authorization")
|
||||
if authHeader == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Expected format: "Bearer <token>"
|
||||
parts := strings.Split(authHeader, " ")
|
||||
if len(parts) != 2 || parts[0] != "Bearer" {
|
||||
return ""
|
||||
}
|
||||
|
||||
return parts[1]
|
||||
}
|
||||
|
||||
func (h *AuthHandler) sendErrorResponse(c *gin.Context, message string, statusCode int) {
|
||||
errorResponse := &contract.ErrorResponse{
|
||||
Error: "error",
|
||||
Message: message,
|
||||
Code: statusCode,
|
||||
}
|
||||
c.JSON(statusCode, errorResponse)
|
||||
}
|
||||
|
||||
func (h *AuthHandler) sendValidationErrorResponse(c *gin.Context, message string, errorCode string) {
|
||||
errorResponse := &contract.ErrorResponse{
|
||||
Error: "validation_error",
|
||||
Message: message,
|
||||
Code: http.StatusBadRequest,
|
||||
Details: map[string]interface{}{
|
||||
"error_code": errorCode,
|
||||
"entity": constants.AuthHandlerEntity,
|
||||
},
|
||||
}
|
||||
c.JSON(http.StatusBadRequest, errorResponse)
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"eslogad-be/internal/contract"
|
||||
)
|
||||
|
||||
type AuthService interface {
|
||||
Login(ctx context.Context, req *contract.LoginRequest) (*contract.LoginResponse, error)
|
||||
ValidateToken(tokenString string) (*contract.UserResponse, error)
|
||||
RefreshToken(ctx context.Context, tokenString string) (*contract.LoginResponse, error)
|
||||
Logout(ctx context.Context, tokenString string) error
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type CommonMiddleware struct{}
|
||||
|
||||
func NewCommonMiddleware() *CommonMiddleware {
|
||||
return &CommonMiddleware{}
|
||||
}
|
||||
|
||||
func (m *CommonMiddleware) CORS(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
|
||||
|
||||
if r.Method == "OPTIONS" {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func (m *CommonMiddleware) ContentType(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func (m *CommonMiddleware) Logging(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
start := time.Now()
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
|
||||
_ = time.Since(start)
|
||||
})
|
||||
}
|
||||
|
||||
func (m *CommonMiddleware) Recovery(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
|
||||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||
}
|
||||
}()
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"eslogad-be/internal/appcontext"
|
||||
"eslogad-be/internal/contract"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type FileService interface {
|
||||
UploadProfileAvatar(ctx context.Context, userID uuid.UUID, filename string, content []byte, contentType string) (string, error)
|
||||
UploadDocument(ctx context.Context, userID uuid.UUID, filename string, content []byte, contentType string) (string, string, error)
|
||||
}
|
||||
|
||||
type FileHandler struct {
|
||||
service FileService
|
||||
}
|
||||
|
||||
func NewFileHandler(service FileService) *FileHandler {
|
||||
return &FileHandler{service: service}
|
||||
}
|
||||
|
||||
func (h *FileHandler) UploadProfileAvatar(c *gin.Context) {
|
||||
appCtx := appcontext.FromGinContext(c.Request.Context())
|
||||
if appCtx.UserID == uuid.Nil {
|
||||
c.JSON(http.StatusUnauthorized, &contract.ErrorResponse{Error: "Unauthorized", Code: http.StatusUnauthorized})
|
||||
return
|
||||
}
|
||||
file, header, err := c.Request.FormFile("file")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "file is required", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
content, err := io.ReadAll(io.LimitReader(file, 10<<20))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "failed to read file", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
ct := header.Header.Get("Content-Type")
|
||||
url, err := h.service.UploadProfileAvatar(c.Request.Context(), appCtx.UserID, header.Filename, content, ct)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(map[string]string{"url": url}))
|
||||
}
|
||||
|
||||
func (h *FileHandler) UploadDocument(c *gin.Context) {
|
||||
appCtx := appcontext.FromGinContext(c.Request.Context())
|
||||
if appCtx.UserID == uuid.Nil {
|
||||
c.JSON(http.StatusUnauthorized, &contract.ErrorResponse{Error: "Unauthorized", Code: http.StatusUnauthorized})
|
||||
return
|
||||
}
|
||||
file, header, err := c.Request.FormFile("file")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "file is required", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
content, err := io.ReadAll(io.LimitReader(file, 20<<20))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "failed to read file", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
ct := header.Header.Get("Content-Type")
|
||||
url, key, err := h.service.UploadDocument(c.Request.Context(), appCtx.UserID, header.Filename, content, ct)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(map[string]string{"url": url, "key": key}))
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"eslogad-be/internal/logger"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type HealthHandler struct {
|
||||
}
|
||||
|
||||
func NewHealthHandler() *HealthHandler {
|
||||
return &HealthHandler{}
|
||||
}
|
||||
|
||||
func (hh *HealthHandler) HealthCheck(c *gin.Context) {
|
||||
log := logger.NewContextLogger(c, "healthCheck")
|
||||
log.Info("Health Check success")
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"status": "Healthy!!",
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,306 @@
|
||||
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)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"eslogad-be/internal/contract"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type UserService interface {
|
||||
CreateUser(ctx context.Context, req *contract.CreateUserRequest) (*contract.UserResponse, error)
|
||||
UpdateUser(ctx context.Context, id uuid.UUID, req *contract.UpdateUserRequest) (*contract.UserResponse, error)
|
||||
DeleteUser(ctx context.Context, id uuid.UUID) error
|
||||
GetUserByID(ctx context.Context, id uuid.UUID) (*contract.UserResponse, error)
|
||||
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
|
||||
|
||||
GetProfile(ctx context.Context, userID uuid.UUID) (*contract.UserProfileResponse, error)
|
||||
UpdateProfile(ctx context.Context, userID uuid.UUID, req *contract.UpdateUserProfileRequest) (*contract.UserProfileResponse, error)
|
||||
|
||||
ListTitles(ctx context.Context) (*contract.ListTitlesResponse, error)
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"eslogad-be/internal/contract"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type UserValidator interface {
|
||||
ValidateCreateUserRequest(req *contract.CreateUserRequest) (error, string)
|
||||
ValidateUpdateUserRequest(req *contract.UpdateUserRequest) (error, string)
|
||||
ValidateListUsersRequest(req *contract.ListUsersRequest) (error, string)
|
||||
ValidateChangePasswordRequest(req *contract.ChangePasswordRequest) (error, string)
|
||||
ValidateUserID(userID uuid.UUID) (error, string)
|
||||
ValidateUpdateUserOutletRequest(req *contract.UpdateUserOutletRequest) (error, string)
|
||||
}
|
||||
Reference in New Issue
Block a user