Files
ai_site/platform/internal/userstore/creds.go
2026-07-31 10:31:17 +08:00

103 lines
2.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package userstore
import (
"context"
"crypto/rand"
"fmt"
"strings"
)
const passwordAlphabet = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789"
const usernameAlphabet = "abcdefghijkmnopqrstuvwxyz23456789"
const usernameFirst = "abcdefghijkmnopqrstuvwxyz"
// RandomPassword 生成可读随机密码(不含易混字符 0/O/1/l
func RandomPassword(n int) (string, error) {
if n < 8 {
n = 12
}
b := make([]byte, n)
if _, err := rand.Read(b); err != nil {
return "", err
}
out := make([]byte, n)
for i := range b {
out[i] = passwordAlphabet[int(b[i])%len(passwordAlphabet)]
}
return string(out), nil
}
// RandomUsername 生成随机登录名(字母开头,仅小写字母与数字,默认 10 位)。
func RandomUsername() (string, error) {
const n = 10
b := make([]byte, n)
if _, err := rand.Read(b); err != nil {
return "", err
}
out := make([]byte, n)
out[0] = usernameFirst[int(b[0])%len(usernameFirst)]
for i := 1; i < n; i++ {
out[i] = usernameAlphabet[int(b[i])%len(usernameAlphabet)]
}
return string(out), nil
}
// AllocUniqueUsername 分配全局唯一随机用户名。
func AllocUniqueUsername(ctx context.Context, exists func(context.Context, string) (bool, error)) (string, error) {
var last error
for i := 0; i < 16; i++ {
u, err := RandomUsername()
if err != nil {
return "", err
}
ok, err := exists(ctx, u)
if err != nil {
return "", err
}
if ok {
last = fmt.Errorf("username collision")
continue
}
return u, nil
}
if last != nil {
return "", fmt.Errorf("无法生成唯一用户名: %v", last)
}
return "", fmt.Errorf("无法生成唯一用户名")
}
// Deprecated: 保留测试兼容;新逻辑请用 RandomUsername / AllocUniqueUsername。
func SuggestAdminUsername(slug string) string {
s := strings.ToLower(strings.TrimSpace(slug))
var b strings.Builder
for _, r := range s {
if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') {
b.WriteRune(r)
}
}
base := b.String()
if base == "" {
base = "co"
}
if len(base) > 20 {
base = base[:20]
}
return base + "_adm"
}
// Deprecated: 见 AllocUniqueUsername。
func NextUsernameCandidate(base string, attempt int) string {
base = strings.TrimSpace(base)
if base == "" {
base = "user"
}
if attempt <= 0 {
return base
}
suffix, err := RandomPassword(4)
if err != nil {
return fmt.Sprintf("%s%d", base, attempt)
}
return fmt.Sprintf("%s_%s", base, strings.ToLower(suffix))
}