diff --git a/cmd/cli/cmd/status.go b/cmd/cli/cmd/status.go index 1ed3912..55f1277 100644 --- a/cmd/cli/cmd/status.go +++ b/cmd/cli/cmd/status.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "os" + "strings" "github.com/nullify-platform/cli/internal/auth" "github.com/nullify-platform/cli/internal/client" @@ -42,7 +43,8 @@ var securityStatusCmd = &cobra.Command{ // Fetch metrics overview qs := lib.BuildQueryString(queryParams) - overviewBody, err := lib.DoGet(ctx, nullifyClient.HttpClient, nullifyClient.BaseURL, "/admin/metrics/overview"+qs) + overviewBody, err := lib.DoPostJSON(ctx, nullifyClient.HttpClient, nullifyClient.BaseURL, "/admin/metrics/overview"+qs, strings.NewReader(`{"query":{}}`)) + if err != nil { fmt.Fprintf(os.Stderr, "Error fetching metrics: %v\n", err) os.Exit(ExitNetworkError) diff --git a/internal/client/client.go b/internal/client/client.go index 93b0a5c..b069153 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -26,6 +26,11 @@ type NullifyClient struct { // NewNullifyClient creates a client for the given host with bearer token auth. func NewNullifyClient(nullifyHost string, token string) *NullifyClient { + apiHost := nullifyHost + if !strings.HasPrefix(nullifyHost, "api.") { + apiHost = "api." + nullifyHost + } + httpClient := &http.Client{ Timeout: 30 * time.Second, Transport: NewRetryTransport(&authTransport{ @@ -37,7 +42,7 @@ func NewNullifyClient(nullifyHost string, token string) *NullifyClient { return &NullifyClient{ Host: nullifyHost, - BaseURL: "https://" + nullifyHost, + BaseURL: "https://" + apiHost, Token: token, HttpClient: httpClient, } diff --git a/internal/client/refreshing_transport.go b/internal/client/refreshing_transport.go index 1441c73..f6de03b 100644 --- a/internal/client/refreshing_transport.go +++ b/internal/client/refreshing_transport.go @@ -3,6 +3,7 @@ package client import ( "context" "net/http" + "strings" "sync" "time" @@ -47,9 +48,14 @@ func NewRefreshingNullifyClient(nullifyHost string, tokenProvider TokenProvider) Transport: NewRetryTransport(t), } + apiHost := nullifyHost + if !strings.HasPrefix(nullifyHost, "api.") { + apiHost = "api." + nullifyHost + } + return &NullifyClient{ Host: nullifyHost, - BaseURL: "https://" + nullifyHost, + BaseURL: "https://" + apiHost, Token: "", // Token is managed by the refreshing transport; do not use this field directly. HttpClient: httpClient, }, nil @@ -87,9 +93,6 @@ func (t *refreshingAuthTransport) RoundTrip(req *http.Request) (*http.Response, token := t.getToken(req.Context()) r := req.Clone(req.Context()) - r.URL.Scheme = "https" - r.URL.Host = t.nullifyHost - r.Host = t.nullifyHost r.Header.Set("Authorization", "Bearer "+token) r.Header.Set("User-Agent", "Nullify-CLI/mcp") return t.transport.RoundTrip(r) diff --git a/internal/client/transport.go b/internal/client/transport.go index f47f089..3b04d74 100644 --- a/internal/client/transport.go +++ b/internal/client/transport.go @@ -14,9 +14,6 @@ type authTransport struct { func (t *authTransport) RoundTrip(req *http.Request) (*http.Response, error) { r := req.Clone(req.Context()) - r.URL.Scheme = "https" - r.URL.Host = t.nullifyHost - r.Host = t.nullifyHost r.Header.Set("Authorization", "Bearer "+t.token) r.Header.Set("User-Agent", "Nullify-CLI/"+logger.Version) return t.transport.RoundTrip(r) diff --git a/internal/lib/http.go b/internal/lib/http.go index d67d387..a318ee5 100644 --- a/internal/lib/http.go +++ b/internal/lib/http.go @@ -59,6 +59,32 @@ func DoPost(ctx context.Context, httpClient Doer, baseURL, path string) (string, return string(body), nil } +// DoPostJSON performs a POST request with a JSON body and returns the response body as a string. +func DoPostJSON(ctx context.Context, httpClient Doer, baseURL, path string, body io.Reader) (string, error) { + req, err := http.NewRequestWithContext(ctx, "POST", baseURL+path, body) + if err != nil { + return "", err + } + req.Header.Set("Content-Type", "application/json") + + resp, err := httpClient.Do(req) + if err != nil { + return "", err + } + defer resp.Body.Close() + + respBody, err := io.ReadAll(io.LimitReader(resp.Body, 10<<20)) + if err != nil { + return "", err + } + + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return "", fmt.Errorf("API returned %d: %s", resp.StatusCode, string(respBody)) + } + + return string(respBody), nil +} + // DoGet performs a GET request and returns the response body as a string. // Returns an error if the request fails or the status code is not 2xx. func DoGet(ctx context.Context, httpClient Doer, baseURL, path string) (string, error) {