chore: initial commit of ai site platform
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
102
platform/internal/userstore/creds.go
Normal file
102
platform/internal/userstore/creds.go
Normal file
@@ -0,0 +1,102 @@
|
||||
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))
|
||||
}
|
||||
Reference in New Issue
Block a user