Yimaru-BackEnd/internal/services/user/register.go

130 lines
3.5 KiB
Go

package user
import (
"Yimaru-Backend/internal/domain"
"context"
"time"
)
func (s *Service) VerifyOtp(ctx context.Context, userName string, otpCode string) error {
// 1. Retrieve the OTP from the store
storedOtp, err := s.otpStore.GetOtp(ctx, userName)
if err != nil {
return err // could be ErrOtpNotFound or other DB errors
}
// 2. Check if OTP was already used
if storedOtp.Used {
return domain.ErrOtpAlreadyUsed
}
// 3. Check if OTP has expired
if time.Now().After(storedOtp.ExpiresAt) {
return domain.ErrOtpExpired
}
// 4. Check if the provided OTP matches
if storedOtp.Otp != otpCode {
return domain.ErrInvalidOtp
}
// 5. Mark OTP as used
storedOtp.Used = true
storedOtp.UsedAt = timePtr(time.Now())
if err := s.otpStore.MarkOtpAsUsed(ctx, storedOtp); err != nil {
return err
}
return nil
}
func (s *Service) CheckPhoneEmailExist(ctx context.Context, phoneNum, email string) (bool, bool, error) { // email,phone,error
return s.userStore.CheckPhoneEmailExist(ctx, phoneNum, email)
}
func (s *Service) SendRegisterCode(ctx context.Context, medium domain.OtpMedium, sentTo string, provider domain.SMSProvider) error {
var err error
// check if user exists
switch medium {
case domain.OtpMediumEmail:
_, err = s.userStore.GetUserByEmailPhone(ctx, sentTo, "")
case domain.OtpMediumSms:
_, err = s.userStore.GetUserByEmailPhone(ctx, "", sentTo)
}
if err != nil && err != domain.ErrUserNotFound {
return err
}
// send otp based on the medium
return s.SendOtp(ctx, sentTo, domain.OtpRegister, medium, provider)
}
func (s *Service) RegisterUser(ctx context.Context, registerReq domain.RegisterUserReq) (domain.User, error) {
// Check if the email or phone is already registered based on OTP medium
phoneExists, emailExists, err := s.userStore.CheckPhoneEmailExist(ctx, registerReq.PhoneNumber, registerReq.Email)
if err != nil {
return domain.User{}, err
}
if registerReq.OtpMedium == domain.OtpMediumEmail {
if emailExists {
return domain.User{}, domain.ErrEmailAlreadyRegistered
}
} else {
if phoneExists {
return domain.User{}, domain.ErrPhoneAlreadyRegistered
}
}
// Hash the password
hashedPassword, err := hashPassword(registerReq.Password)
if err != nil {
return domain.User{}, err
}
// Prepare the user
userR := domain.User{
FirstName: registerReq.FirstName,
LastName: registerReq.LastName,
UserName: registerReq.UserName,
Email: registerReq.Email,
PhoneNumber: registerReq.PhoneNumber,
Password: hashedPassword,
Role: domain.RoleStudent,
EmailVerified: false, // verification pending via OTP
PhoneVerified: false,
EducationLevel: registerReq.EducationLevel,
Age: registerReq.Age,
Country: registerReq.Country,
Region: registerReq.Region,
Status: domain.UserStatusPending,
ProfileCompleted: false,
PreferredLanguage: registerReq.PreferredLanguage,
CreatedAt: time.Now(),
}
var sentTo string
// var provider domain.Provid
if registerReq.OtpMedium == domain.OtpMediumEmail {
sentTo = registerReq.Email
} else {
sentTo = registerReq.PhoneNumber
}
// Send OTP to the user (email/SMS)
if err := s.SendOtp(ctx, sentTo, domain.OtpRegister, registerReq.OtpMedium, domain.TwilioSms); err != nil {
return domain.User{}, err
}
// Create the user (no OTP validation yet)
user, err := s.userStore.CreateUserWithoutOtp(ctx, userR)
if err != nil {
return domain.User{}, err
}
return user, nil
}