package service import ( "context" "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) } 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, } } 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 } 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, }, 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 } accessToken := s.jwtService.GenerateAccessToken(user.ClientID.String(), user.ID.String()) refreshTokenString, expiresAt := s.jwtService.GenerateRefreshToken() refreshToken := entities.RefreshToken{ ID: uuid.New(), UserID: user.ID, // TenantID: user.TenantID, 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 // } verificationToken := s.jwtService.GenerateAccessToken(user.ClientID.String(), user.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 } accessToken := s.jwtService.GenerateAccessToken(refreshToken.ClientID.String(), refreshToken.UserID.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 }