feat: support discord id account updates

This commit is contained in:
2026-04-04 20:16:59 +00:00
parent ad0e123666
commit 5ac90408f3
2 changed files with 116 additions and 3 deletions

View File

@@ -32,6 +32,28 @@ func main() {
handleLeafOrRun("health", args[1:], commands.RunHealth)
case "config":
handleConfig(args[1:])
case "update-discord-id":
tokenFlag := ""
var filtered []string
for i := 1; i < len(args); i++ {
switch args[i] {
case "--token":
if i+1 < len(args) {
i++
tokenFlag = args[i]
}
default:
filtered = append(filtered, args[i])
}
}
if len(filtered) < 1 {
output.Error("usage: hf update-discord-id <username> [discord-id]")
}
discordID := ""
if len(filtered) >= 2 {
discordID = filtered[1]
}
commands.RunUserUpdateDiscordID(filtered[0], discordID, tokenFlag)
default:
if group, ok := findGroup(args[0]); ok {
handleGroup(group, args[1:])
@@ -204,6 +226,31 @@ func handleGroup(group help.Group, args []string) {
return
}
if len(args) > 0 && args[0] == "update-discord-id" {
tokenFlag := ""
var filtered []string
for i := 1; i < len(args); i++ {
switch args[i] {
case "--token":
if i+1 < len(args) {
i++
tokenFlag = args[i]
}
default:
filtered = append(filtered, args[i])
}
}
if len(filtered) < 1 {
output.Error("usage: hf update-discord-id <username> [discord-id]")
}
discordID := ""
if len(filtered) >= 2 {
discordID = filtered[1]
}
commands.RunUserUpdateDiscordID(filtered[0], discordID, tokenFlag)
return
}
output.Errorf("hf %s %s is recognized but not implemented yet", group.Name, sub.Name)
}
@@ -238,7 +285,7 @@ func handleUserCommand(subCmd string, args []string) {
}
commands.RunUserGet(filtered[0], tokenFlag)
case "create":
username, password, email, fullName := "", "", "", ""
username, password, email, fullName, discordUserID := "", "", "", "", ""
for i := 0; i < len(filtered); i++ {
switch filtered[i] {
case "--user":
@@ -261,6 +308,11 @@ func handleUserCommand(subCmd string, args []string) {
i++
fullName = filtered[i]
}
case "--discord-user-id":
if i+1 < len(filtered) {
i++
discordUserID = filtered[i]
}
default:
output.Errorf("unknown flag: %s", filtered[i])
}
@@ -268,7 +320,7 @@ func handleUserCommand(subCmd string, args []string) {
if username == "" {
output.Error("usage: hf user create --user <username>")
}
commands.RunUserCreate(username, password, email, fullName, accMgrTokenFlag)
commands.RunUserCreate(username, password, email, fullName, discordUserID, accMgrTokenFlag)
case "update":
if len(filtered) < 1 {
output.Error("usage: hf user update <username> [--email ...] [--full-name ...] [--pass ...] [--active ...]")

View File

@@ -4,6 +4,8 @@ import (
"bytes"
"encoding/json"
"fmt"
"os"
"os/exec"
"strings"
"git.hangman-lab.top/zhi/HarborForge.Cli/internal/client"
@@ -23,6 +25,7 @@ type userResponse struct {
IsAdmin bool `json:"is_admin"`
RoleID *int `json:"role_id"`
RoleName *string `json:"role_name"`
DiscordUserID *string `json:"discord_user_id"`
CreatedAt string `json:"created_at"`
}
@@ -137,10 +140,41 @@ type userCreatePayload struct {
Email string `json:"email"`
FullName *string `json:"full_name,omitempty"`
Password *string `json:"password,omitempty"`
DiscordUserID *string `json:"discord_user_id,omitempty"`
}
func maybeResolveDiscordUserID(explicit string, requireEnv bool) (string, bool, error) {
if strings.TrimSpace(explicit) != "" {
return strings.TrimSpace(explicit), true, nil
}
agentID := strings.TrimSpace(os.Getenv("AGENT_ID"))
agentVerify := strings.TrimSpace(os.Getenv("AGENT_VERIFY"))
if agentID == "" || agentVerify == "" {
if requireEnv {
return "", false, fmt.Errorf("discord id not provided and AGENT_ID/AGENT_VERIFY are missing")
}
return "", false, nil
}
cmd := exec.Command("ego-mgr", "get", "discord-id")
out, err := cmd.Output()
if err != nil {
if requireEnv {
return "", false, fmt.Errorf("failed to resolve discord id from ego-mgr: %w", err)
}
return "", false, nil
}
value := strings.TrimSpace(string(out))
if value == "" {
if requireEnv {
return "", false, fmt.Errorf("ego-mgr returned empty discord id")
}
return "", false, nil
}
return value, true, nil
}
// RunUserCreate implements `hf user create`.
func RunUserCreate(username, password, email, fullName, accMgrTokenFlag string) {
func RunUserCreate(username, password, email, fullName, discordUserID, accMgrTokenFlag string) {
// Resolve account-manager token
var accMgrToken string
if mode.IsPaddedCell() {
@@ -181,6 +215,11 @@ func RunUserCreate(username, password, email, fullName, accMgrTokenFlag string)
Email: email,
Password: &password,
}
if resolvedDiscordID, ok, err := maybeResolveDiscordUserID(discordUserID, false); err != nil {
output.Errorf("failed to resolve discord user id: %v", err)
} else if ok {
payload.DiscordUserID = &resolvedDiscordID
}
if fullName != "" {
payload.FullName = &fullName
}
@@ -216,6 +255,28 @@ func RunUserCreate(username, password, email, fullName, accMgrTokenFlag string)
fmt.Printf("user created: %s\n", u.Username)
}
// RunUserUpdateDiscordID updates a user's discord_user_id field.
func RunUserUpdateDiscordID(username, discordUserID, tokenFlag string) {
token := ResolveToken(tokenFlag)
resolvedDiscordID, _, err := maybeResolveDiscordUserID(discordUserID, true)
if err != nil {
output.Errorf("failed to resolve discord user id: %v", err)
}
body, err := json.Marshal(map[string]interface{}{"discord_user_id": resolvedDiscordID})
if err != nil {
output.Errorf("cannot marshal payload: %v", err)
}
cfg, err := config.Load()
if err != nil {
output.Errorf("config error: %v", err)
}
c := client.New(cfg.BaseURL, token)
if _, err := c.Patch("/users/"+username, bytes.NewReader(body)); err != nil {
output.Errorf("failed to update discord id: %v", err)
}
fmt.Printf("discord id updated: %s\n", username)
}
// RunUserUpdate implements `hf user update <username>`.
func RunUserUpdate(username string, args []string, tokenFlag string) {
token := ResolveToken(tokenFlag)