Require email at registration (editable on profile), add a Resend mailer with idempotent first-answer sends, and keep answer saves independent of delivery.
53 lines
1.3 KiB
Go
53 lines
1.3 KiB
Go
package store
|
|
|
|
import (
|
|
"fmt"
|
|
"net/mail"
|
|
"strings"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
const (
|
|
minEmailLen = 3
|
|
maxEmailLen = 254
|
|
)
|
|
|
|
// NormalizeEmail trims and lowercases an address for storage/comparison.
|
|
func NormalizeEmail(s string) string {
|
|
return strings.ToLower(strings.TrimSpace(s))
|
|
}
|
|
|
|
// ValidateEmail returns a normalized address or an error message suitable for UI.
|
|
func ValidateEmail(raw string) (normalized string, errMsg string) {
|
|
normalized = NormalizeEmail(raw)
|
|
if normalized == "" {
|
|
return "", "Email is required."
|
|
}
|
|
n := utf8.RuneCountInString(normalized)
|
|
if n < minEmailLen || len(normalized) > maxEmailLen {
|
|
return "", "Enter a valid email address."
|
|
}
|
|
addr, err := mail.ParseAddress(normalized)
|
|
if err != nil || addr.Address != normalized {
|
|
return "", "Enter a valid email address."
|
|
}
|
|
at := strings.LastIndex(normalized, "@")
|
|
if at < 1 || at == len(normalized)-1 {
|
|
return "", "Enter a valid email address."
|
|
}
|
|
domain := normalized[at+1:]
|
|
if !strings.Contains(domain, ".") || strings.HasPrefix(domain, ".") || strings.HasSuffix(domain, ".") {
|
|
return "", "Enter a valid email address."
|
|
}
|
|
return normalized, ""
|
|
}
|
|
|
|
// MustValidateEmail is like ValidateEmail but returns a Go error.
|
|
func MustValidateEmail(raw string) (string, error) {
|
|
n, msg := ValidateEmail(raw)
|
|
if msg != "" {
|
|
return "", fmt.Errorf("%s", msg)
|
|
}
|
|
return n, nil
|
|
}
|