wms-be/modules/user/service/user_service.go

323 lines
9.4 KiB
Go

package service
import (
"context"
"errors"
"fmt"
"github.com/Caknoooo/go-gin-clean-starter/database/entities"
authDto "github.com/Caknoooo/go-gin-clean-starter/modules/auth/dto"
authRepo "github.com/Caknoooo/go-gin-clean-starter/modules/auth/repository"
authService "github.com/Caknoooo/go-gin-clean-starter/modules/auth/service"
"github.com/Caknoooo/go-gin-clean-starter/modules/user/dto"
"github.com/Caknoooo/go-gin-clean-starter/modules/user/repository"
"github.com/Caknoooo/go-gin-clean-starter/pkg/utils"
"github.com/google/uuid"
"gorm.io/gorm"
)
type UserService interface {
Register(ctx context.Context, req dto.UserCreateRequest) (dto.UserResponse, error)
GetUserById(ctx context.Context, userId string) (dto.UserResponse, error)
Verify(ctx context.Context, req dto.UserLoginRequest) (authDto.TokenResponse, error)
SendVerificationEmail(ctx context.Context, req dto.SendVerificationEmailRequest) error
VerifyEmail(ctx context.Context, req dto.VerifyEmailRequest) (dto.VerifyEmailResponse, error)
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 {
userRepository repository.UserRepository
refreshTokenRepository authRepo.RefreshTokenRepository
jwtService authService.JWTService
db *gorm.DB
}
func NewUserService(
userRepo repository.UserRepository,
refreshTokenRepo authRepo.RefreshTokenRepository,
jwtService authService.JWTService,
db *gorm.DB,
) UserService {
return &userService{
userRepository: userRepo,
refreshTokenRepository: refreshTokenRepo,
jwtService: jwtService,
db: db,
}
}
// 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 {
return dto.UserResponse{}, err
}
if exists {
return dto.UserResponse{}, dto.ErrEmailAlreadyExists
}
enryptPassword, err := utils.HashPassword(req.Password)
if err != nil {
return dto.UserResponse{}, err
}
user := entities.M_User{
Name: req.Name,
Username: req.Username,
Email: req.Email,
Password: enryptPassword,
Gender: req.Gender,
Address: req.Address,
Phone: req.Phone,
PhotoUrl: req.PhotoUrl,
ClientID: req.ClientID,
}
createdUser, err := s.userRepository.Register(ctx, s.db, user)
if err != nil {
return dto.UserResponse{}, err
}
return dto.UserResponse{
ID: createdUser.ID.String(),
Name: createdUser.Name,
Username: createdUser.Username,
Email: createdUser.Email,
Phone: createdUser.Phone,
Gender: createdUser.Gender,
Address: createdUser.Address,
PhotoUrl: createdUser.PhotoUrl,
}, nil
}
func (s *userService) GetUserById(ctx context.Context, userId string) (dto.UserResponse, error) {
user, err := s.userRepository.GetUserById(ctx, s.db, userId)
if err != nil {
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,
Username: user.Username,
Email: user.Email,
Gender: user.Gender,
Address: user.Address,
Phone: user.Phone,
PhotoUrl: user.PhotoUrl,
Roles: roles,
}, nil
}
func (s *userService) Verify(ctx context.Context, req dto.UserLoginRequest) (authDto.TokenResponse, error) {
user, err := s.userRepository.GetUserByEmail(ctx, s.db, req.Email)
if err != nil {
return authDto.TokenResponse{}, dto.ErrEmailNotFound
}
isValid := utils.CheckPasswordHash(req.Password, user.Password)
if !isValid {
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(), roles[0].ID.String())
refreshTokenString, expiresAt := s.jwtService.GenerateRefreshToken()
refreshToken := entities.RefreshToken{
ID: uuid.New(),
UserID: user.ID,
ClientID: user.ClientID,
Token: refreshTokenString,
ExpiresAt: expiresAt,
}
_, err = s.refreshTokenRepository.Create(ctx, s.db, refreshToken)
if err != nil {
return authDto.TokenResponse{}, err
}
return authDto.TokenResponse{
AccessToken: accessToken,
RefreshToken: refreshTokenString,
}, nil
}
func (s *userService) SendVerificationEmail(ctx context.Context, req dto.SendVerificationEmailRequest) error {
user, err := s.userRepository.GetUserByEmail(ctx, s.db, req.Email)
if err != nil {
return dto.ErrEmailNotFound
}
// if user.IsVerified {
// return dto.ErrAccountAlreadyVerified
// }
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
return utils.SendMail(user.Email, subject, body)
}
func (s *userService) VerifyEmail(ctx context.Context, req dto.VerifyEmailRequest) (dto.VerifyEmailResponse, error) {
token, err := s.jwtService.ValidateToken(req.Token)
if err != nil || !token.Valid {
return dto.VerifyEmailResponse{}, dto.ErrTokenInvalid
}
userTokenInfo, err := s.jwtService.GetUserIDByToken(req.Token)
if err != nil {
return dto.VerifyEmailResponse{}, dto.ErrTokenInvalid
}
user, err := s.userRepository.GetUserById(ctx, s.db, userTokenInfo.UserID)
if err != nil {
return dto.VerifyEmailResponse{}, dto.ErrUserNotFound
}
// user.IsVerified = true
updatedUser, err := s.userRepository.Update(ctx, s.db, user)
if err != nil {
return dto.VerifyEmailResponse{}, err
}
return dto.VerifyEmailResponse{
Email: updatedUser.Email,
// IsVerified: updatedUser.IsVerified,
}, nil
}
func (s *userService) Update(ctx context.Context, req dto.UserUpdateRequest, userId string) (dto.UserUpdateResponse, error) {
user, err := s.userRepository.GetUserById(ctx, s.db, userId)
if err != nil {
return dto.UserUpdateResponse{}, dto.ErrUserNotFound
}
if req.Name != "" {
user.Name = req.Name
}
if req.Email != "" {
user.Email = req.Email
}
if req.Username != "" {
user.Username = req.Username
}
if req.Password != "" {
enryptPassword, err := utils.HashPassword(req.Password)
if err != nil {
return dto.UserUpdateResponse{}, err
}
user.Password = enryptPassword
}
if req.Gender != "" {
user.Gender = req.Gender
}
if req.Address != "" {
user.Address = req.Address
}
if req.Phone != "" {
user.Phone = req.Phone
}
if req.PhotoUrl != "" {
user.PhotoUrl = req.PhotoUrl
}
if req.ClientID != uuid.Nil {
user.ClientID = req.ClientID
}
if req.MaintenanceGroupUserID != uuid.Nil {
user.MaintenanceGroupUserID = req.MaintenanceGroupUserID
}
if req.LocationID != uuid.Nil {
user.LocationID = req.LocationID
}
updatedUser, err := s.userRepository.Update(ctx, s.db, user)
if err != nil {
return dto.UserUpdateResponse{}, err
}
return dto.UserUpdateResponse{
ID: updatedUser.ID.String(),
Name: updatedUser.Name,
Username: updatedUser.Username,
Phone: updatedUser.Phone,
Email: updatedUser.Email,
Gender: updatedUser.Gender,
Address: updatedUser.Address,
PhotoUrl: updatedUser.PhotoUrl,
}, nil
}
func (s *userService) Delete(ctx context.Context, userId string) error {
return s.userRepository.Delete(ctx, s.db, userId)
}
func (s *userService) RefreshToken(ctx context.Context, req authDto.RefreshTokenRequest) (authDto.TokenResponse, error) {
refreshToken, err := s.refreshTokenRepository.FindByToken(ctx, s.db, req.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(), roles[0].ID.String())
newRefreshTokenString, expiresAt := s.jwtService.GenerateRefreshToken()
err = s.refreshTokenRepository.DeleteByToken(ctx, s.db, req.RefreshToken)
if err != nil {
return authDto.TokenResponse{}, err
}
newRefreshToken := entities.RefreshToken{
ID: uuid.New(),
UserID: refreshToken.UserID,
Token: newRefreshTokenString,
ExpiresAt: expiresAt,
}
_, err = s.refreshTokenRepository.Create(ctx, s.db, newRefreshToken)
if err != nil {
return authDto.TokenResponse{}, err
}
return authDto.TokenResponse{
AccessToken: accessToken,
RefreshToken: newRefreshTokenString,
}, nil
}