feat(auth): add user profile, phone login, and account deletion

This commit is contained in:
iu
2026-10-12 14:26:32 +03:00
parent 1cb9aaf198
commit 86aff695df
46 changed files with 2893 additions and 134 deletions
+63
View File
@@ -0,0 +1,63 @@
package auth
import (
"strings"
"galo/internal/httpx"
)
// Identifier kinds for login and registration.
const (
IdentifierEmail = "email"
IdentifierPhone = "phone"
)
// NormalizePhone validates and canonicalises a phone number to E.164: a leading
// plus followed by 8–15 digits. It is the single normalisation point used by
// registration, identifier addition and login, so lookups compare canonical
// values only.
func NormalizePhone(raw string) (string, error) {
s := strings.TrimSpace(raw)
if s == "" {
return "", httpx.BadRequest("invalid_phone", "phone is required")
}
if !strings.HasPrefix(s, "+") {
return "", httpx.BadRequest("invalid_phone", "phone must be in E.164 format with a leading +")
}
digits := s[1:]
if len(digits) < 8 || len(digits) > 15 {
return "", httpx.BadRequest("invalid_phone", "phone must contain between 8 and 15 digits")
}
for _, r := range digits {
if r < '0' || r > '9' {
return "", httpx.BadRequest("invalid_phone", "phone may contain digits only after the leading +")
}
}
return s, nil
}
func validEmail(s string) bool {
i := strings.Index(s, "@")
return i > 0 && i < len(s)-1 && !strings.ContainsAny(s, " \t\r\n")
}
// NormalizeIdentifier classifies a login identifier as email (contains `@`) or
// phone, returning the canonical value for lookups.
func NormalizeIdentifier(raw string) (kind, value string, err error) {
s := strings.TrimSpace(raw)
if s == "" {
return "", "", httpx.BadRequest("invalid_identifier", "identifier is required")
}
if strings.Contains(s, "@") {
v := strings.ToLower(s)
if !validEmail(v) {
return "", "", httpx.BadRequest("invalid_identifier", "valid email is required")
}
return IdentifierEmail, v, nil
}
v, err := NormalizePhone(s)
if err != nil {
return "", "", err
}
return IdentifierPhone, v, nil
}