package main import ( "context" "flag" "fmt" "git.yeij.top/AskaEth/Scoreboard/server/internal/auth" "git.yeij.top/AskaEth/Scoreboard/server/internal/config" "git.yeij.top/AskaEth/Scoreboard/server/internal/store" ) // runPasswd 实现 `scoreboard passwd` 子命令:重置用户密码并作废其全部会话。 // 初始密码是首次启动随机生成的,只能用日志找回;忘了就用这个命令重置。 func runPasswd(args []string) error { fs := flag.NewFlagSet("passwd", flag.ExitOnError) configPath := fs.String("config", "config.yaml", "配置文件路径") dataDir := fs.String("data", "", "数据目录;非空时覆盖配置文件里的 dataDir") // passwd 不涉及赛事项目,参数只为与 server / backup 的命令行口径一致(解析顺序、校验规则相同)。 project := fs.String("project", "", "赛事项目名;留空则用记住的上一次赛事,再没有就用 default") username := fs.String("user", "admin", "用户名") newUsername := fs.String("new-username", "", "同时把用户名改成这个(留空则不改)") password := fs.String("password", "", "新密码;留空则随机生成并打印") if err := fs.Parse(args); err != nil { return err } cfg, err := config.Load(*configPath) if err != nil { return err } // Console 拉起服务端时用 -data 把数据指到安装目录之外;这里必须用同一份, // 否则改的是安装目录下那个空库里的账号。 cfg.ApplyDataDir(*dataDir) // 解析一次只为与另外两个命令同名同规则;passwd 不需要赛事项目名,解析结果不参与后续逻辑。 if _, _, err := resolveProjectName(cfg.DataDir, *project); err != nil { return err } st, err := store.Open(cfg.DataDir) if err != nil { return err } defer func() { _ = st.Close() }() if err := st.Migrate(); err != nil { return err } pwd := *password generated := pwd == "" if generated { pwd, err = auth.RandomPassword(16) if err != nil { return err } } svc := auth.New(st.DB(), auth.Options{}) if *newUsername != "" && *newUsername != *username { if err := svc.SetUsername(context.Background(), *username, *newUsername); err != nil { return err } fmt.Printf("已将用户名 %s 改为 %s\n", *username, *newUsername) *username = *newUsername } if err := svc.SetPassword(context.Background(), *username, pwd, generated); err != nil { return err } if generated { fmt.Printf("已重置用户 %s 的密码:%s\n请登录后立即修改(已要求下次登录强制改密)。\n", *username, pwd) } else { fmt.Printf("已重置用户 %s 的密码。\n", *username) } return nil }