veza/veza-backend-api/internal/validators/email_validator.go

121 lines
2.7 KiB
Go
Raw Normal View History

2025-12-03 19:29:37 +00:00
package validators
import (
"errors"
"regexp"
"strings"
"veza-backend-api/internal/models"
2026-03-05 22:03:43 +00:00
"gorm.io/gorm"
2025-12-03 19:29:37 +00:00
)
// 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
}