summaryrefslogtreecommitdiffstats
path: root/internal/client/client.go
diff options
context:
space:
mode:
authors <[email protected]>2025-11-03 21:17:12 -0500
committers <[email protected]>2025-11-03 21:17:12 -0500
commit923d6aece0b508c303393b23e8f605d63f46835f (patch)
tree65b629c84a20d9cb1f34ba16797dbbe8861a7a91 /internal/client/client.go
downloaddborg-923d6aece0b508c303393b23e8f605d63f46835f.tar.gz
dborg-923d6aece0b508c303393b23e8f605d63f46835f.zip
hi
Diffstat (limited to 'internal/client/client.go')
-rw-r--r--internal/client/client.go119
1 files changed, 119 insertions, 0 deletions
diff --git a/internal/client/client.go b/internal/client/client.go
new file mode 100644
index 0000000..098479f
--- /dev/null
+++ b/internal/client/client.go
@@ -0,0 +1,119 @@
+package client
+
+import (
+ "bytes"
+ "dborg/internal/config"
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+ "time"
+)
+
+type Client struct {
+ config *config.Config
+ httpClient *http.Client
+}
+
+func New(cfg *config.Config) (*Client, error) {
+ if err := cfg.Validate(); err != nil {
+ return nil, err
+ }
+
+ return &Client{
+ config: cfg,
+ httpClient: &http.Client{
+ Timeout: cfg.Timeout,
+ },
+ }, nil
+}
+
+func (c *Client) doRequest(method, path string, params url.Values, body interface{}) ([]byte, error) {
+ fullURL := c.config.BaseURL + path
+ if params != nil && len(params) > 0 {
+ fullURL += "?" + params.Encode()
+ }
+
+ var reqBody io.Reader
+ if body != nil {
+ jsonData, err := json.Marshal(body)
+ if err != nil {
+ return nil, fmt.Errorf("failed to marshal request body: %w", err)
+ }
+ reqBody = bytes.NewBuffer(jsonData)
+ }
+
+ req, err := http.NewRequest(method, fullURL, reqBody)
+ if err != nil {
+ return nil, fmt.Errorf("failed to create request: %w", err)
+ }
+
+ req.Header.Set("X-API-Key", c.config.APIKey)
+ req.Header.Set("User-Agent", c.config.UserAgent)
+ if body != nil {
+ req.Header.Set("Content-Type", "application/json")
+ }
+
+ var resp *http.Response
+ var lastErr error
+
+ for attempt := 0; attempt <= c.config.MaxRetries; attempt++ {
+ if attempt > 0 {
+ time.Sleep(time.Duration(attempt) * time.Second)
+ }
+
+ resp, err = c.httpClient.Do(req)
+ if err != nil {
+ lastErr = err
+ continue
+ }
+
+ defer resp.Body.Close()
+
+ if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusCreated {
+ return io.ReadAll(resp.Body)
+ }
+
+ bodyBytes, _ := io.ReadAll(resp.Body)
+
+ switch resp.StatusCode {
+ case http.StatusForbidden:
+ lastErr = fmt.Errorf("access denied (403): %s - This endpoint requires premium access", string(bodyBytes))
+ case http.StatusUnauthorized:
+ lastErr = fmt.Errorf("unauthorized (401): %s - Check your API key", string(bodyBytes))
+ case http.StatusTooManyRequests:
+ lastErr = fmt.Errorf("rate limit exceeded (429): %s", string(bodyBytes))
+ case http.StatusBadRequest:
+ lastErr = fmt.Errorf("bad request (400): %s", string(bodyBytes))
+ default:
+ lastErr = fmt.Errorf("API request failed with status %d: %s", resp.StatusCode, string(bodyBytes))
+ }
+
+ if resp.StatusCode != http.StatusTooManyRequests && resp.StatusCode < 500 {
+ break
+ }
+ }
+
+ return nil, lastErr
+}
+
+func (c *Client) Get(path string, params url.Values) ([]byte, error) {
+ return c.doRequest(http.MethodGet, path, params, nil)
+}
+
+func (c *Client) Post(path string, body interface{}) ([]byte, error) {
+ return c.doRequest(http.MethodPost, path, nil, body)
+}
+
+func (c *Client) Delete(path string) ([]byte, error) {
+ return c.doRequest(http.MethodDelete, path, nil, nil)
+}
+
+func (c *Client) Patch(path string, body interface{}) ([]byte, error) {
+ return c.doRequest(http.MethodPatch, path, nil, body)
+}
+
+func (c *Client) Put(path string, body interface{}) ([]byte, error) {
+ return c.doRequest(http.MethodPut, path, nil, body)
+}