client.go raw
1 package internal
2
3 import (
4 "bytes"
5 "context"
6 "encoding/json"
7 "errors"
8 "fmt"
9 "io"
10 "net/http"
11 "net/url"
12 "time"
13
14 "github.com/go-acme/lego/v4/providers/dns/internal/errutils"
15 "github.com/go-acme/lego/v4/providers/dns/internal/useragent"
16 )
17
18 const defaultBaseURL = "https://www.syse.no/api"
19
20 // Client the Syse API client.
21 type Client struct {
22 credentials map[string]string
23
24 BaseURL *url.URL
25 HTTPClient *http.Client
26 }
27
28 // NewClient creates a new Client.
29 func NewClient(credentials map[string]string) (*Client, error) {
30 if len(credentials) == 0 {
31 return nil, errors.New("credentials missing")
32 }
33
34 baseURL, _ := url.Parse(defaultBaseURL)
35
36 return &Client{
37 credentials: credentials,
38 BaseURL: baseURL,
39 HTTPClient: &http.Client{Timeout: 10 * time.Second},
40 }, nil
41 }
42
43 func (c *Client) CreateRecord(ctx context.Context, zone string, record Record) (*Record, error) {
44 endpoint := c.BaseURL.JoinPath("dns", zone)
45
46 req, err := newJSONRequest(ctx, http.MethodPost, endpoint, record)
47 if err != nil {
48 return nil, err
49 }
50
51 req.SetBasicAuth(zone, c.credentials[zone])
52
53 result := new(Record)
54
55 err = c.do(req, result)
56 if err != nil {
57 return nil, err
58 }
59
60 return result, nil
61 }
62
63 func (c *Client) DeleteRecord(ctx context.Context, zone, recordID string) error {
64 endpoint := c.BaseURL.JoinPath("dns", zone, recordID)
65
66 req, err := newJSONRequest(ctx, http.MethodDelete, endpoint, nil)
67 if err != nil {
68 return err
69 }
70
71 req.SetBasicAuth(zone, c.credentials[zone])
72
73 return c.do(req, nil)
74 }
75
76 func (c *Client) do(req *http.Request, result any) error {
77 useragent.SetHeader(req.Header)
78
79 resp, err := c.HTTPClient.Do(req)
80 if err != nil {
81 return errutils.NewHTTPDoError(req, err)
82 }
83
84 defer func() { _ = resp.Body.Close() }()
85
86 if resp.StatusCode/100 != 2 {
87 raw, _ := io.ReadAll(resp.Body)
88
89 return errutils.NewUnexpectedStatusCodeError(req, resp.StatusCode, raw)
90 }
91
92 if result == nil {
93 return nil
94 }
95
96 raw, err := io.ReadAll(resp.Body)
97 if err != nil {
98 return errutils.NewReadResponseError(req, resp.StatusCode, err)
99 }
100
101 err = json.Unmarshal(raw, result)
102 if err != nil {
103 return errutils.NewUnmarshalError(req, resp.StatusCode, raw, err)
104 }
105
106 return nil
107 }
108
109 func newJSONRequest(ctx context.Context, method string, endpoint *url.URL, payload any) (*http.Request, error) {
110 buf := new(bytes.Buffer)
111
112 if payload != nil {
113 err := json.NewEncoder(buf).Encode(payload)
114 if err != nil {
115 return nil, fmt.Errorf("failed to create request JSON body: %w", err)
116 }
117 }
118
119 req, err := http.NewRequestWithContext(ctx, method, endpoint.String(), buf)
120 if err != nil {
121 return nil, fmt.Errorf("unable to create request: %w", err)
122 }
123
124 req.Header.Set("Accept", "application/json")
125
126 if payload != nil {
127 req.Header.Set("Content-Type", "application/json")
128 }
129
130 return req, nil
131 }
132