package validation import ( "net/mail" "regexp" "strings" "unicode" "git.techease.ru/Smart-search/smart-search-back/pkg/errors" ) const ( MinPasswordLength = 8 MaxPasswordLength = 128 MaxEmailLength = 254 MaxNameLength = 100 MaxPhoneLength = 20 MaxRequestTxtLen = 50000 MaxFileSizeBytes = 10 * 1024 * 1024 ) var ( phoneRegex = regexp.MustCompile(`^\+?[1-9]\d{6,14}$`) ) func ValidateEmail(email string) error { if email == "" { return errors.NewBusinessError(errors.ValidationInvalidEmail, "email is required") } if len(email) > MaxEmailLength { return errors.NewBusinessError(errors.ValidationInvalidEmail, "email is too long") } _, err := mail.ParseAddress(email) if err != nil { return errors.NewBusinessError(errors.ValidationInvalidEmail, "invalid email format") } parts := strings.Split(email, "@") if len(parts) != 2 || parts[0] == "" || parts[1] == "" { return errors.NewBusinessError(errors.ValidationInvalidEmail, "invalid email format") } if strings.Contains(parts[1], "..") { return errors.NewBusinessError(errors.ValidationInvalidEmail, "invalid email format") } return nil } func ValidatePassword(password string) error { if password == "" { return errors.NewBusinessError(errors.ValidationInvalidPassword, "password is required") } if len(password) < MinPasswordLength { return errors.NewBusinessError(errors.ValidationInvalidPassword, "password must be at least 8 characters") } if len(password) > MaxPasswordLength { return errors.NewBusinessError(errors.ValidationInvalidPassword, "password is too long") } var hasUpper, hasLower, hasDigit bool for _, c := range password { switch { case unicode.IsUpper(c): hasUpper = true case unicode.IsLower(c): hasLower = true case unicode.IsDigit(c): hasDigit = true } } if !hasUpper || !hasLower || !hasDigit { return errors.NewBusinessError(errors.ValidationInvalidPassword, "password must contain uppercase, lowercase and digit") } return nil } func ValidatePhone(phone string) error { if phone == "" { return nil } if len(phone) > MaxPhoneLength { return errors.NewBusinessError(errors.ValidationInvalidPhone, "phone is too long") } cleaned := strings.ReplaceAll(phone, " ", "") cleaned = strings.ReplaceAll(cleaned, "-", "") cleaned = strings.ReplaceAll(cleaned, "(", "") cleaned = strings.ReplaceAll(cleaned, ")", "") if !phoneRegex.MatchString(cleaned) { return errors.NewBusinessError(errors.ValidationInvalidPhone, "invalid phone format") } return nil } func ValidateName(name string) error { if name == "" { return errors.NewBusinessError(errors.ValidationInvalidName, "name is required") } if len(name) > MaxNameLength { return errors.NewBusinessError(errors.ValidationInvalidName, "name is too long") } trimmed := strings.TrimSpace(name) if trimmed == "" { return errors.NewBusinessError(errors.ValidationInvalidName, "name cannot be only whitespace") } return nil } func ValidateRequestTxt(txt string) error { if len(txt) > MaxRequestTxtLen { return errors.NewBusinessError(errors.ValidationRequestTooLong, "request text exceeds 50000 characters limit") } return nil } func ValidateFileSize(size int) error { if size > MaxFileSizeBytes { return errors.NewBusinessError(errors.ValidationFileTooLarge, "file size exceeds 10MB limit") } return nil } func ValidateRegistration(email, password, name, phone string) error { if err := ValidateEmail(email); err != nil { return err } if err := ValidatePassword(password); err != nil { return err } if err := ValidateName(name); err != nil { return err } if err := ValidatePhone(phone); err != nil { return err } return nil }