155 lines
4.1 KiB
Go
155 lines
4.1 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
|
|
}
|
|
|
|
user, err := s.userStore.GetUserByUserName(ctx, userName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
newUser := domain.UpdateUserReq{
|
|
UserID: user.ID,
|
|
Status: domain.ValidString{
|
|
Value: string(domain.UserStatusActive),
|
|
Valid: true,
|
|
},
|
|
}
|
|
|
|
s.userStore.UpdateUserStatus(ctx, newUser)
|
|
|
|
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,
|
|
PhoneVerified: false,
|
|
EducationLevel: registerReq.EducationLevel,
|
|
Age: registerReq.Age,
|
|
Country: registerReq.Country,
|
|
Region: registerReq.Region,
|
|
Status: domain.UserStatusPending,
|
|
ProfileCompleted: false,
|
|
PreferredLanguage: registerReq.PreferredLanguage,
|
|
|
|
// Optional fields
|
|
NickName: registerReq.NickName,
|
|
Occupation: registerReq.Occupation,
|
|
LearningGoal: registerReq.LearningGoal,
|
|
LanguageGoal: registerReq.LanguageGoal,
|
|
LanguageChallange: registerReq.LanguageChallange,
|
|
FavoutiteTopic: registerReq.FavoutiteTopic,
|
|
// ProfilePictureURL: registerReq.ProfilePictureURL,
|
|
|
|
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, registerReq.UserName, 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
|
|
}
|