test wheels
This commit is contained in:
@@ -25,24 +25,20 @@ type CustomerAuthProcessor interface {
|
||||
}
|
||||
|
||||
type customerAuthProcessor struct {
|
||||
customerAuthRepo repository.CustomerAuthRepository
|
||||
otpProcessor OtpProcessor
|
||||
otpRepo repository.OtpRepository
|
||||
jwtSecret string
|
||||
tokenTTLMinutes int
|
||||
otpStorage map[string]*models.OtpSession // In-memory storage for OTP sessions
|
||||
registrationStorage map[string]*models.RegistrationSession // In-memory storage for registration sessions
|
||||
customerAuthRepo repository.CustomerAuthRepository
|
||||
otpProcessor OtpProcessor
|
||||
otpRepo repository.OtpRepository
|
||||
jwtSecret string
|
||||
tokenTTLMinutes int
|
||||
}
|
||||
|
||||
func NewCustomerAuthProcessor(customerAuthRepo repository.CustomerAuthRepository, otpProcessor OtpProcessor, otpRepo repository.OtpRepository, jwtSecret string, tokenTTLMinutes int) CustomerAuthProcessor {
|
||||
return &customerAuthProcessor{
|
||||
customerAuthRepo: customerAuthRepo,
|
||||
otpProcessor: otpProcessor,
|
||||
otpRepo: otpRepo,
|
||||
jwtSecret: jwtSecret,
|
||||
tokenTTLMinutes: tokenTTLMinutes,
|
||||
otpStorage: make(map[string]*models.OtpSession),
|
||||
registrationStorage: make(map[string]*models.RegistrationSession),
|
||||
customerAuthRepo: customerAuthRepo,
|
||||
otpProcessor: otpProcessor,
|
||||
otpRepo: otpRepo,
|
||||
jwtSecret: jwtSecret,
|
||||
tokenTTLMinutes: tokenTTLMinutes,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,17 +151,19 @@ func (p *customerAuthProcessor) StartRegistration(ctx context.Context, req *cont
|
||||
return nil, fmt.Errorf("failed to create OTP session: %w", err)
|
||||
}
|
||||
|
||||
// Store registration session
|
||||
registrationSession := &models.RegistrationSession{
|
||||
Token: registrationToken,
|
||||
PhoneNumber: req.PhoneNumber,
|
||||
Name: req.Name,
|
||||
BirthDate: req.BirthDate,
|
||||
ExpiresAt: time.Now().Add(10 * time.Minute),
|
||||
Step: "otp_sent",
|
||||
// Store registration data in OTP session metadata
|
||||
registrationData := map[string]interface{}{
|
||||
"registration_token": registrationToken,
|
||||
"name": req.Name,
|
||||
"birth_date": req.BirthDate,
|
||||
"step": "otp_sent",
|
||||
}
|
||||
|
||||
p.registrationStorage[registrationToken] = registrationSession
|
||||
// Update OTP session with registration metadata
|
||||
otpSession.Metadata = registrationData
|
||||
if err := p.otpRepo.UpdateOtpSession(ctx, otpSession); err != nil {
|
||||
return nil, fmt.Errorf("failed to update OTP session with registration data: %w", err)
|
||||
}
|
||||
|
||||
// Send OTP via WhatsApp
|
||||
if err := p.otpProcessor.SendOtpViaWhatsApp(req.PhoneNumber, otpSession.Code, "registration"); err != nil {
|
||||
@@ -184,18 +182,19 @@ func (p *customerAuthProcessor) StartRegistration(ctx context.Context, req *cont
|
||||
}
|
||||
|
||||
func (p *customerAuthProcessor) VerifyOtp(ctx context.Context, req *contract.RegisterVerifyOtpRequest) (*models.RegisterVerifyOtpResponse, error) {
|
||||
// Get registration session
|
||||
registrationSession, exists := p.registrationStorage[req.RegistrationToken]
|
||||
if !exists {
|
||||
otpSession, err := p.otpRepo.GetOtpSessionByRegistrationToken(ctx, req.RegistrationToken)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get OTP session: %w", err)
|
||||
}
|
||||
|
||||
if otpSession == nil {
|
||||
return nil, fmt.Errorf("invalid or expired registration token")
|
||||
}
|
||||
|
||||
if time.Now().After(registrationSession.ExpiresAt) {
|
||||
delete(p.registrationStorage, req.RegistrationToken)
|
||||
if otpSession.IsExpired() {
|
||||
return nil, fmt.Errorf("registration token expired")
|
||||
}
|
||||
|
||||
// Validate OTP format
|
||||
if !p.otpProcessor.ValidateOtpCode(req.OtpCode) {
|
||||
return &models.RegisterVerifyOtpResponse{
|
||||
Status: "FAILED",
|
||||
@@ -203,34 +202,33 @@ func (p *customerAuthProcessor) VerifyOtp(ctx context.Context, req *contract.Reg
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Get the OTP session for this phone number and purpose
|
||||
otpSession, err := p.otpRepo.GetOtpSessionByPhoneAndPurpose(ctx, registrationSession.PhoneNumber, "registration")
|
||||
if err != nil {
|
||||
return &models.RegisterVerifyOtpResponse{
|
||||
Status: "FAILED",
|
||||
Message: "Failed to validate OTP session.",
|
||||
}, nil
|
||||
}
|
||||
|
||||
if otpSession == nil {
|
||||
return &models.RegisterVerifyOtpResponse{
|
||||
Status: "FAILED",
|
||||
Message: "No active OTP session found.",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Verify OTP code and mark as used
|
||||
if otpSession.Code != req.OtpCode {
|
||||
otpSession.IncrementAttempts()
|
||||
p.otpRepo.UpdateOtpSession(ctx, otpSession)
|
||||
if err := p.otpRepo.UpdateOtpSession(ctx, otpSession); err != nil {
|
||||
fmt.Printf("Warning: failed to update OTP session attempts: %v\n", err)
|
||||
}
|
||||
return &models.RegisterVerifyOtpResponse{
|
||||
Status: "FAILED",
|
||||
Message: "Invalid OTP code.",
|
||||
}, nil
|
||||
}
|
||||
|
||||
if otpSession.IsUsed || otpSession.IsMaxAttemptsReached() {
|
||||
return &models.RegisterVerifyOtpResponse{
|
||||
Status: "FAILED",
|
||||
Message: "OTP code already used or max attempts reached.",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Mark OTP as used
|
||||
otpSession.MarkAsUsed()
|
||||
|
||||
// Update registration step in metadata
|
||||
if otpSession.Metadata == nil {
|
||||
otpSession.Metadata = make(map[string]interface{})
|
||||
}
|
||||
otpSession.Metadata["step"] = "otp_verified"
|
||||
|
||||
if err := p.otpRepo.UpdateOtpSession(ctx, otpSession); err != nil {
|
||||
return &models.RegisterVerifyOtpResponse{
|
||||
Status: "FAILED",
|
||||
@@ -238,10 +236,6 @@ func (p *customerAuthProcessor) VerifyOtp(ctx context.Context, req *contract.Reg
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Update registration session
|
||||
registrationSession.Step = "otp_verified"
|
||||
p.registrationStorage[req.RegistrationToken] = registrationSession
|
||||
|
||||
return &models.RegisterVerifyOtpResponse{
|
||||
Status: "OTP_VERIFIED",
|
||||
Message: "OTP verified, continue to set password.",
|
||||
@@ -252,23 +246,26 @@ func (p *customerAuthProcessor) VerifyOtp(ctx context.Context, req *contract.Reg
|
||||
}
|
||||
|
||||
func (p *customerAuthProcessor) SetPassword(ctx context.Context, req *contract.RegisterSetPasswordRequest) (*models.RegisterSetPasswordResponse, error) {
|
||||
// Validate passwords match
|
||||
if req.Password != req.ConfirmPassword {
|
||||
return nil, fmt.Errorf("passwords do not match")
|
||||
}
|
||||
|
||||
// Get registration session
|
||||
registrationSession, exists := p.registrationStorage[req.RegistrationToken]
|
||||
if !exists {
|
||||
// Get OTP session by registration token from metadata
|
||||
otpSession, err := p.otpRepo.GetOtpSessionByRegistrationToken(ctx, req.RegistrationToken)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get OTP session: %w", err)
|
||||
}
|
||||
|
||||
if otpSession == nil {
|
||||
return nil, fmt.Errorf("invalid or expired registration token")
|
||||
}
|
||||
|
||||
if time.Now().After(registrationSession.ExpiresAt) {
|
||||
delete(p.registrationStorage, req.RegistrationToken)
|
||||
if otpSession.IsExpired() {
|
||||
return nil, fmt.Errorf("registration token expired")
|
||||
}
|
||||
|
||||
if registrationSession.Step != "otp_verified" {
|
||||
step, ok := otpSession.Metadata["step"].(string)
|
||||
if !ok || step != "otp_verified" {
|
||||
return nil, fmt.Errorf("OTP verification required before setting password")
|
||||
}
|
||||
|
||||
@@ -280,31 +277,38 @@ func (p *customerAuthProcessor) SetPassword(ctx context.Context, req *contract.R
|
||||
|
||||
passwordHashStr := string(passwordHash)
|
||||
|
||||
// Extract registration data from OTP session metadata
|
||||
name, ok := otpSession.Metadata["name"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid registration data: name not found")
|
||||
}
|
||||
|
||||
birthDateStr, ok := otpSession.Metadata["birth_date"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid registration data: birth_date not found")
|
||||
}
|
||||
|
||||
// Parse birth date
|
||||
birthDate, err := time.Parse("2006-01-02", registrationSession.BirthDate)
|
||||
birthDate, err := time.Parse("2006-01-02", birthDateStr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid birth date format: %w", err)
|
||||
}
|
||||
|
||||
// Create customer
|
||||
defaultOrgID := uuid.MustParse("87bec7c1-e274-4f66-bac5-84e632208470") // This should be configurable
|
||||
|
||||
customer := &entities.Customer{
|
||||
Name: registrationSession.Name,
|
||||
PhoneNumber: ®istrationSession.PhoneNumber,
|
||||
BirthDate: &birthDate,
|
||||
PasswordHash: &passwordHashStr,
|
||||
IsActive: true,
|
||||
// Note: OrganizationID should be set based on your business logic
|
||||
// For now, we'll use a default organization or require it in the request
|
||||
OrganizationID: defaultOrgID,
|
||||
Name: name,
|
||||
PhoneNumber: &otpSession.PhoneNumber,
|
||||
BirthDate: &birthDate,
|
||||
PasswordHash: &passwordHashStr,
|
||||
IsActive: true,
|
||||
}
|
||||
|
||||
if err := p.customerAuthRepo.CreateCustomer(ctx, customer); err != nil {
|
||||
return nil, fmt.Errorf("failed to create customer: %w", err)
|
||||
}
|
||||
|
||||
// Clean up registration session
|
||||
delete(p.registrationStorage, req.RegistrationToken)
|
||||
|
||||
// Generate JWT tokens using customer JWT util
|
||||
accessToken, refreshToken, _, err := util.GenerateCustomerTokens(customer, p.jwtSecret, p.tokenTTLMinutes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate tokens: %w", err)
|
||||
@@ -320,7 +324,7 @@ func (p *customerAuthProcessor) SetPassword(ctx context.Context, req *contract.R
|
||||
ID: customer.ID,
|
||||
Name: customer.Name,
|
||||
PhoneNumber: *customer.PhoneNumber,
|
||||
BirthDate: registrationSession.BirthDate,
|
||||
BirthDate: birthDate.Format("2006-01-02"),
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
|
||||
Reference in New Issue
Block a user