135 lines
3.3 KiB
Go
135 lines
3.3 KiB
Go
package services
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/go-redis/redis/v8"
|
|
"github.com/golang-jwt/jwt/v5"
|
|
"github.com/pahmiudahgede/senggoldong/config"
|
|
"github.com/pahmiudahgede/senggoldong/dto"
|
|
"github.com/pahmiudahgede/senggoldong/internal/repositories"
|
|
"github.com/pahmiudahgede/senggoldong/model"
|
|
)
|
|
|
|
type AuthService interface {
|
|
RegisterUser(request dto.RegisterRequest) (*model.User, error)
|
|
VerifyOTP(phone, otp string) error
|
|
GetUserByPhone(phone string) (*model.User, error)
|
|
GenerateJWT(user *model.User) (string, error)
|
|
}
|
|
|
|
type authService struct {
|
|
UserRepo repositories.UserRepository
|
|
}
|
|
|
|
func NewAuthService(userRepo repositories.UserRepository) AuthService {
|
|
return &authService{UserRepo: userRepo}
|
|
}
|
|
|
|
func (s *authService) RegisterUser(request dto.RegisterRequest) (*model.User, error) {
|
|
|
|
user, err := s.UserRepo.FindByPhone(request.Phone)
|
|
if err == nil && user != nil {
|
|
return nil, fmt.Errorf("user with phone %s already exists", request.Phone)
|
|
}
|
|
|
|
user = &model.User{
|
|
Phone: request.Phone,
|
|
RoleID: request.RoleID,
|
|
EmailVerified: false,
|
|
}
|
|
|
|
err = s.UserRepo.CreateUser(user)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create user: %v", err)
|
|
}
|
|
|
|
_, err = s.SendOTP(request.Phone)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to send OTP: %v", err)
|
|
}
|
|
|
|
return user, nil
|
|
}
|
|
|
|
func (s *authService) GetUserByPhone(phone string) (*model.User, error) {
|
|
user, err := s.UserRepo.FindByPhone(phone)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error retrieving user by phone: %v", err)
|
|
}
|
|
if user == nil {
|
|
return nil, fmt.Errorf("user not found")
|
|
}
|
|
return user, nil
|
|
}
|
|
|
|
func (s *authService) SendOTP(phone string) (string, error) {
|
|
otpCode := generateOTP()
|
|
|
|
message := fmt.Sprintf("Your OTP code is: %s", otpCode)
|
|
err := config.SendWhatsAppMessage(phone, message)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to send OTP via WhatsApp: %v", err)
|
|
}
|
|
|
|
expirationTime := 5 * time.Minute
|
|
err = config.RedisClient.Set(config.Ctx, phone, otpCode, expirationTime).Err()
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to store OTP in Redis: %v", err)
|
|
}
|
|
|
|
return otpCode, nil
|
|
}
|
|
|
|
func (s *authService) VerifyOTP(phone, otp string) error {
|
|
|
|
otpRecord, err := config.RedisClient.Get(config.Ctx, phone).Result()
|
|
if err == redis.Nil {
|
|
|
|
return fmt.Errorf("OTP not found or expired")
|
|
} else if err != nil {
|
|
|
|
return fmt.Errorf("failed to retrieve OTP from Redis: %v", err)
|
|
}
|
|
|
|
if otp != otpRecord {
|
|
return fmt.Errorf("invalid OTP")
|
|
}
|
|
|
|
err = config.RedisClient.Del(config.Ctx, phone).Err()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to delete OTP from Redis: %v", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *authService) GenerateJWT(user *model.User) (string, error) {
|
|
if user == nil || user.Role == nil {
|
|
return "", fmt.Errorf("user or user role is nil, cannot generate token")
|
|
}
|
|
|
|
claims := jwt.MapClaims{
|
|
"sub": user.ID,
|
|
"role": user.Role.RoleName,
|
|
"iat": time.Now().Unix(),
|
|
"exp": time.Now().Add(time.Hour * 24).Unix(),
|
|
}
|
|
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
|
|
secretKey := config.GetSecretKey()
|
|
|
|
tokenString, err := token.SignedString([]byte(secretKey))
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to generate JWT token: %v", err)
|
|
}
|
|
|
|
return tokenString, nil
|
|
}
|
|
|
|
func generateOTP() string {
|
|
return fmt.Sprintf("%06d", time.Now().UnixNano()%1000000)
|
|
}
|