package service import ( "crypto/rand" "math/big" "regexp" "strings" "github.com/snip/backend/internal/domain" ) const base62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" // ReservedCodes can never be claimed as custom aliases, because these top-level // paths are owned by the frontend / api commands. var ReservedCodes = map[string]struct{}{ "": {}, "api": {}, "login": {}, "dashboard": {}, "assets": {}, "static": {}, "healthz": {}, "favicon.svg": {}, "favicon.ico": {}, "robots.txt": {}, "unlock": {}, } // IsReserved reports whether a code collides with an app-owned path. func IsReserved(code string) bool { _, ok := ReservedCodes[strings.ToLower(code)] return ok } func encodeBase62(n int64) string { if n == 0 { return string(base62[0]) } var b strings.Builder for n > 0 { b.WriteByte(base62[n%62]) n /= 62 } // reverse s := []byte(b.String()) for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { s[i], s[j] = s[j], s[i] } return string(s) } // scramble spreads sequential ids so codes don't look enumerable, while staying // short. Reversible multiplicative hash over a 31-bit space. func scramble(id int64) int64 { const prime = 2654435761 const mod = 0x7fffffff return ((id + 1) * prime) % mod } // CodeFromSequence turns a DB sequence value into the shortest unique base62 // code (no collision checks needed — the sequence guarantees uniqueness). func CodeFromSequence(seq int64) string { return encodeBase62(scramble(seq + 100000)) } var aliasRe = regexp.MustCompile(`^[a-zA-Z0-9_-]{3,32}$`) // ValidateAlias checks a user-supplied custom code. func ValidateAlias(alias string) error { if !aliasRe.MatchString(alias) { return domain.ErrInvalidAlias } if IsReserved(alias) { return domain.ErrReserved } return nil } var urlRe = regexp.MustCompile(`(?i)^https?://[^\s.]+\.[^\s]{2,}$`) // NormalizeURL ensures a scheme is present. func NormalizeURL(raw string) string { s := strings.TrimSpace(raw) if s == "" { return "" } low := strings.ToLower(s) if strings.HasPrefix(low, "http://") || strings.HasPrefix(low, "https://") { return s } return "https://" + s } // ValidURL validates a normalized destination. func ValidURL(raw string) bool { return urlRe.MatchString(raw) } var pinRe = regexp.MustCompile(`^\d{6}$`) // ValidPin reports whether a string is exactly 6 digits. func ValidPin(pin string) bool { return pinRe.MatchString(pin) } func pick(arr []string) string { n, _ := rand.Int(rand.Reader, big.NewInt(int64(len(arr)))) return arr[n.Int64()] } // MemorableSlug builds an easy-to-say "adjective-noun-verb" code. func MemorableSlug() string { return pick(adjectives) + "-" + pick(nouns) + "-" + pick(verbs) } // newID mints an opaque link id. func newID() string { const n = 10 b := make([]byte, n) for i := range b { idx, _ := rand.Int(rand.Reader, big.NewInt(62)) b[i] = base62[idx.Int64()] } return "l_" + string(b) }