package commands import ( "bytes" "encoding/json" "fmt" "git.hangman-lab.top/zhi/HarborForge.Cli/internal/client" "git.hangman-lab.top/zhi/HarborForge.Cli/internal/config" "git.hangman-lab.top/zhi/HarborForge.Cli/internal/output" ) type commentResponse struct { ID int `json:"id"` TaskID int `json:"task_id"` AuthorID int `json:"author_id"` Content string `json:"content"` CreatedAt string `json:"created_at"` UpdatedAt *string `json:"updated_at"` } func RunCommentAdd(taskCode, content, tokenFlag string) { if taskCode == "" || content == "" { output.Error("usage: hf comment add --task --content ") } c := newAuthedClient(tokenFlag) taskID := resolveTaskID(c, taskCode) me := currentUser(c) payload := map[string]interface{}{ "task_id": taskID, "author_id": me.ID, "content": content, } body, err := json.Marshal(payload) if err != nil { output.Errorf("cannot marshal payload: %v", err) } data, err := c.Post("/comments", bytes.NewReader(body)) if err != nil { output.Errorf("failed to add comment: %v", err) } if output.JSONMode { var raw json.RawMessage if err := json.Unmarshal(data, &raw); err != nil { output.Errorf("invalid JSON response: %v", err) } output.PrintJSON(raw) return } var resp commentResponse if err := json.Unmarshal(data, &resp); err != nil { output.Errorf("cannot parse response: %v", err) } fmt.Printf("comment added to %s: #%d\n", taskCode, resp.ID) } func RunCommentList(taskCode, tokenFlag string) { if taskCode == "" { output.Error("usage: hf comment list --task ") } c := newAuthedClient(tokenFlag) taskID := resolveTaskID(c, taskCode) data, err := c.Get(fmt.Sprintf("/tasks/%d/comments", taskID)) if err != nil { output.Errorf("failed to list comments: %v", err) } if output.JSONMode { var raw json.RawMessage if err := json.Unmarshal(data, &raw); err != nil { output.Errorf("invalid JSON response: %v", err) } output.PrintJSON(raw) return } var comments []commentResponse if err := json.Unmarshal(data, &comments); err != nil { output.Errorf("cannot parse comment list: %v", err) } headers := []string{"ID", "AUTHOR", "CREATED", "CONTENT"} var rows [][]string for _, item := range comments { content := item.Content if len(content) > 60 { content = content[:57] + "..." } rows = append(rows, []string{fmt.Sprintf("%d", item.ID), fmt.Sprintf("%d", item.AuthorID), item.CreatedAt, content}) } output.PrintTable(headers, rows) } func newAuthedClient(tokenFlag string) *client.Client { token := ResolveToken(tokenFlag) cfg, err := config.Load() if err != nil { output.Errorf("config error: %v", err) } return client.New(cfg.BaseURL, token) } type authMeResponse struct { ID int `json:"id"` Username string `json:"username"` } func currentUser(c *client.Client) authMeResponse { data, err := c.Get("/auth/me") if err != nil { output.Errorf("failed to resolve current user: %v", err) } var me authMeResponse if err := json.Unmarshal(data, &me); err != nil { output.Errorf("cannot parse current user: %v", err) } return me } func resolveTaskID(c *client.Client, taskCode string) int { data, err := c.Get("/tasks/" + taskCode) if err != nil { output.Errorf("failed to resolve task %s: %v", taskCode, err) } var task taskResponse if err := json.Unmarshal(data, &task); err != nil { output.Errorf("cannot parse task %s: %v", taskCode, err) } return task.ID }