feat(user): Add username support for login and implement username checks in repository

This commit is contained in:
Habib Fatkhul Rohman 2025-10-28 22:29:56 +07:00
parent 62a188fcc6
commit 98bdfce83a
4 changed files with 74 additions and 30 deletions

View File

@ -53,7 +53,7 @@ func (r *refreshTokenRepository) FindByToken(ctx context.Context, tx *gorm.DB, t
var refreshToken entities.RefreshToken
if err := tx.WithContext(ctx).
Preload("User").
Preload("User.UserRoles.Role").
Preload("User.Roles").
Take(&refreshToken).Error; err != nil {
return entities.RefreshToken{}, err
}

View File

@ -130,7 +130,7 @@ type (
}
UserLoginRequest struct {
Email string `json:"email" form:"email" binding:"required"`
Login string `json:"login" form:"login" binding:"required"`
Password string `json:"password" form:"password" binding:"required"`
}

View File

@ -12,7 +12,9 @@ type (
Register(ctx context.Context, tx *gorm.DB, user entities.M_User) (entities.M_User, error)
GetUserById(ctx context.Context, tx *gorm.DB, userId string) (entities.M_User, error)
GetUserByEmail(ctx context.Context, tx *gorm.DB, email string) (entities.M_User, error)
GetUserByUsername(ctx context.Context, tx *gorm.DB, username string) (entities.M_User, error)
CheckEmail(ctx context.Context, tx *gorm.DB, email string) (entities.M_User, bool, error)
CheckUsername(ctx context.Context, tx *gorm.DB, username 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)
@ -23,6 +25,37 @@ type (
}
)
// CheckUsername implements UserRepository.
func (r *userRepository) CheckUsername(ctx context.Context, tx *gorm.DB, username string) (entities.M_User, bool, error) {
if tx == nil {
tx = r.db
}
var user entities.M_User
if err := tx.WithContext(ctx).Where("username = ?", username).Take(&user).Error; err != nil {
return entities.M_User{}, false, err
}
return user, true, nil
}
// GetUserByUsername implements UserRepository.
func (r *userRepository) GetUserByUsername(ctx context.Context, tx *gorm.DB, username string) (entities.M_User, error) {
if tx == nil {
tx = r.db
}
var user entities.M_User
if err := tx.WithContext(ctx).
Preload("Roles").
Where("username = ?", username).
Take(&user).Error; err != nil {
return entities.M_User{}, err
}
return user, nil
}
func NewUserRepository(db *gorm.DB) UserRepository {
return &userRepository{
db: db,

View File

@ -4,6 +4,7 @@ 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"
@ -124,9 +125,17 @@ func (s *userService) GetUserById(ctx context.Context, userId string) (dto.UserR
}
func (s *userService) Verify(ctx context.Context, req dto.UserLoginRequest) (authDto.TokenResponse, error) {
user, err := s.userRepository.GetUserByEmail(ctx, s.db, req.Email)
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.ErrEmailNotFound
return authDto.TokenResponse{}, dto.ErrUserNotFound
}
isValid := utils.CheckPasswordHash(req.Password, user.Password)
@ -134,19 +143,18 @@ 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")
}
// 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()
@ -292,31 +300,34 @@ func (s *userService) RefreshToken(ctx context.Context, req authDto.RefreshToken
}
roles := append([]entities.M_Role{}, refreshToken.User.Roles...)
if len(roles) == 0 {
return authDto.TokenResponse{}, errors.New("user has no roles assigned")
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()
// newRefreshTokenString, expiresAt := s.jwtService.GenerateRefreshToken()
err = s.refreshTokenRepository.DeleteByToken(ctx, s.db, req.RefreshToken)
if err != nil {
return authDto.TokenResponse{}, err
}
// 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,
}
// 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
}
// _, err = s.refreshTokenRepository.Create(ctx, s.db, newRefreshToken)
// if err != nil {
// return authDto.TokenResponse{}, err
// }
return authDto.TokenResponse{
AccessToken: accessToken,
RefreshToken: newRefreshTokenString,
// RefreshToken: newRefreshTokenString,
RefreshToken: req.RefreshToken, // atau kosongkan jika tidak ingin return refresh token sama sekali
}, nil
}