433 lines
12 KiB
Go
433 lines
12 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"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"
|
|
pkgdto "github.com/Caknoooo/go-gin-clean-starter/pkg/dto"
|
|
"github.com/Caknoooo/go-gin-clean-starter/pkg/utils"
|
|
"github.com/google/uuid"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type UserService interface {
|
|
Create(ctx context.Context, req dto.UserCreateRequest) (dto.UserResponse, error)
|
|
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)
|
|
GetAll(ctx context.Context) ([]dto.UserResponse, error)
|
|
}
|
|
|
|
type userService struct {
|
|
userRepository repository.UserRepository
|
|
refreshTokenRepository authRepo.RefreshTokenRepository
|
|
jwtService authService.JWTService
|
|
db *gorm.DB
|
|
}
|
|
|
|
// GetAll implements UserService.
|
|
func (s *userService) GetAll(ctx context.Context) ([]dto.UserResponse, error) {
|
|
users, err := s.userRepository.GetAll(ctx, s.db)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var responses []dto.UserResponse
|
|
for _, user := range users {
|
|
var roles []dto.UserRolesResponse
|
|
for _, role := range user.Roles {
|
|
roles = append(roles, dto.UserRolesResponse{
|
|
ID: role.ID.String(),
|
|
Name: role.Name,
|
|
})
|
|
}
|
|
|
|
client := pkgdto.IdNameResponse{}
|
|
if user.Client.ID != uuid.Nil {
|
|
client.ID = user.Client.ID.String()
|
|
client.Name = user.Client.Name
|
|
}
|
|
|
|
responses = append(responses, 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,
|
|
Client: client,
|
|
})
|
|
}
|
|
|
|
return responses, nil
|
|
}
|
|
|
|
// Create implements UserService.
|
|
// Create implements UserService.
|
|
func (s *userService) Create(ctx context.Context, req dto.UserCreateRequest) (dto.UserResponse, error) {
|
|
// Cek apakah email sudah terdaftar
|
|
// _, 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
|
|
// }
|
|
|
|
// Hash password
|
|
hashedPassword, 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: hashedPassword,
|
|
Gender: req.Gender,
|
|
Address: req.Address,
|
|
Phone: req.Phone,
|
|
PhotoUrl: req.PhotoUrl,
|
|
ClientID: req.ClientID,
|
|
MaintenanceGroupUserID: req.MaintenanceGroupUserID,
|
|
LocationID: req.LocationID,
|
|
}
|
|
|
|
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 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,
|
|
})
|
|
}
|
|
|
|
var client pkgdto.IdNameResponse
|
|
client = pkgdto.IdNameResponse{
|
|
ID: user.Client.ID.String(),
|
|
Name: user.Client.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,
|
|
Client: client,
|
|
}, nil
|
|
}
|
|
|
|
func (s *userService) Verify(ctx context.Context, req dto.UserLoginRequest) (authDto.TokenResponse, error) {
|
|
var user entities.M_User
|
|
var err error
|
|
|
|
if strings.Contains(req.Login, "@") {
|
|
user, err = s.userRepository.GetUserByEmail(ctx, s.db, req.Login)
|
|
} else {
|
|
user, err = s.userRepository.GetUserByUsername(ctx, s.db, req.Login)
|
|
}
|
|
|
|
if err != nil {
|
|
return authDto.TokenResponse{}, dto.ErrUserNotFound
|
|
}
|
|
|
|
isValid := utils.CheckPasswordHash(req.Password, user.Password)
|
|
if !isValid {
|
|
fmt.Println("Password validation error:", err)
|
|
return authDto.TokenResponse{}, dto.ErrUserNotFound
|
|
}
|
|
roles := append([]entities.M_Role{}, user.Roles...)
|
|
|
|
if len(roles) == 0 {
|
|
return authDto.TokenResponse{}, errors.New("user has no roles assigned")
|
|
}
|
|
|
|
// DeleteByUserID
|
|
err = s.refreshTokenRepository.DeleteByUserID(ctx, s.db, user.ID.String())
|
|
if err != nil {
|
|
return authDto.TokenResponse{}, err
|
|
}
|
|
|
|
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("1 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,
|
|
// ClientID: refreshToken.ClientID,
|
|
// 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,
|
|
RefreshToken: req.RefreshToken, // atau kosongkan jika tidak ingin return refresh token sama sekali
|
|
|
|
}, nil
|
|
}
|