Skip to content

Commit a25218d

Browse files
authored
Add DNS provider for Spaceship (#2406)
1 parent c0260c1 commit a25218d

File tree

13 files changed

+776
-8
lines changed

13 files changed

+776
-8
lines changed

README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -204,39 +204,39 @@ Detailed documentation is available [here](https://go-acme.github.io/lego/dns).
204204
<td><a href="https://go-acme.github.io/lego/dns/simply/">Simply.com</a></td>
205205
</tr><tr>
206206
<td><a href="https://go-acme.github.io/lego/dns/sonic/">Sonic</a></td>
207+
<td><a href="https://go-acme.github.io/lego/dns/spaceship/">Spaceship</a></td>
207208
<td><a href="https://go-acme.github.io/lego/dns/stackpath/">Stackpath</a></td>
208209
<td><a href="https://go-acme.github.io/lego/dns/technitium/">Technitium</a></td>
209-
<td><a href="https://go-acme.github.io/lego/dns/tencentcloud/">Tencent Cloud DNS</a></td>
210210
</tr><tr>
211+
<td><a href="https://go-acme.github.io/lego/dns/tencentcloud/">Tencent Cloud DNS</a></td>
211212
<td><a href="https://go-acme.github.io/lego/dns/timewebcloud/">Timeweb Cloud</a></td>
212213
<td><a href="https://go-acme.github.io/lego/dns/transip/">TransIP</a></td>
213214
<td><a href="https://go-acme.github.io/lego/dns/safedns/">UKFast SafeDNS</a></td>
214-
<td><a href="https://go-acme.github.io/lego/dns/ultradns/">Ultradns</a></td>
215215
</tr><tr>
216+
<td><a href="https://go-acme.github.io/lego/dns/ultradns/">Ultradns</a></td>
216217
<td><a href="https://go-acme.github.io/lego/dns/variomedia/">Variomedia</a></td>
217218
<td><a href="https://go-acme.github.io/lego/dns/vegadns/">VegaDNS</a></td>
218219
<td><a href="https://go-acme.github.io/lego/dns/vercel/">Vercel</a></td>
219-
<td><a href="https://go-acme.github.io/lego/dns/versio/">Versio.[nl|eu|uk]</a></td>
220220
</tr><tr>
221+
<td><a href="https://go-acme.github.io/lego/dns/versio/">Versio.[nl|eu|uk]</a></td>
221222
<td><a href="https://go-acme.github.io/lego/dns/vinyldns/">VinylDNS</a></td>
222223
<td><a href="https://go-acme.github.io/lego/dns/vkcloud/">VK Cloud</a></td>
223224
<td><a href="https://go-acme.github.io/lego/dns/volcengine/">Volcano Engine/火山引擎</a></td>
224-
<td><a href="https://go-acme.github.io/lego/dns/vscale/">Vscale</a></td>
225225
</tr><tr>
226+
<td><a href="https://go-acme.github.io/lego/dns/vscale/">Vscale</a></td>
226227
<td><a href="https://go-acme.github.io/lego/dns/vultr/">Vultr</a></td>
227228
<td><a href="https://go-acme.github.io/lego/dns/webnames/">Webnames</a></td>
228229
<td><a href="https://go-acme.github.io/lego/dns/websupport/">Websupport</a></td>
229-
<td><a href="https://go-acme.github.io/lego/dns/wedos/">WEDOS</a></td>
230230
</tr><tr>
231+
<td><a href="https://go-acme.github.io/lego/dns/wedos/">WEDOS</a></td>
231232
<td><a href="https://go-acme.github.io/lego/dns/westcn/">West.cn/西部数码</a></td>
232233
<td><a href="https://go-acme.github.io/lego/dns/yandex360/">Yandex 360</a></td>
233234
<td><a href="https://go-acme.github.io/lego/dns/yandexcloud/">Yandex Cloud</a></td>
234-
<td><a href="https://go-acme.github.io/lego/dns/yandex/">Yandex PDD</a></td>
235235
</tr><tr>
236+
<td><a href="https://go-acme.github.io/lego/dns/yandex/">Yandex PDD</a></td>
236237
<td><a href="https://go-acme.github.io/lego/dns/zoneee/">Zone.ee</a></td>
237238
<td><a href="https://go-acme.github.io/lego/dns/zonomi/">Zonomi</a></td>
238239
<td></td>
239-
<td></td>
240240
</tr></table>
241241

242242
<!-- END DNS PROVIDERS LIST -->

cmd/zz_gen_cmd_dnshelp.go

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/content/dns/zz_gen_spaceship.md

Lines changed: 69 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/data/zz_cli_help.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ To display the documentation for a specific DNS provider, run:
148148
$ lego dnshelp -c code
149149
150150
Supported DNS providers:
151-
acme-dns, alidns, allinkl, arvancloud, auroradns, autodns, azure, azuredns, bindman, bluecat, brandit, bunny, checkdomain, civo, clouddns, cloudflare, cloudns, cloudru, cloudxns, conoha, constellix, corenetworks, cpanel, derak, desec, designate, digitalocean, directadmin, dnshomede, dnsimple, dnsmadeeasy, dnspod, dode, domeneshop, dreamhost, duckdns, dyn, dynu, easydns, edgedns, efficientip, epik, exec, exoscale, freemyip, gandi, gandiv5, gcloud, gcore, glesys, godaddy, googledomains, hetzner, hostingde, hosttech, httpnet, httpreq, huaweicloud, hurricane, hyperone, ibmcloud, iij, iijdpf, infoblox, infomaniak, internetbs, inwx, ionos, ipv64, iwantmyname, joker, liara, lightsail, limacity, linode, liquidweb, loopia, luadns, mailinabox, manageengine, manual, metaname, mijnhost, mittwald, myaddr, mydnsjp, mythicbeasts, namecheap, namedotcom, namesilo, nearlyfreespeech, netcup, netlify, nicmanager, nifcloud, njalla, nodion, ns1, oraclecloud, otc, ovh, pdns, plesk, porkbun, rackspace, rainyun, rcodezero, regfish, regru, rfc2136, rimuhosting, route53, safedns, sakuracloud, scaleway, selectel, selectelv2, selfhostde, servercow, shellrent, simply, sonic, stackpath, technitium, tencentcloud, timewebcloud, transip, ultradns, variomedia, vegadns, vercel, versio, vinyldns, vkcloud, volcengine, vscale, vultr, webnames, websupport, wedos, westcn, yandex, yandex360, yandexcloud, zoneee, zonomi
151+
acme-dns, alidns, allinkl, arvancloud, auroradns, autodns, azure, azuredns, bindman, bluecat, brandit, bunny, checkdomain, civo, clouddns, cloudflare, cloudns, cloudru, cloudxns, conoha, constellix, corenetworks, cpanel, derak, desec, designate, digitalocean, directadmin, dnshomede, dnsimple, dnsmadeeasy, dnspod, dode, domeneshop, dreamhost, duckdns, dyn, dynu, easydns, edgedns, efficientip, epik, exec, exoscale, freemyip, gandi, gandiv5, gcloud, gcore, glesys, godaddy, googledomains, hetzner, hostingde, hosttech, httpnet, httpreq, huaweicloud, hurricane, hyperone, ibmcloud, iij, iijdpf, infoblox, infomaniak, internetbs, inwx, ionos, ipv64, iwantmyname, joker, liara, lightsail, limacity, linode, liquidweb, loopia, luadns, mailinabox, manageengine, manual, metaname, mijnhost, mittwald, myaddr, mydnsjp, mythicbeasts, namecheap, namedotcom, namesilo, nearlyfreespeech, netcup, netlify, nicmanager, nifcloud, njalla, nodion, ns1, oraclecloud, otc, ovh, pdns, plesk, porkbun, rackspace, rainyun, rcodezero, regfish, regru, rfc2136, rimuhosting, route53, safedns, sakuracloud, scaleway, selectel, selectelv2, selfhostde, servercow, shellrent, simply, sonic, spaceship, stackpath, technitium, tencentcloud, timewebcloud, transip, ultradns, variomedia, vegadns, vercel, versio, vinyldns, vkcloud, volcengine, vscale, vultr, webnames, websupport, wedos, westcn, yandex, yandex360, yandexcloud, zoneee, zonomi
152152
153153
More information: https://go-acme.github.io/lego/dns
154154
"""
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
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+
)
16+
17+
const defaultBaseURL = "https://spaceship.dev/api/v1/"
18+
19+
// Client the Spaceship API client.
20+
type Client struct {
21+
apiKey string
22+
apiSecret string
23+
24+
baseURL *url.URL
25+
HTTPClient *http.Client
26+
}
27+
28+
// NewClient creates a new Client.
29+
func NewClient(apiKey, apiSecret string) (*Client, error) {
30+
if apiKey == "" || apiSecret == "" {
31+
return nil, errors.New("credentials missing")
32+
}
33+
34+
baseURL, _ := url.Parse(defaultBaseURL)
35+
36+
return &Client{
37+
apiKey: apiKey,
38+
apiSecret: apiSecret,
39+
baseURL: baseURL,
40+
HTTPClient: &http.Client{Timeout: 10 * time.Second},
41+
}, nil
42+
}
43+
44+
func (c *Client) do(req *http.Request, result any) error {
45+
req.Header.Add("X-Api-Secret", c.apiSecret)
46+
req.Header.Add("X-Api-Key", c.apiKey)
47+
48+
resp, err := c.HTTPClient.Do(req)
49+
if err != nil {
50+
return errutils.NewHTTPDoError(req, err)
51+
}
52+
53+
defer func() { _ = resp.Body.Close() }()
54+
55+
if resp.StatusCode/100 != 2 {
56+
return parseError(req, resp)
57+
}
58+
59+
if result == nil {
60+
return nil
61+
}
62+
63+
raw, err := io.ReadAll(resp.Body)
64+
if err != nil {
65+
return errutils.NewReadResponseError(req, resp.StatusCode, err)
66+
}
67+
68+
err = json.Unmarshal(raw, result)
69+
if err != nil {
70+
return errutils.NewUnmarshalError(req, resp.StatusCode, raw, err)
71+
}
72+
73+
return nil
74+
}
75+
76+
func (c *Client) AddRecord(ctx context.Context, domain string, record Record) error {
77+
endpoint := c.baseURL.JoinPath("dns", "records", domain)
78+
79+
req, err := newJSONRequest(ctx, http.MethodPut, endpoint, Foo{Items: []Record{record}})
80+
if err != nil {
81+
return err
82+
}
83+
84+
err = c.do(req, nil)
85+
if err != nil {
86+
return err
87+
}
88+
89+
return nil
90+
}
91+
92+
func (c *Client) DeleteRecord(ctx context.Context, domain string, record Record) error {
93+
endpoint := c.baseURL.JoinPath("dns", "records", domain)
94+
95+
req, err := newJSONRequest(ctx, http.MethodDelete, endpoint, []Record{record})
96+
if err != nil {
97+
return err
98+
}
99+
100+
err = c.do(req, nil)
101+
if err != nil {
102+
return err
103+
}
104+
105+
return nil
106+
}
107+
108+
func (c *Client) GetRecords(ctx context.Context, domain string) ([]Record, error) {
109+
endpoint := c.baseURL.JoinPath("dns", "records", domain)
110+
111+
req, err := newJSONRequest(ctx, http.MethodGet, endpoint, nil)
112+
if err != nil {
113+
return nil, err
114+
}
115+
116+
var result GetRecordsResponse
117+
err = c.do(req, &result)
118+
if err != nil {
119+
return nil, err
120+
}
121+
122+
return result.Items, nil
123+
}
124+
125+
func newJSONRequest(ctx context.Context, method string, endpoint *url.URL, payload any) (*http.Request, error) {
126+
buf := new(bytes.Buffer)
127+
128+
if payload != nil {
129+
err := json.NewEncoder(buf).Encode(payload)
130+
if err != nil {
131+
return nil, fmt.Errorf("failed to create request JSON body: %w", err)
132+
}
133+
}
134+
135+
req, err := http.NewRequestWithContext(ctx, method, endpoint.String(), buf)
136+
if err != nil {
137+
return nil, fmt.Errorf("unable to create request: %w", err)
138+
}
139+
140+
req.Header.Set("Accept", "application/json")
141+
142+
if payload != nil {
143+
req.Header.Set("Content-Type", "application/json")
144+
}
145+
146+
return req, nil
147+
}
148+
149+
func parseError(req *http.Request, resp *http.Response) error {
150+
raw, _ := io.ReadAll(resp.Body)
151+
152+
var errAPI APIError
153+
err := json.Unmarshal(raw, &errAPI)
154+
if err != nil {
155+
return errutils.NewUnexpectedStatusCodeError(req, resp.StatusCode, raw)
156+
}
157+
158+
return &errAPI
159+
}

0 commit comments

Comments
 (0)