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 }