90 lines
1.9 KiB
Go
90 lines
1.9 KiB
Go
package client
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"time"
|
|
|
|
"git.wzray.com/homelab/hivemind/internal/web"
|
|
"git.wzray.com/homelab/hivemind/internal/web/middleware"
|
|
)
|
|
|
|
type Client struct {
|
|
http *http.Client
|
|
middleware middleware.Middleware
|
|
}
|
|
|
|
const timeout = time.Duration(2) * time.Second
|
|
|
|
func (c *Client) Call(host string, path string, data any, out any) error {
|
|
var body io.Reader
|
|
if data != nil {
|
|
raw, err := json.Marshal(data)
|
|
if err != nil {
|
|
return fmt.Errorf("marshal body: %w", err)
|
|
}
|
|
body = bytes.NewReader(raw)
|
|
}
|
|
|
|
uri := (&url.URL{
|
|
Scheme: "http",
|
|
Host: host,
|
|
Path: path,
|
|
}).String()
|
|
|
|
r, err := http.NewRequest("POST", uri, body)
|
|
if err != nil {
|
|
return fmt.Errorf("build http request: %w", err)
|
|
}
|
|
|
|
if body != nil {
|
|
r.Header.Set("Content-Type", "application/json; charset=utf-8")
|
|
}
|
|
|
|
if err := c.middleware.Client(r); err != nil {
|
|
return fmt.Errorf("apply middleware: %w", err)
|
|
}
|
|
|
|
httpResponse, err := c.http.Do(r)
|
|
if err != nil {
|
|
return fmt.Errorf("send request: %w", err)
|
|
}
|
|
defer httpResponse.Body.Close()
|
|
|
|
if httpResponse.StatusCode < 200 || httpResponse.StatusCode >= 300 {
|
|
b, _ := io.ReadAll(httpResponse.Body)
|
|
return fmt.Errorf("http %d: %s", httpResponse.StatusCode, string(b))
|
|
}
|
|
|
|
var resp web.Response[json.RawMessage]
|
|
if err := json.NewDecoder(httpResponse.Body).Decode(&resp); err != nil {
|
|
return fmt.Errorf("decode response wrapper: %w", err)
|
|
}
|
|
|
|
if !resp.Ok {
|
|
return fmt.Errorf("error on the remote: %w", errors.New(resp.Err))
|
|
}
|
|
|
|
if out != nil {
|
|
if err := json.Unmarshal(resp.Data, out); err != nil {
|
|
return fmt.Errorf("decode response body: %w", err)
|
|
}
|
|
}
|
|
|
|
io.Copy(io.Discard, httpResponse.Body)
|
|
return nil
|
|
}
|
|
|
|
func New(middleware middleware.Middleware) *Client {
|
|
return &Client{
|
|
http: &http.Client{
|
|
Timeout: timeout,
|
|
},
|
|
middleware: middleware,
|
|
}
|
|
}
|