MIF_E31222379_BE/internal/services/auth_service.go

178 lines
3.8 KiB
Go

package services
import (
"errors"
"fmt"
"math/rand"
"rijig/config"
"rijig/dto"
"rijig/internal/repositories"
"rijig/model"
"rijig/utils"
"time"
"github.com/golang-jwt/jwt/v5"
"github.com/google/uuid"
)
type AuthService interface {
RegisterUser(req *dto.RegisterRequest) error
VerifyOTP(req *dto.VerifyOTPRequest) (*dto.UserDataResponse, error)
}
type authService struct {
userRepo repositories.UserRepository
roleRepo repositories.RoleRepository
}
func NewAuthService(userRepo repositories.UserRepository, roleRepo repositories.RoleRepository) AuthService {
return &authService{userRepo, roleRepo}
}
const otpCooldown = 30
func (s *authService) RegisterUser(req *dto.RegisterRequest) error {
user, err := s.userRepo.GetUserByPhone(req.Phone)
if err == nil && user != nil {
return errors.New("phone number already registered")
}
lastOtpSent, err := utils.GetStringData("otp_sent:" + req.Phone)
if err == nil && lastOtpSent != "" {
lastSentTime, err := time.Parse(time.RFC3339, lastOtpSent)
if err != nil {
return errors.New("invalid OTP sent timestamp")
}
if time.Since(lastSentTime).Seconds() < otpCooldown {
return errors.New("please wait before requesting another OTP")
}
}
userID := uuid.New().String()
user = &model.User{
Phone: req.Phone,
RoleID: req.RoleID,
}
err = utils.SetJSONData("user:"+userID, user, 10*time.Minute)
if err != nil {
return err
}
err = utils.SetStringData("user_phone:"+req.Phone, userID, 10*time.Minute)
if err != nil {
return err
}
otp := generateOTP()
err = config.SendWhatsAppMessage(req.Phone, fmt.Sprintf("Your OTP is: %s", otp))
if err != nil {
return err
}
err = utils.SetStringData("otp:"+req.Phone, otp, 10*time.Minute)
if err != nil {
return err
}
err = utils.SetStringData("otp_sent:"+req.Phone, time.Now().Format(time.RFC3339), 10*time.Minute)
if err != nil {
return err
}
return nil
}
func (s *authService) VerifyOTP(req *dto.VerifyOTPRequest) (*dto.UserDataResponse, error) {
isLoggedIn, err := utils.GetStringData("user_logged_in:" + req.Phone)
if err == nil && isLoggedIn == "true" {
return nil, errors.New("you are already logged in")
}
storedOTP, err := utils.GetStringData("otp:" + req.Phone)
if err != nil {
return nil, err
}
if storedOTP == "" {
return nil, errors.New("OTP expired or not found")
}
if storedOTP != req.OTP {
return nil, errors.New("invalid OTP")
}
userID, err := utils.GetStringData("user_phone:" + req.Phone)
if err != nil || userID == "" {
return nil, errors.New("user data not found in Redis")
}
userData, err := utils.GetJSONData("user:" + userID)
if err != nil || userData == nil {
return nil, errors.New("user data not found in Redis")
}
user := &model.User{
Phone: userData["phone"].(string),
RoleID: userData["roleId"].(string),
}
createdUser, err := s.userRepo.CreateUser(user)
if err != nil {
return nil, err
}
role, err := s.roleRepo.FindByID(createdUser.RoleID)
if err != nil {
return nil, err
}
token, err := generateJWTToken(createdUser.ID)
if err != nil {
return nil, err
}
err = utils.SetStringData("user_logged_in:"+req.Phone, "true", 0)
if err != nil {
return nil, err
}
return &dto.UserDataResponse{
UserID: createdUser.ID,
UserRole: role.RoleName,
Token: token,
}, nil
}
func generateOTP() string {
rand.Seed(time.Now().UnixNano())
otp := fmt.Sprintf("%06d", rand.Intn(1000000))
return otp
}
func generateJWTToken(userID string) (string, error) {
expirationTime := time.Now().Add(24 * time.Hour)
claims := &jwt.RegisteredClaims{
Issuer: userID,
ExpiresAt: jwt.NewNumericDate(expirationTime),
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
secretKey := config.GetSecretKey()
signedToken, err := token.SignedString([]byte(secretKey))
if err != nil {
return "", err
}
return signedToken, nil
}