Implement user role management and update user response structure to include roles
This commit is contained in:
parent
8357303237
commit
f28b3a5244
|
|
@ -3,6 +3,7 @@ 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"
|
||||
|
|
@ -68,22 +69,24 @@ func (c *userController) GetAllUser(ctx *gin.Context) {
|
|||
logrus.Info("Client ID: ", clientId)
|
||||
var filter = &query.UserFilter{
|
||||
ClientID: clientId,
|
||||
Name: ctx.Query("name"), // example additional filter
|
||||
Name: ctx.Query("name"),
|
||||
Includes: []string{"Roles"}, // Tambahkan ini
|
||||
}
|
||||
logrus.Info("Filter: ", filter)
|
||||
filter.BindPagination(ctx)
|
||||
|
||||
ctx.ShouldBindQuery(filter)
|
||||
|
||||
logrus.Info("Filter sebelum query: ")
|
||||
users, total, err := pagination.PaginatedQueryWithIncludable[query.M_User](c.db, filter)
|
||||
// logrus.Info("Filter: ", filter)
|
||||
if err != nil {
|
||||
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_USER, err.Error(), nil)
|
||||
ctx.JSON(http.StatusBadRequest, res)
|
||||
return
|
||||
}
|
||||
|
||||
// Convert ke DTO
|
||||
userResponses := ToUserResponses(users)
|
||||
paginationResponse := pagination.CalculatePagination(filter.Pagination, total)
|
||||
response := pagination.NewPaginatedResponse(http.StatusOK, dto.MESSAGE_SUCCESS_GET_LIST_USER, users, paginationResponse)
|
||||
response := pagination.NewPaginatedResponse(http.StatusOK, dto.MESSAGE_SUCCESS_GET_LIST_USER, userResponses, paginationResponse)
|
||||
ctx.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
|
|
@ -223,3 +226,35 @@ func (c *userController) Refresh(ctx *gin.Context) {
|
|||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,15 +64,21 @@ type (
|
|||
}
|
||||
|
||||
UserResponse struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Gender string `json:"gender"`
|
||||
Address string `json:"address"`
|
||||
Phone string `json:"phone"`
|
||||
Email string `json:"email"`
|
||||
PhotoUrl string `json:"photo_url"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Gender string `json:"gender"`
|
||||
Address string `json:"address"`
|
||||
Phone string `json:"phone"`
|
||||
Email string `json:"email"`
|
||||
PhotoUrl string `json:"photo_url"`
|
||||
Roles []UserRolesResponse `json:"roles,omitempty"`
|
||||
}
|
||||
|
||||
UserRolesResponse struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
UserPaginationResponse struct {
|
||||
|
|
@ -81,7 +87,7 @@ type (
|
|||
}
|
||||
|
||||
GetAllUserRepositoryResponse struct {
|
||||
Users []entities.User `json:"users"`
|
||||
Users []entities.M_User `json:"users"`
|
||||
dto.PaginationResponse
|
||||
}
|
||||
|
||||
|
|
@ -127,4 +133,8 @@ type (
|
|||
Email string `json:"email" form:"email" binding:"required"`
|
||||
Password string `json:"password" form:"password" binding:"required"`
|
||||
}
|
||||
|
||||
SwitchRoleRequest struct {
|
||||
RoleID string `json:"role_id" form:"role_id" binding:"required,uuid4"`
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package query
|
||||
|
||||
import (
|
||||
"github.com/Caknoooo/go-gin-clean-starter/database/entities"
|
||||
"github.com/Caknoooo/go-pagination"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
|
@ -15,12 +16,15 @@ type M_User struct {
|
|||
Phone string `json:"phone"`
|
||||
Email string `json:"email"`
|
||||
PhotoUrl string `json:"photo_url"`
|
||||
// Roles []entities.M_Role `json:"roles" gorm:"many2many:m_user_roles;"`
|
||||
Roles []entities.M_Role `gorm:"many2many:m_user_roles;foreignKey:ID;joinForeignKey:UserID;References:ID;JoinReferences:RoleID" json:"roles"`
|
||||
}
|
||||
|
||||
type UserFilter struct {
|
||||
pagination.BaseFilter
|
||||
Name string `form:"name"` // tambahkan ini
|
||||
ClientID string `form:"client_id"` // tambahkan ini
|
||||
Name string `form:"name"` // tambahkan ini
|
||||
ClientID string `form:"client_id"` // tambahkan ini
|
||||
Includes []string `form:"includes"` // tambahkan ini
|
||||
}
|
||||
|
||||
func (f *UserFilter) ApplyFilters(query *gorm.DB) *gorm.DB {
|
||||
|
|
@ -31,6 +35,15 @@ func (f *UserFilter) ApplyFilters(query *gorm.DB) *gorm.DB {
|
|||
if f.ClientID != "" {
|
||||
query = query.Where("client_id = ?", f.ClientID)
|
||||
}
|
||||
|
||||
// Manual preload untuk roles dengan field terbatas
|
||||
// for _, include := range f.Includes {
|
||||
// if include == "Roles" {
|
||||
// query = query.Preload("Roles", func(db *gorm.DB) *gorm.DB {
|
||||
// return db.Select("id", "name") // Hanya ambil id dan name
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
return query
|
||||
}
|
||||
|
||||
|
|
@ -66,5 +79,7 @@ func (f *UserFilter) Validate() {
|
|||
}
|
||||
|
||||
func (f *UserFilter) GetAllowedIncludes() map[string]bool {
|
||||
return map[string]bool{}
|
||||
return map[string]bool{
|
||||
"Roles": true,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ type (
|
|||
CheckEmail(ctx context.Context, tx *gorm.DB, email string) (entities.M_User, bool, error)
|
||||
Update(ctx context.Context, tx *gorm.DB, user entities.M_User) (entities.M_User, error)
|
||||
Delete(ctx context.Context, tx *gorm.DB, userId string) error
|
||||
SwitchRole(ctx context.Context, tx *gorm.DB, userId string, roleId string) (entities.M_User, error)
|
||||
}
|
||||
|
||||
userRepository struct {
|
||||
|
|
@ -28,6 +29,69 @@ func NewUserRepository(db *gorm.DB) UserRepository {
|
|||
}
|
||||
}
|
||||
|
||||
// SwitchRole implements UserRepository.
|
||||
func (r *userRepository) SwitchRole(ctx context.Context, tx *gorm.DB, userId string, roleId string) (entities.M_User, error) {
|
||||
if tx == nil {
|
||||
tx = r.db
|
||||
}
|
||||
var user entities.M_User
|
||||
// Preload UserRoles dan Role di dalamnya
|
||||
if err := tx.WithContext(ctx).
|
||||
Where("id = ?", userId).
|
||||
Preload("UserRoles.Role").
|
||||
Take(&user).Error; err != nil {
|
||||
return entities.M_User{}, err
|
||||
}
|
||||
var role entities.M_Role
|
||||
if err := tx.WithContext(ctx).Where("id = ?", roleId).Take(&role).Error; err != nil {
|
||||
return entities.M_User{}, err
|
||||
}
|
||||
// Ganti semua role user dengan role baru (hapus yang lama, insert yang baru)
|
||||
if err := tx.WithContext(ctx).
|
||||
Where("user_id = ?", userId).
|
||||
Delete(&entities.M_User_Role{}).Error; err != nil {
|
||||
return entities.M_User{}, err
|
||||
}
|
||||
newUserRole := entities.M_User_Role{
|
||||
UserID: user.ID,
|
||||
RoleID: role.ID,
|
||||
}
|
||||
if err := tx.WithContext(ctx).Create(&newUserRole).Error; err != nil {
|
||||
return entities.M_User{}, err
|
||||
}
|
||||
// Ambil ulang user beserta roles-nya
|
||||
if err := tx.WithContext(ctx).
|
||||
Where("id = ?", userId).
|
||||
Preload("UserRoles.Role").
|
||||
Take(&user).Error; err != nil {
|
||||
return entities.M_User{}, err
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// func (r *userRepository) SwitchRole(ctx context.Context, tx *gorm.DB, userId string, roleId string) (entities.M_User, error) {
|
||||
// if tx == nil {
|
||||
// tx = r.db
|
||||
// }
|
||||
// var user entities.M_User
|
||||
// if err := tx.WithContext(ctx).Where("id = ?", userId).Take(&user).Error; err != nil {
|
||||
// return entities.M_User{}, err
|
||||
// }
|
||||
// // Update field ActiveRoleID
|
||||
// user.ActiveRoleID = roleId
|
||||
// if err := tx.WithContext(ctx).Save(&user).Error; err != nil {
|
||||
// return entities.M_User{}, err
|
||||
// }
|
||||
// // Preload roles jika perlu
|
||||
// if err := tx.WithContext(ctx).
|
||||
// Where("id = ?", userId).
|
||||
// Preload("UserRoles.Role").
|
||||
// Take(&user).Error; err != nil {
|
||||
// return entities.M_User{}, err
|
||||
// }
|
||||
// return user, nil
|
||||
// }
|
||||
|
||||
func (r *userRepository) Register(ctx context.Context, tx *gorm.DB, user entities.M_User) (entities.M_User, error) {
|
||||
if tx == nil {
|
||||
tx = r.db
|
||||
|
|
@ -46,7 +110,10 @@ func (r *userRepository) GetUserById(ctx context.Context, tx *gorm.DB, userId st
|
|||
}
|
||||
|
||||
var user entities.M_User
|
||||
if err := tx.WithContext(ctx).Where("id = ?", userId).Take(&user).Error; err != nil {
|
||||
if err := tx.WithContext(ctx).
|
||||
Preload("Roles").
|
||||
Where("id = ?", userId).
|
||||
Take(&user).Error; err != nil {
|
||||
return entities.M_User{}, err
|
||||
}
|
||||
|
||||
|
|
@ -59,7 +126,10 @@ func (r *userRepository) GetUserByEmail(ctx context.Context, tx *gorm.DB, email
|
|||
}
|
||||
|
||||
var user entities.M_User
|
||||
if err := tx.WithContext(ctx).Where("email = ?", email).Take(&user).Error; err != nil {
|
||||
if err := tx.WithContext(ctx).
|
||||
Preload("Roles").
|
||||
Where("email = ?", email).
|
||||
Take(&user).Error; err != nil {
|
||||
return entities.M_User{}, err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ func Start(db *gorm.DB) {
|
|||
c := cron.New()
|
||||
// Setiap hari jam 00:00
|
||||
_, err := c.AddFunc("0 0 * * *", func() {
|
||||
if err := db.Model(&entities.User{}).Where("is_verified = ?", true).Update("is_verified", false).Error; err != nil {
|
||||
if err := db.Model(&entities.M_User{}).Where("is_verified = ?", true).Update("is_verified", false).Error; err != nil {
|
||||
log.Println("Failed to update user verification:", err)
|
||||
} else {
|
||||
log.Println("All users set is_verified to false at", time.Now())
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package service
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/Caknoooo/go-gin-clean-starter/database/entities"
|
||||
|
|
@ -24,6 +25,7 @@ type UserService interface {
|
|||
Update(ctx context.Context, req dto.UserUpdateRequest, userId string) (dto.UserUpdateResponse, error)
|
||||
Delete(ctx context.Context, userId string) error
|
||||
RefreshToken(ctx context.Context, req authDto.RefreshTokenRequest) (authDto.TokenResponse, error)
|
||||
SwitchRole(ctx context.Context, req dto.SwitchRoleRequest) (authDto.TokenResponse, error)
|
||||
}
|
||||
|
||||
type userService struct {
|
||||
|
|
@ -47,6 +49,11 @@ func NewUserService(
|
|||
}
|
||||
}
|
||||
|
||||
// SwitchRole implements UserService.
|
||||
func (s *userService) SwitchRole(ctx context.Context, req dto.SwitchRoleRequest) (authDto.TokenResponse, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (s *userService) Register(ctx context.Context, req dto.UserCreateRequest) (dto.UserResponse, error) {
|
||||
_, exists, err := s.userRepository.CheckEmail(ctx, s.db, req.Email)
|
||||
if err != nil && err != gorm.ErrRecordNotFound {
|
||||
|
|
@ -95,6 +102,14 @@ func (s *userService) GetUserById(ctx context.Context, userId string) (dto.UserR
|
|||
return dto.UserResponse{}, err
|
||||
}
|
||||
|
||||
var roles []dto.UserRolesResponse
|
||||
for _, ur := range user.Roles {
|
||||
roles = append(roles, dto.UserRolesResponse{
|
||||
ID: ur.ID.String(),
|
||||
Name: ur.Name,
|
||||
})
|
||||
}
|
||||
|
||||
return dto.UserResponse{
|
||||
ID: user.ID.String(),
|
||||
Name: user.Name,
|
||||
|
|
@ -104,6 +119,7 @@ func (s *userService) GetUserById(ctx context.Context, userId string) (dto.UserR
|
|||
Address: user.Address,
|
||||
Phone: user.Phone,
|
||||
PhotoUrl: user.PhotoUrl,
|
||||
Roles: roles,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
@ -118,14 +134,25 @@ func (s *userService) Verify(ctx context.Context, req dto.UserLoginRequest) (aut
|
|||
fmt.Println("Password validation error:", err)
|
||||
return authDto.TokenResponse{}, dto.ErrUserNotFound
|
||||
}
|
||||
// Ambil roles dari UserRoles
|
||||
roles := append([]entities.M_Role{}, user.Roles...)
|
||||
// var roles []dto.UserRolesResponse
|
||||
// for _, ur := range user.Roles {
|
||||
// roles = append(roles, dto.UserRolesResponse{
|
||||
// ID: ur.ID.String(),
|
||||
// Name: ur.Name,
|
||||
// })
|
||||
// }
|
||||
if len(roles) == 0 {
|
||||
return authDto.TokenResponse{}, errors.New("user has no roles assigned")
|
||||
}
|
||||
|
||||
accessToken := s.jwtService.GenerateAccessToken(user.ClientID.String(), user.ID.String())
|
||||
accessToken := s.jwtService.GenerateAccessToken(user.ClientID.String(), user.ID.String(), roles[0].ID.String())
|
||||
refreshTokenString, expiresAt := s.jwtService.GenerateRefreshToken()
|
||||
|
||||
refreshToken := entities.RefreshToken{
|
||||
ID: uuid.New(),
|
||||
UserID: user.ID,
|
||||
// TenantID: user.TenantID,
|
||||
ID: uuid.New(),
|
||||
UserID: user.ID,
|
||||
ClientID: user.ClientID,
|
||||
Token: refreshTokenString,
|
||||
ExpiresAt: expiresAt,
|
||||
|
|
@ -152,7 +179,12 @@ func (s *userService) SendVerificationEmail(ctx context.Context, req dto.SendVer
|
|||
// return dto.ErrAccountAlreadyVerified
|
||||
// }
|
||||
|
||||
verificationToken := s.jwtService.GenerateAccessToken(user.ClientID.String(), user.ID.String())
|
||||
roles := append([]entities.M_Role{}, user.Roles...)
|
||||
if len(roles) == 0 {
|
||||
return errors.New("user has no roles assigned")
|
||||
}
|
||||
|
||||
verificationToken := s.jwtService.GenerateAccessToken(user.ClientID.String(), user.ID.String(), roles[0].ID.String())
|
||||
|
||||
subject := "Email Verification"
|
||||
body := "Please verify your email using this token: " + verificationToken
|
||||
|
|
@ -258,8 +290,12 @@ func (s *userService) RefreshToken(ctx context.Context, req authDto.RefreshToken
|
|||
if err != nil {
|
||||
return authDto.TokenResponse{}, err
|
||||
}
|
||||
roles := append([]entities.M_Role{}, refreshToken.User.Roles...)
|
||||
if len(roles) == 0 {
|
||||
return authDto.TokenResponse{}, errors.New("user has no roles assigned")
|
||||
}
|
||||
|
||||
accessToken := s.jwtService.GenerateAccessToken(refreshToken.ClientID.String(), refreshToken.UserID.String())
|
||||
accessToken := s.jwtService.GenerateAccessToken(refreshToken.ClientID.String(), refreshToken.UserID.String(), roles[0].ID.String())
|
||||
newRefreshTokenString, expiresAt := s.jwtService.GenerateRefreshToken()
|
||||
|
||||
err = s.refreshTokenRepository.DeleteByToken(ctx, s.db, req.RefreshToken)
|
||||
|
|
|
|||
Loading…
Reference in New Issue