237 lines
7.2 KiB
Go
237 lines
7.2 KiB
Go
package validator
|
|
|
|
import (
|
|
"errors"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"apskel-pos-be/internal/constants"
|
|
"apskel-pos-be/internal/contract"
|
|
)
|
|
|
|
type CustomerAuthValidator interface {
|
|
ValidateCheckPhoneRequest(req *contract.CheckPhoneRequest) (error, string)
|
|
ValidateRegisterStartRequest(req *contract.RegisterStartRequest) (error, string)
|
|
ValidateRegisterVerifyOtpRequest(req *contract.RegisterVerifyOtpRequest) (error, string)
|
|
ValidateRegisterSetPasswordRequest(req *contract.RegisterSetPasswordRequest) (error, string)
|
|
ValidateCustomerLoginRequest(req *contract.CustomerLoginRequest) (error, string)
|
|
ValidateResendOtpRequest(req *contract.ResendOtpRequest) (error, string)
|
|
}
|
|
|
|
type CustomerAuthValidatorImpl struct{}
|
|
|
|
func NewCustomerAuthValidator() CustomerAuthValidator {
|
|
return &CustomerAuthValidatorImpl{}
|
|
}
|
|
|
|
func (v *CustomerAuthValidatorImpl) ValidateCheckPhoneRequest(req *contract.CheckPhoneRequest) (error, string) {
|
|
if req == nil {
|
|
return errors.New("request is required"), constants.ValidationErrorCode
|
|
}
|
|
|
|
// Validate phone number
|
|
if strings.TrimSpace(req.PhoneNumber) == "" {
|
|
return errors.New("phone number is required"), constants.ValidationErrorCode
|
|
}
|
|
|
|
if !v.isValidPhoneNumber(req.PhoneNumber) {
|
|
return errors.New("invalid phone number format"), constants.ValidationErrorCode
|
|
}
|
|
|
|
return nil, ""
|
|
}
|
|
|
|
func (v *CustomerAuthValidatorImpl) ValidateRegisterStartRequest(req *contract.RegisterStartRequest) (error, string) {
|
|
if req == nil {
|
|
return errors.New("request is required"), constants.ValidationErrorCode
|
|
}
|
|
|
|
// Validate phone number
|
|
if strings.TrimSpace(req.PhoneNumber) == "" {
|
|
return errors.New("phone number is required"), constants.ValidationErrorCode
|
|
}
|
|
|
|
if !v.isValidPhoneNumber(req.PhoneNumber) {
|
|
return errors.New("invalid phone number format"), constants.ValidationErrorCode
|
|
}
|
|
|
|
// Validate name
|
|
if strings.TrimSpace(req.Name) == "" {
|
|
return errors.New("name is required"), constants.ValidationErrorCode
|
|
}
|
|
|
|
if len(req.Name) < 2 {
|
|
return errors.New("name must be at least 2 characters long"), constants.ValidationErrorCode
|
|
}
|
|
|
|
if len(req.Name) > 100 {
|
|
return errors.New("name cannot exceed 100 characters"), constants.ValidationErrorCode
|
|
}
|
|
|
|
// Validate birth date
|
|
if strings.TrimSpace(req.BirthDate) == "" {
|
|
return errors.New("birth date is required"), constants.ValidationErrorCode
|
|
}
|
|
|
|
if !v.isValidDateFormat(req.BirthDate) {
|
|
return errors.New("invalid birth date format (YYYY-MM-DD)"), constants.ValidationErrorCode
|
|
}
|
|
|
|
return nil, ""
|
|
}
|
|
|
|
func (v *CustomerAuthValidatorImpl) ValidateRegisterVerifyOtpRequest(req *contract.RegisterVerifyOtpRequest) (error, string) {
|
|
if req == nil {
|
|
return errors.New("request is required"), constants.ValidationErrorCode
|
|
}
|
|
|
|
// Validate registration token
|
|
if strings.TrimSpace(req.RegistrationToken) == "" {
|
|
return errors.New("registration token is required"), constants.ValidationErrorCode
|
|
}
|
|
|
|
// Validate OTP code
|
|
if strings.TrimSpace(req.OtpCode) == "" {
|
|
return errors.New("OTP code is required"), constants.ValidationErrorCode
|
|
}
|
|
|
|
if !v.isValidOtpCode(req.OtpCode) {
|
|
return errors.New("invalid OTP code format"), constants.ValidationErrorCode
|
|
}
|
|
|
|
return nil, ""
|
|
}
|
|
|
|
func (v *CustomerAuthValidatorImpl) ValidateRegisterSetPasswordRequest(req *contract.RegisterSetPasswordRequest) (error, string) {
|
|
if req == nil {
|
|
return errors.New("request is required"), constants.ValidationErrorCode
|
|
}
|
|
|
|
// Validate registration token
|
|
if strings.TrimSpace(req.RegistrationToken) == "" {
|
|
return errors.New("registration token is required"), constants.ValidationErrorCode
|
|
}
|
|
|
|
// Validate password
|
|
if strings.TrimSpace(req.Password) == "" {
|
|
return errors.New("password is required"), constants.ValidationErrorCode
|
|
}
|
|
|
|
if len(req.Password) < 8 {
|
|
return errors.New("password must be at least 8 characters long"), constants.ValidationErrorCode
|
|
}
|
|
|
|
if len(req.Password) > 128 {
|
|
return errors.New("password cannot exceed 128 characters"), constants.ValidationErrorCode
|
|
}
|
|
|
|
// Validate confirm password
|
|
if strings.TrimSpace(req.ConfirmPassword) == "" {
|
|
return errors.New("confirm password is required"), constants.ValidationErrorCode
|
|
}
|
|
|
|
if req.Password != req.ConfirmPassword {
|
|
return errors.New("passwords do not match"), constants.ValidationErrorCode
|
|
}
|
|
|
|
// Validate password strength
|
|
if !v.isStrongPassword(req.Password) {
|
|
return errors.New("password must contain at least one uppercase letter, one lowercase letter, and one number"), constants.ValidationErrorCode
|
|
}
|
|
|
|
return nil, ""
|
|
}
|
|
|
|
func (v *CustomerAuthValidatorImpl) ValidateCustomerLoginRequest(req *contract.CustomerLoginRequest) (error, string) {
|
|
if req == nil {
|
|
return errors.New("request is required"), constants.ValidationErrorCode
|
|
}
|
|
|
|
// Validate phone number
|
|
if strings.TrimSpace(req.PhoneNumber) == "" {
|
|
return errors.New("phone number is required"), constants.ValidationErrorCode
|
|
}
|
|
|
|
if !v.isValidPhoneNumber(req.PhoneNumber) {
|
|
return errors.New("invalid phone number format"), constants.ValidationErrorCode
|
|
}
|
|
|
|
// Validate password
|
|
if strings.TrimSpace(req.Password) == "" {
|
|
return errors.New("password is required"), constants.ValidationErrorCode
|
|
}
|
|
|
|
return nil, ""
|
|
}
|
|
|
|
// Helper validation functions
|
|
func (v *CustomerAuthValidatorImpl) isValidPhoneNumber(phoneNumber string) bool {
|
|
// Basic phone number validation - adjust regex based on your requirements
|
|
phoneRegex := regexp.MustCompile(`^\+?[1-9]\d{1,14}$`)
|
|
return phoneRegex.MatchString(phoneNumber)
|
|
}
|
|
|
|
func (v *CustomerAuthValidatorImpl) isValidDateFormat(date string) bool {
|
|
// Basic date format validation for YYYY-MM-DD
|
|
dateRegex := regexp.MustCompile(`^\d{4}-\d{2}-\d{2}$`)
|
|
if !dateRegex.MatchString(date) {
|
|
return false
|
|
}
|
|
|
|
// You can add more sophisticated date validation here if needed
|
|
return true
|
|
}
|
|
|
|
func (v *CustomerAuthValidatorImpl) isValidOtpCode(code string) bool {
|
|
// OTP code should be 4-8 digits
|
|
otpRegex := regexp.MustCompile(`^\d{4,8}$`)
|
|
return otpRegex.MatchString(code)
|
|
}
|
|
|
|
func (v *CustomerAuthValidatorImpl) ValidateResendOtpRequest(req *contract.ResendOtpRequest) (error, string) {
|
|
if req == nil {
|
|
return errors.New("request is required"), constants.CustomerEntity
|
|
}
|
|
|
|
// Validate phone number
|
|
if req.PhoneNumber == "" {
|
|
return errors.New("phone number is required"), constants.CustomerEntity
|
|
}
|
|
|
|
// Validate phone number format
|
|
if !v.isValidPhoneNumber(req.PhoneNumber) {
|
|
return errors.New("invalid phone number format"), constants.CustomerEntity
|
|
}
|
|
|
|
// Validate purpose
|
|
if req.Purpose == "" {
|
|
return errors.New("purpose is required"), constants.CustomerEntity
|
|
}
|
|
|
|
// Validate purpose values
|
|
validPurposes := []string{"login", "registration"}
|
|
if !v.contains(validPurposes, req.Purpose) {
|
|
return errors.New("purpose must be either 'login' or 'registration'"), constants.CustomerEntity
|
|
}
|
|
|
|
return nil, ""
|
|
}
|
|
|
|
func (v *CustomerAuthValidatorImpl) isStrongPassword(password string) bool {
|
|
// Password must contain at least one uppercase, one lowercase, and one number
|
|
hasUpper := regexp.MustCompile(`[A-Z]`).MatchString(password)
|
|
hasLower := regexp.MustCompile(`[a-z]`).MatchString(password)
|
|
hasNumber := regexp.MustCompile(`[0-9]`).MatchString(password)
|
|
|
|
return hasUpper && hasLower && hasNumber
|
|
}
|
|
|
|
func (v *CustomerAuthValidatorImpl) contains(slice []string, item string) bool {
|
|
for _, s := range slice {
|
|
if s == item {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|