diff --git a/internal/api/client.go b/internal/api/client.go index 0ec49f3..dd466d3 100644 --- a/internal/api/client.go +++ b/internal/api/client.go @@ -115,10 +115,11 @@ type Credential struct { // Variable represents an n8n variable type Variable struct { - ID string `json:"id,omitempty"` - Key string `json:"key"` - Value string `json:"value"` - Type string `json:"type,omitempty"` + ID string `json:"id,omitempty"` + Key string `json:"key"` + Value string `json:"value"` + Type string `json:"type,omitempty"` + ProjectID string `json:"projectId,omitempty"` } // ListResult contains paginated list results @@ -683,8 +684,8 @@ func (c *Client) TriggerWebhook(path, method string) ([]byte, error) { // --- Variables --- -// ListVariables returns all variables -func (c *Client) ListVariables(limit int, cursor string) ([]Variable, error) { +// ListVariables returns all variables, optionally filtered by project. +func (c *Client) ListVariables(limit int, cursor string, projectID string) ([]Variable, error) { params := url.Values{} if limit > 0 { params.Set("limit", strconv.Itoa(limit)) @@ -692,6 +693,9 @@ func (c *Client) ListVariables(limit int, cursor string) ([]Variable, error) { if cursor != "" { params.Set("cursor", cursor) } + if projectID != "" { + params.Set("projectId", projectID) + } path := "/variables" if len(params) > 0 { @@ -713,9 +717,12 @@ func (c *Client) ListVariables(limit int, cursor string) ([]Variable, error) { return resp.Data, nil } -// CreateVariable creates a new variable -func (c *Client) CreateVariable(key, value string) error { +// CreateVariable creates a new variable, optionally scoped to a project. +func (c *Client) CreateVariable(key, value, projectID string) error { body := map[string]string{"key": key, "value": value} + if projectID != "" { + body["projectId"] = projectID + } _, err := c.request(http.MethodPost, "/variables", body) return err } diff --git a/internal/cmd/variable/variable.go b/internal/cmd/variable/variable.go index e4d26c9..7a76f4a 100644 --- a/internal/cmd/variable/variable.go +++ b/internal/cmd/variable/variable.go @@ -20,6 +20,8 @@ func NewVariableCmd() *cobra.Command { Long: `List, create, update, and delete n8n environment variables.`, } + cmd.PersistentFlags().String("project", "", "Project ID (omit for global variables)") + cmd.AddCommand(newListCmd()) cmd.AddCommand(newGetCmd()) cmd.AddCommand(newCreateCmd()) @@ -53,7 +55,8 @@ func newListCmd() *cobra.Command { return err } - vars, err := client.ListVariables(0, "") + projectID, _ := cmd.Flags().GetString("project") + vars, err := client.ListVariables(0, "", projectID) if err != nil { return fmt.Errorf("failed to list variables: %w", err) } @@ -68,14 +71,34 @@ func newListCmd() *cobra.Command { return nil } - fmt.Printf("%-8s %-40s %s\n", "ID", "KEY", "VALUE") - fmt.Printf("%-8s %-40s %s\n", strings.Repeat("-", 8), strings.Repeat("-", 40), strings.Repeat("-", 50)) + hasProject := false + for _, v := range vars { + if v.ProjectID != "" { + hasProject = true + break + } + } + + if hasProject { + fmt.Printf("%-8s %-18s %-40s %s\n", "ID", "PROJECT", "KEY", "VALUE") + fmt.Printf("%-8s %-18s %-40s %s\n", + strings.Repeat("-", 8), strings.Repeat("-", 18), + strings.Repeat("-", 40), strings.Repeat("-", 50)) + } else { + fmt.Printf("%-8s %-40s %s\n", "ID", "KEY", "VALUE") + fmt.Printf("%-8s %-40s %s\n", strings.Repeat("-", 8), strings.Repeat("-", 40), strings.Repeat("-", 50)) + } + for _, v := range vars { value := v.Value if len(value) > 50 { value = value[:47] + "..." } - fmt.Printf("%-8s %-40s %s\n", v.ID, v.Key, value) + if hasProject { + fmt.Printf("%-8s %-18s %-40s %s\n", v.ID, v.ProjectID, v.Key, value) + } else { + fmt.Printf("%-8s %-40s %s\n", v.ID, v.Key, value) + } } return nil @@ -96,7 +119,8 @@ func newGetCmd() *cobra.Command { return err } - vars, err := client.ListVariables(0, "") + projectID, _ := cmd.Flags().GetString("project") + vars, err := client.ListVariables(0, "", projectID) if err != nil { return fmt.Errorf("failed to list variables: %w", err) } @@ -147,7 +171,8 @@ special shell characters (e.g. n8nctl var create key --value 'b!xyz').`, return fmt.Errorf("value is required: pass as second argument or use --value") } - if err := client.CreateVariable(key, value); err != nil { + projectID, _ := cmd.Flags().GetString("project") + if err := client.CreateVariable(key, value, projectID); err != nil { return fmt.Errorf("failed to create variable: %w", err) } @@ -188,8 +213,9 @@ special shell characters (e.g. n8nctl var update key --value 'b!xyz').`, return fmt.Errorf("value is required: pass as second argument or use --value") } - // Resolve key to ID - vars, err := client.ListVariables(0, "") + // Resolve key to ID within the given project scope. + projectID, _ := cmd.Flags().GetString("project") + vars, err := client.ListVariables(0, "", projectID) if err != nil { return fmt.Errorf("failed to list variables: %w", err) } @@ -224,8 +250,9 @@ func newDeleteCmd() *cobra.Command { return err } - // Resolve key to ID - vars, err := client.ListVariables(0, "") + // Resolve key to ID within the given project scope. + projectID, _ := cmd.Flags().GetString("project") + vars, err := client.ListVariables(0, "", projectID) if err != nil { return fmt.Errorf("failed to list variables: %w", err) }