Compare commits
3 Commits
e2177521e0
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| cd22642472 | |||
| 5ac90408f3 | |||
| ad0e123666 |
@@ -32,6 +32,28 @@ func main() {
|
|||||||
handleLeafOrRun("health", args[1:], commands.RunHealth)
|
handleLeafOrRun("health", args[1:], commands.RunHealth)
|
||||||
case "config":
|
case "config":
|
||||||
handleConfig(args[1:])
|
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:
|
default:
|
||||||
if group, ok := findGroup(args[0]); ok {
|
if group, ok := findGroup(args[0]); ok {
|
||||||
handleGroup(group, args[1:])
|
handleGroup(group, args[1:])
|
||||||
@@ -204,6 +226,31 @@ func handleGroup(group help.Group, args []string) {
|
|||||||
return
|
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)
|
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)
|
commands.RunUserGet(filtered[0], tokenFlag)
|
||||||
case "create":
|
case "create":
|
||||||
username, password, email, fullName := "", "", "", ""
|
username, password, email, fullName, discordUserID := "", "", "", "", ""
|
||||||
for i := 0; i < len(filtered); i++ {
|
for i := 0; i < len(filtered); i++ {
|
||||||
switch filtered[i] {
|
switch filtered[i] {
|
||||||
case "--user":
|
case "--user":
|
||||||
@@ -261,6 +308,11 @@ func handleUserCommand(subCmd string, args []string) {
|
|||||||
i++
|
i++
|
||||||
fullName = filtered[i]
|
fullName = filtered[i]
|
||||||
}
|
}
|
||||||
|
case "--discord-user-id":
|
||||||
|
if i+1 < len(filtered) {
|
||||||
|
i++
|
||||||
|
discordUserID = filtered[i]
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
output.Errorf("unknown flag: %s", filtered[i])
|
output.Errorf("unknown flag: %s", filtered[i])
|
||||||
}
|
}
|
||||||
@@ -268,7 +320,7 @@ func handleUserCommand(subCmd string, args []string) {
|
|||||||
if username == "" {
|
if username == "" {
|
||||||
output.Error("usage: hf user create --user <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":
|
case "update":
|
||||||
if len(filtered) < 1 {
|
if len(filtered) < 1 {
|
||||||
output.Error("usage: hf user update <username> [--email ...] [--full-name ...] [--pass ...] [--active ...]")
|
output.Error("usage: hf user update <username> [--email ...] [--full-name ...] [--pass ...] [--active ...]")
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
type Client struct {
|
type Client struct {
|
||||||
BaseURL string
|
BaseURL string
|
||||||
Token string
|
Token string
|
||||||
|
APIKey string
|
||||||
HTTPClient *http.Client
|
HTTPClient *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,6 +29,17 @@ func New(baseURL, token string) *Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewWithAPIKey creates a Client that authenticates using X-API-Key.
|
||||||
|
func NewWithAPIKey(baseURL, apiKey string) *Client {
|
||||||
|
return &Client{
|
||||||
|
BaseURL: strings.TrimRight(baseURL, "/"),
|
||||||
|
APIKey: apiKey,
|
||||||
|
HTTPClient: &http.Client{
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// RequestError represents a non-2xx HTTP response.
|
// RequestError represents a non-2xx HTTP response.
|
||||||
type RequestError struct {
|
type RequestError struct {
|
||||||
StatusCode int
|
StatusCode int
|
||||||
@@ -45,7 +57,9 @@ func (c *Client) Do(method, path string, body io.Reader) ([]byte, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot create request: %w", err)
|
return nil, fmt.Errorf("cannot create request: %w", err)
|
||||||
}
|
}
|
||||||
if c.Token != "" {
|
if c.APIKey != "" {
|
||||||
|
req.Header.Set("X-API-Key", c.APIKey)
|
||||||
|
} else if c.Token != "" {
|
||||||
req.Header.Set("Authorization", "Bearer "+c.Token)
|
req.Header.Set("Authorization", "Bearer "+c.Token)
|
||||||
}
|
}
|
||||||
if body != nil {
|
if body != nil {
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.hangman-lab.top/zhi/HarborForge.Cli/internal/client"
|
"git.hangman-lab.top/zhi/HarborForge.Cli/internal/client"
|
||||||
@@ -23,6 +25,7 @@ type userResponse struct {
|
|||||||
IsAdmin bool `json:"is_admin"`
|
IsAdmin bool `json:"is_admin"`
|
||||||
RoleID *int `json:"role_id"`
|
RoleID *int `json:"role_id"`
|
||||||
RoleName *string `json:"role_name"`
|
RoleName *string `json:"role_name"`
|
||||||
|
DiscordUserID *string `json:"discord_user_id"`
|
||||||
CreatedAt string `json:"created_at"`
|
CreatedAt string `json:"created_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,10 +140,41 @@ type userCreatePayload struct {
|
|||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
FullName *string `json:"full_name,omitempty"`
|
FullName *string `json:"full_name,omitempty"`
|
||||||
Password *string `json:"password,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`.
|
// 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
|
// Resolve account-manager token
|
||||||
var accMgrToken string
|
var accMgrToken string
|
||||||
if mode.IsPaddedCell() {
|
if mode.IsPaddedCell() {
|
||||||
@@ -181,6 +215,11 @@ func RunUserCreate(username, password, email, fullName, accMgrTokenFlag string)
|
|||||||
Email: email,
|
Email: email,
|
||||||
Password: &password,
|
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 != "" {
|
if fullName != "" {
|
||||||
payload.FullName = &fullName
|
payload.FullName = &fullName
|
||||||
}
|
}
|
||||||
@@ -194,7 +233,7 @@ func RunUserCreate(username, password, email, fullName, accMgrTokenFlag string)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
output.Errorf("config error: %v", err)
|
output.Errorf("config error: %v", err)
|
||||||
}
|
}
|
||||||
c := client.New(cfg.BaseURL, accMgrToken)
|
c := client.NewWithAPIKey(cfg.BaseURL, accMgrToken)
|
||||||
data, err := c.Post("/users", bytes.NewReader(body))
|
data, err := c.Post("/users", bytes.NewReader(body))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
output.Errorf("failed to create user: %v", err)
|
output.Errorf("failed to create user: %v", err)
|
||||||
@@ -216,6 +255,28 @@ func RunUserCreate(username, password, email, fullName, accMgrTokenFlag string)
|
|||||||
fmt.Printf("user created: %s\n", u.Username)
|
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>`.
|
// RunUserUpdate implements `hf user update <username>`.
|
||||||
func RunUserUpdate(username string, args []string, tokenFlag string) {
|
func RunUserUpdate(username string, args []string, tokenFlag string) {
|
||||||
token := ResolveToken(tokenFlag)
|
token := ResolveToken(tokenFlag)
|
||||||
|
|||||||
Reference in New Issue
Block a user