package main import ( "crypto/rand" "encoding/hex" "errors" "strings" "time" ) func mustRandToken(n int) string { b := make([]byte, n) if _, err := rand.Read(b); err != nil { panic(err) } return hex.EncodeToString(b) } func now() time.Time { return time.Now().UTC() } func defaultUsername() string { // io-game style: adjective-animal-xxxx adjectives := []string{"swift", "brave", "mighty", "cheeky", "sneaky", "zippy", "bouncy", "crispy", "fuzzy", "spicy", "snappy", "jazzy", "spry", "bold", "witty"} animals := []string{"otter", "panda", "falcon", "lynx", "badger", "tiger", "koala", "yak", "gecko", "eagle", "mamba", "fox", "yak", "whale", "rhino"} a := adjectives[int(time.Now().UnixNano())%len(adjectives)] an := animals[int(time.Now().UnixNano()/17)%len(animals)] return a + "-" + an + "-" + strings.ToLower(mustRandToken(2)) } func slugify(s string) string { s = strings.ToLower(s) s = strings.TrimSpace(s) s = strings.ReplaceAll(s, " ", "-") // keep alnum and - only var b strings.Builder for _, r := range s { if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '-' { b.WriteRune(r) } } out := b.String() if out == "" { out = "user-" + mustRandToken(2) } return out } func ensureUniqueUsernameAndSlug(u *User) error { // try to keep username/slug unique by appending short token if needed baseU := u.Username baseS := slugify(u.Username) for i := 0; i < 5; i++ { candU := baseU candS := baseS if i > 0 { suffix := "-" + mustRandToken(1) candU = baseU + suffix candS = baseS + suffix } var cnt int64 db.Model(&User{}).Where("username = ?", candU).Count(&cnt) if cnt == 0 { db.Model(&User{}).Where("slug = ?", candS).Count(&cnt) if cnt == 0 { u.Username = candU u.Slug = candS return nil } } } return errors.New("cannot create unique username/slug") } func userByEmail(email string) (*User, error) { var u User tx := db.Where("email = ?", strings.ToLower(email)).First(&u) if tx.Error != nil { return nil, tx.Error } return &u, nil } func userBySlug(slug string) (*User, error) { var u User tx := db.Where("slug = ?", slug).First(&u) if tx.Error != nil { return nil, tx.Error } return &u, nil } func atoiSafe(s string) int { var n int for _, r := range s { if r < '0' || r > '9' { return 0 } } _, _ = fmtSscanf(s, "%d", &n) return n } // minimal sscanf to avoid fmt import just for one call func fmtSscanf(s, _ string, a *int) (int, error) { // Only supports "%d" n := 0 for i := 0; i < len(s); i++ { c := s[i] if c < '0' || c > '9' { break } n = n*10 + int(c-'0') } *a = n return 1, nil } func stripAfterDot(s string) string { if idx := strings.Index(s, "."); idx != -1 { return s[:idx] } return s }