package validators import ( "errors" "regexp" "strings" "gorm.io/gorm" "veza-backend-api/internal/models" ) // RFC 5322 compliant email regex (simplified but covers most cases) // This regex validates: // - Local part: alphanumeric, dots, underscores, hyphens, plus signs // - @ symbol // - Domain part: alphanumeric, dots, hyphens // - TLD: at least 2 characters var emailRegex = regexp.MustCompile(`^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$`) // EmailValidator valide les emails selon RFC 5322 type EmailValidator struct { db *gorm.DB } // NewEmailValidator crée une nouvelle instance d'EmailValidator func NewEmailValidator(db *gorm.DB) *EmailValidator { return &EmailValidator{db: db} } // ValidateFormat valide le format de l'email selon RFC 5322 func (v *EmailValidator) ValidateFormat(email string) bool { email = strings.ToLower(strings.TrimSpace(email)) // RFC 5321: Email addresses are limited to 254 characters if len(email) > 254 { return false } // Vérifier que l'email n'est pas vide if len(email) == 0 { return false } // Vérifier le format avec regex if !emailRegex.MatchString(email) { return false } // Vérifications supplémentaires parts := strings.Split(email, "@") if len(parts) != 2 { return false } localPart := parts[0] domainPart := parts[1] // Local part ne peut pas être vide if len(localPart) == 0 || len(localPart) > 64 { return false } // Domain part ne peut pas être vide if len(domainPart) == 0 || len(domainPart) > 253 { return false } // Le domaine doit contenir au moins un point if !strings.Contains(domainPart, ".") { return false } // Le local part ne peut pas commencer ou finir par un point if strings.HasPrefix(localPart, ".") || strings.HasSuffix(localPart, ".") { return false } // Le domaine ne peut pas commencer ou finir par un point ou un tiret if strings.HasPrefix(domainPart, ".") || strings.HasSuffix(domainPart, ".") || strings.HasPrefix(domainPart, "-") || strings.HasSuffix(domainPart, "-") { return false } return true } // IsUnique vérifie si l'email est unique en base de données func (v *EmailValidator) IsUnique(email string) (bool, error) { email = strings.ToLower(strings.TrimSpace(email)) var count int64 err := v.db.Model(&models.User{}). Where("LOWER(email) = ?", email). Count(&count).Error if err != nil { return false, err } return count == 0, nil } // Validate effectue une validation complète de l'email (format + unicité) func (v *EmailValidator) Validate(email string) error { if !v.ValidateFormat(email) { return errors.New("invalid email format") } unique, err := v.IsUnique(email) if err != nil { return err } if !unique { return errors.New("email already exists") } return nil }