From 1b7edd3c19a0de924bd301a8802a2a0807cf6b2b Mon Sep 17 00:00:00 2001 From: Christo De Lange Date: Mon, 1 Sep 2025 19:37:23 -0400 Subject: [PATCH 1/5] chore: release? --- .goreleaser.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index b203c28f..74ab530f 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -17,6 +17,10 @@ builds: - windows - darwin - linux + ignore: + - goos: windows + goarch: arm64 + archives: - name_template: "hapi-{{ .Version }}-{{ .Os }}-{{ .Arch }}" From d36631363818644ca4b46323124fd550ae0587f5 Mon Sep 17 00:00:00 2001 From: Christo De Lange Date: Mon, 1 Sep 2025 19:47:10 -0400 Subject: [PATCH 2/5] fix: org --- .goreleaser.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 74ab530f..bfae0f4c 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -9,10 +9,10 @@ builds: - -mod=vendor - -v ldflags: - - -X github.com/hostinger/api-cli/utils.Build={{ .ShortCommit }} - - -X github.com/hostinger/api-cli/utils.Major={{ .Major }} - - -X github.com/hostinger/api-cli/utils.Minor={{ .Minor }} - - -X github.com/hostinger/api-cli/utils.Patch={{ .Patch }} + - -X github.com/softwarecatscom/api-cli/utils.Build={{ .ShortCommit }} + - -X github.com/softwarecatscom/api-cli/utils.Major={{ .Major }} + - -X github.com/softwarecatscom/api-cli/utils.Minor={{ .Minor }} + - -X github.com/softwarecatscom/api-cli/utils.Patch={{ .Patch }} goos: - windows - darwin @@ -43,7 +43,7 @@ checksum: release: github: - owner: hostinger + owner: softwarecatscom name: api-cli changelog: From 0cf29f7c8e6cb1cd0399ec5a6f7db104a8ac7586 Mon Sep 17 00:00:00 2001 From: Christo De Lange Date: Wed, 17 Sep 2025 12:09:37 -0400 Subject: [PATCH 3/5] fix: resolve hapi vps firewall list JSON parsing error The firewall list command was failing due to a mismatch between the OpenAPI specification and actual API implementation. The spec defines a paginated response structure but the API returns a direct array. Changes: - Bypass generated client parsing that expects paginated response - Use raw HTTP client to get actual API response - Parse response as direct array of firewall resources - Maintain existing output formatting This resolves the "cannot unmarshal array into struct" error and allows the firewall list command to work correctly. Related: https://github.com/hostinger/api/issues/23 --- cmd/vps/firewall/list.go | 64 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/cmd/vps/firewall/list.go b/cmd/vps/firewall/list.go index 6fa85d5d..0cd57b3b 100644 --- a/cmd/vps/firewall/list.go +++ b/cmd/vps/firewall/list.go @@ -2,11 +2,17 @@ package firewall import ( "context" - "github.com/hostinger/api-cli/api" + "encoding/json" + "io" + "net/http" + + //"github.com/hostinger/api-cli/api" "github.com/hostinger/api-cli/client" "github.com/hostinger/api-cli/output" "github.com/hostinger/api-cli/utils" + "github.com/oapi-codegen/oapi-codegen/v2/pkg/securityprovider" "github.com/spf13/cobra" + "github.com/spf13/viper" "log" ) @@ -15,12 +21,40 @@ var ListCmd = &cobra.Command{ Short: "Get firewall list", Long: `This endpoint retrieves a list of all firewalls available.`, Run: func(cmd *cobra.Command, args []string) { - r, err := api.Request().VPSGetFirewallListV1WithResponse(context.TODO(), firewallListRequestParameters(cmd)) + // Create raw client to bypass the broken generated parsing + rawClient, err := createRawClient() + if err != nil { + log.Fatal(err) + } + + // Call the raw HTTP endpoint + resp, err := rawClient.VPSGetFirewallListV1(context.TODO(), firewallListRequestParameters(cmd)) if err != nil { log.Fatal(err) } + defer func() { _ = resp.Body.Close() }() + + // Read the raw response body + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + log.Fatal(err) + } + + // Handle error status codes + if resp.StatusCode >= http.StatusBadRequest { + output.Format(cmd, bodyBytes, resp.StatusCode) + return + } - output.Format(cmd, r.Body, r.StatusCode()) + // Validate that we can parse the response as a direct array + // This confirms the API returns the correct format + var firewalls []client.VPSV1FirewallFirewallResource + if err := json.Unmarshal(bodyBytes, &firewalls); err != nil { + log.Fatalf("Failed to parse firewall list response: %v", err) + } + + // Pass the raw JSON to output formatter + output.Format(cmd, bodyBytes, resp.StatusCode) }, } @@ -28,6 +62,30 @@ func init() { ListCmd.Flags().IntP("page", "", 1, "Page number") } +func createRawClient() (client.ClientInterface, error) { + // Replicate the logic from api.Request() but return raw client + bearerToken, err := securityprovider.NewSecurityProviderBearerToken(viper.GetString("api_token")) + if err != nil { + return nil, err + } + + var apiUrl = viper.GetString("api_url") + if apiUrl == "" { + apiUrl = "https://developers.hostinger.com" + } + + return client.NewClient( + apiUrl, + client.WithRequestEditorFn(bearerToken.Intercept), + client.WithRequestEditorFn(addUserAgent), + ) +} + +func addUserAgent(_ context.Context, req *http.Request) error { + req.Header.Set("User-Agent", "hostinger-cli/"+utils.CLIVersion.String(false)) + return nil +} + func firewallListRequestParameters(cmd *cobra.Command) *client.VPSGetFirewallListV1Params { pageId, _ := cmd.Flags().GetInt("page") From b88dbfcd608111346d52612c308eb4f8295a091d Mon Sep 17 00:00:00 2001 From: Christo De Lange Date: Wed, 17 Sep 2025 13:15:24 -0400 Subject: [PATCH 4/5] feat: Support format in hapi.yaml --- output/output.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/output/output.go b/output/output.go index 9df920e4..e91344b3 100644 --- a/output/output.go +++ b/output/output.go @@ -3,6 +3,7 @@ package output import ( "fmt" "github.com/spf13/cobra" + "github.com/spf13/viper" "log" "net/http" "os" @@ -18,7 +19,12 @@ func Format(cmd *cobra.Command, body []byte, statusCode int) { fmt.Println(string(body)) os.Exit(1) } - + if format == "" { + val := viper.GetString("format") + if val != "" { + format = val + } + } switch format { default: printTable(body) From 45ebeb137e04f6d1c2f3e7e29ab7d29d248710c9 Mon Sep 17 00:00:00 2001 From: Christo De Lange Date: Wed, 17 Sep 2025 13:44:08 -0400 Subject: [PATCH 5/5] Support HAPI_VERBOSE with default==0 to not break JSON output --- cmd/root.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cmd/root.go b/cmd/root.go index 75c41e12..53bf15f1 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -3,6 +3,7 @@ package cmd import ( "fmt" "github.com/hostinger/api-cli/cmd/vps" + "github.com/spf13/cast" "os" "github.com/spf13/cobra" @@ -61,7 +62,14 @@ func initConfig() { viper.SetEnvPrefix("hapi") viper.AutomaticEnv() // read in environment variables that match + _verbose := viper.GetString("verbose") + verbose := 0 + if _verbose != "" { + verbose = cast.ToInt(_verbose) + } if err := viper.ReadInConfig(); err == nil { - fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed()) + if verbose > 0 { + fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed()) + } } }