* improve(user):优化注册功能提示 * improve(user):优化邮箱大小写重复问题 * improve(user):添加密码强度指示器 * fix(setting):修复系统设置功能里面的数字转换问题 * feat(email):优化邮件发送功能 * chore(email):移除无用配置和日志 * fix(user):修复重置密码功能 * chore(ui):调整轮询间隔 * chore(ui):添加充值提示 * chore(ui):优化用户注册功能 * chore(sql):补充数据库变更文件 * fix(email):优化邮箱校验 --------- Co-authored-by: qian <wuchengqian5@163.com>
115 lines
2.9 KiB
Go
115 lines
2.9 KiB
Go
package admin
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/gogf/gf/v2/frame/g"
|
|
|
|
"github.com/qianfree/team-api/internal/consts"
|
|
"github.com/qianfree/team-api/internal/dao"
|
|
"github.com/qianfree/team-api/internal/logic/common"
|
|
do "github.com/qianfree/team-api/internal/model/do"
|
|
"github.com/qianfree/team-api/internal/utility/crypto"
|
|
)
|
|
|
|
var usernameRegex = regexp.MustCompile(`^[a-zA-Z0-9]{3,20}$`)
|
|
|
|
// AdminExists checks whether any admin user exists in the database.
|
|
func AdminExists(ctx context.Context) (bool, error) {
|
|
count, err := dao.SysAdminUsers.Ctx(ctx).Count()
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return count > 0, nil
|
|
}
|
|
|
|
// ValidateSetupUsername validates the username format.
|
|
func ValidateSetupUsername(username string) error {
|
|
if !usernameRegex.MatchString(username) {
|
|
return common.NewBusinessError(consts.CodeSetupInvalidUsername, consts.MsgSetupInvalidUsername)
|
|
}
|
|
// Disallow pure numeric usernames
|
|
allDigit := true
|
|
for _, c := range username {
|
|
if c < '0' || c > '9' {
|
|
allDigit = false
|
|
break
|
|
}
|
|
}
|
|
if allDigit {
|
|
return common.NewBusinessError(consts.CodeSetupInvalidUsername, "用户名不能为纯数字")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CreateAdmin creates a new super admin account.
|
|
func CreateAdmin(ctx context.Context, username, password, displayName string) error {
|
|
exists, err := AdminExists(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if exists {
|
|
return common.NewBusinessError(consts.CodeSetupCompleted, consts.MsgSetupCompleted)
|
|
}
|
|
|
|
if err := ValidateSetupUsername(username); err != nil {
|
|
return err
|
|
}
|
|
if err := common.ValidatePassword(password); err != nil {
|
|
return common.NewBusinessError(consts.CodePasswordTooWeak, consts.MsgPasswordTooWeak)
|
|
}
|
|
|
|
if displayName == "" {
|
|
displayName = username
|
|
}
|
|
|
|
passwordHash, err := crypto.HashPassword(password)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = dao.SysAdminUsers.Ctx(ctx).Data(do.SysAdminUsers{
|
|
Username: strings.TrimSpace(username),
|
|
PasswordHash: passwordHash,
|
|
DisplayName: strings.TrimSpace(displayName),
|
|
Role: "super_admin",
|
|
Status: "active",
|
|
}).Insert()
|
|
if err != nil {
|
|
if common.IsDuplicateKeyError(err) {
|
|
return common.NewBusinessError(consts.CodeSetupCompleted, consts.MsgSetupCompleted)
|
|
}
|
|
return err
|
|
}
|
|
|
|
g.Log().Infof(ctx, "管理员账号创建成功: username=%s", username)
|
|
return nil
|
|
}
|
|
|
|
// AutoInitAdmin checks INIT_ADMIN_USERNAME/INIT_ADMIN_PASSWORD env vars.
|
|
func AutoInitAdmin(ctx context.Context) (bool, error) {
|
|
username := os.Getenv("INIT_ADMIN_USERNAME")
|
|
password := os.Getenv("INIT_ADMIN_PASSWORD")
|
|
if username == "" || password == "" {
|
|
return false, nil
|
|
}
|
|
|
|
exists, err := AdminExists(ctx)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if exists {
|
|
g.Log().Info(ctx, "管理员已存在,跳过自动初始化")
|
|
return true, nil
|
|
}
|
|
|
|
if err := CreateAdmin(ctx, username, password, username); err != nil {
|
|
return false, err
|
|
}
|
|
g.Log().Info(ctx, "通过环境变量自动初始化管理员完成")
|
|
return true, nil
|
|
}
|