mirror of
https://github.com/certimate-go/certimate.git
synced 2026-06-30 21:05:12 +08:00
feat(provider): new acme dns-01 provider: dnsexit
This commit is contained in:
parent
d26b9015cd
commit
7f6041fd3d
27
internal/certacme/certifiers/sp_dnsexit.go
Normal file
27
internal/certacme/certifiers/sp_dnsexit.go
Normal file
@ -0,0 +1,27 @@
|
||||
package certifiers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
|
||||
"github.com/certimate-go/certimate/internal/domain"
|
||||
"github.com/certimate-go/certimate/pkg/core/certifier/challengers/dns01/dnsexit"
|
||||
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
|
||||
)
|
||||
|
||||
func init() {
|
||||
ACMEDns01Registries.MustRegister(domain.ACMEDns01ProviderTypeDNSExit, func(options *ProviderFactoryOptions) (challenge.Provider, error) {
|
||||
credentials := domain.AccessConfigForDNSExit{}
|
||||
if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil {
|
||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||
}
|
||||
|
||||
provider, err := dnsexit.NewChallenger(&dnsexit.ChallengerConfig{
|
||||
ApiKey: credentials.ApiKey,
|
||||
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
||||
DnsTTL: options.DnsTTL,
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
}
|
||||
@ -183,6 +183,10 @@ type AccessConfigForDiscordBot struct {
|
||||
ChannelId string `json:"channelId,omitempty"`
|
||||
}
|
||||
|
||||
type AccessConfigForDNSExit struct {
|
||||
ApiKey string `json:"apiKey"`
|
||||
}
|
||||
|
||||
type AccessConfigForDNSLA struct {
|
||||
ApiId string `json:"apiId"`
|
||||
ApiSecret string `json:"apiSecret"`
|
||||
|
||||
@ -41,6 +41,7 @@ const (
|
||||
AccessProviderTypeDigitalOcean = AccessProviderType("digitalocean")
|
||||
AccessProviderTypeDingTalkBot = AccessProviderType("dingtalkbot")
|
||||
AccessProviderTypeDiscordBot = AccessProviderType("discordbot")
|
||||
AccessProviderTypeDNSExit = AccessProviderType("dnsexit")
|
||||
AccessProviderTypeDNSLA = AccessProviderType("dnsla")
|
||||
AccessProviderTypeDNSMadeEasy = AccessProviderType("dnsmadeeasy")
|
||||
AccessProviderTypeDogeCloud = AccessProviderType("dogecloud")
|
||||
@ -172,6 +173,7 @@ const (
|
||||
ACMEDns01ProviderTypeCTCCCloudSmartDNS = ACMEDns01ProviderType(AccessProviderTypeCTCCCloud + "-smartdns")
|
||||
ACMEDns01ProviderTypeDeSEC = ACMEDns01ProviderType(AccessProviderTypeDeSEC)
|
||||
ACMEDns01ProviderTypeDigitalOcean = ACMEDns01ProviderType(AccessProviderTypeDigitalOcean)
|
||||
ACMEDns01ProviderTypeDNSExit = ACMEDns01ProviderType(AccessProviderTypeDNSExit)
|
||||
ACMEDns01ProviderTypeDNSLA = ACMEDns01ProviderType(AccessProviderTypeDNSLA)
|
||||
ACMEDns01ProviderTypeDNSMadeEasy = ACMEDns01ProviderType(AccessProviderTypeDNSMadeEasy)
|
||||
ACMEDns01ProviderTypeDuckDNS = ACMEDns01ProviderType(AccessProviderTypeDuckDNS)
|
||||
|
||||
@ -42,8 +42,8 @@ type Config struct {
|
||||
}
|
||||
|
||||
type DNSProvider struct {
|
||||
client *ecloudsdkclouddns.Client
|
||||
config *Config
|
||||
client *ecloudsdkclouddns.Client
|
||||
|
||||
recordIDs map[string]string
|
||||
recordIDsMu sync.Mutex
|
||||
@ -88,8 +88,8 @@ func NewDNSProviderConfig(cfg *Config) (*DNSProvider, error) {
|
||||
})
|
||||
|
||||
return &DNSProvider{
|
||||
client: client,
|
||||
config: cfg,
|
||||
client: client,
|
||||
recordIDs: make(map[string]string),
|
||||
recordIDsMu: sync.Mutex{},
|
||||
}, nil
|
||||
|
||||
@ -74,7 +74,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
|
||||
client, err := ctyundns.NewClient(config.AccessKeyId, config.SecretAccessKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("ctyun: %w", err)
|
||||
} else {
|
||||
client.SetTimeout(config.HTTPTimeout)
|
||||
}
|
||||
|
||||
37
pkg/core/certifier/challengers/dns01/dnsexit/dnsexit.go
Normal file
37
pkg/core/certifier/challengers/dns01/dnsexit/dnsexit.go
Normal file
@ -0,0 +1,37 @@
|
||||
package dnsexit
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/certimate-go/certimate/pkg/core/certifier"
|
||||
"github.com/certimate-go/certimate/pkg/core/certifier/challengers/dns01/dnsexit/internal"
|
||||
)
|
||||
|
||||
type ChallengerConfig struct {
|
||||
ApiKey string `json:"apiKey"`
|
||||
DnsPropagationTimeout int `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallenger(config *ChallengerConfig) (certifier.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := internal.NewDefaultConfig()
|
||||
providerConfig.APIKey = config.ApiKey
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = config.DnsTTL
|
||||
}
|
||||
|
||||
provider, err := internal.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
143
pkg/core/certifier/challengers/dns01/dnsexit/internal/lego.go
Normal file
143
pkg/core/certifier/challengers/dns01/dnsexit/internal/lego.go
Normal file
@ -0,0 +1,143 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||
"github.com/go-acme/lego/v4/platform/config/env"
|
||||
"github.com/samber/lo"
|
||||
|
||||
dnsexitsdk "github.com/certimate-go/certimate/pkg/sdk3rd/dnsexit"
|
||||
)
|
||||
|
||||
const (
|
||||
envNamespace = "DNSEXIT_"
|
||||
|
||||
EnvAPIKey = envNamespace + "APIKEY"
|
||||
|
||||
EnvTTL = envNamespace + "TTL"
|
||||
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
|
||||
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
|
||||
EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
|
||||
)
|
||||
|
||||
var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
|
||||
|
||||
type Config struct {
|
||||
APIKey string
|
||||
|
||||
PropagationTimeout time.Duration
|
||||
PollingInterval time.Duration
|
||||
TTL int
|
||||
HTTPTimeout time.Duration
|
||||
}
|
||||
|
||||
func NewDefaultConfig() *Config {
|
||||
return &Config{
|
||||
TTL: env.GetOrDefaultInt(EnvTTL, 0),
|
||||
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, dns01.DefaultPropagationTimeout),
|
||||
PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
|
||||
HTTPTimeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
|
||||
}
|
||||
}
|
||||
|
||||
type DNSProvider struct {
|
||||
config *Config
|
||||
client *dnsexitsdk.Client
|
||||
}
|
||||
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
values, err := env.Get(EnvAPIKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dnsexit: %w", err)
|
||||
}
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.APIKey = values[EnvAPIKey]
|
||||
|
||||
return NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("dnsexit: the configuration of the DNS provider is nil")
|
||||
}
|
||||
|
||||
client, err := dnsexitsdk.NewClient(config.APIKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dnsexit: %w", err)
|
||||
} else {
|
||||
client.SetTimeout(config.HTTPTimeout)
|
||||
}
|
||||
|
||||
return &DNSProvider{
|
||||
config: config,
|
||||
client: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("dnsexit: could not find zone for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("dnsexit: %w", err)
|
||||
}
|
||||
|
||||
// REF: https://dnsexit.com/dns/dns-api/#example-add-new
|
||||
request := &dnsexitsdk.DnsRecordRequest{
|
||||
Domain: lo.ToPtr(dns01.UnFqdn(authZone)),
|
||||
Add: &dnsexitsdk.DnsRecord{
|
||||
Type: lo.ToPtr("TXT"),
|
||||
Name: lo.ToPtr(subDomain),
|
||||
Content: lo.ToPtr(info.Value),
|
||||
TTL: lo.ToPtr(d.config.TTL),
|
||||
Overwrite: lo.ToPtr(true),
|
||||
},
|
||||
}
|
||||
if _, err := d.client.DnsRecord(request); err != nil {
|
||||
return fmt.Errorf("dnsexit: error when create record: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("dnsexit: could not find zone for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("dnsexit: %w", err)
|
||||
}
|
||||
|
||||
// REF: https://dnsexit.com/dns/dns-api/#delete-a-record
|
||||
request := &dnsexitsdk.DnsRecordRequest{
|
||||
Domain: lo.ToPtr(dns01.UnFqdn(authZone)),
|
||||
Delete: &dnsexitsdk.DnsRecord{
|
||||
Type: lo.ToPtr("TXT"),
|
||||
Name: lo.ToPtr(subDomain),
|
||||
},
|
||||
}
|
||||
if _, err := d.client.DnsRecord(request); err != nil {
|
||||
return fmt.Errorf("dnsexit: error when delete record: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||
}
|
||||
@ -40,8 +40,8 @@ type Config struct {
|
||||
}
|
||||
|
||||
type DNSProvider struct {
|
||||
client *dnslasdk.Client
|
||||
config *Config
|
||||
client *dnslasdk.Client
|
||||
|
||||
recordIDs map[string]string
|
||||
recordIDsMu sync.Mutex
|
||||
@ -76,14 +76,14 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
|
||||
client, err := dnslasdk.NewClient(config.APIId, config.APISecret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("dnsla: %w", err)
|
||||
} else {
|
||||
client.SetTimeout(config.HTTPTimeout)
|
||||
}
|
||||
|
||||
return &DNSProvider{
|
||||
client: client,
|
||||
config: config,
|
||||
client: client,
|
||||
recordIDs: make(map[string]string),
|
||||
recordIDsMu: sync.Mutex{},
|
||||
}, nil
|
||||
|
||||
@ -34,8 +34,8 @@ type Config struct {
|
||||
}
|
||||
|
||||
type DNSProvider struct {
|
||||
client *dynv6.Provider
|
||||
config *Config
|
||||
client *dynv6.Provider
|
||||
}
|
||||
|
||||
func NewDefaultConfig() *Config {
|
||||
@ -66,8 +66,8 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
client := &dynv6.Provider{Token: config.HTTPToken}
|
||||
|
||||
return &DNSProvider{
|
||||
client: client,
|
||||
config: config,
|
||||
client: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@ -39,8 +39,8 @@ type Config struct {
|
||||
}
|
||||
|
||||
type DNSProvider struct {
|
||||
client *gnamesdk.Client
|
||||
config *Config
|
||||
client *gnamesdk.Client
|
||||
|
||||
recordIDs map[string]int64
|
||||
recordIDsMu sync.Mutex
|
||||
@ -75,14 +75,14 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
|
||||
client, err := gnamesdk.NewClient(config.AppID, config.AppKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("gname: %w", err)
|
||||
} else {
|
||||
client.SetTimeout(config.HTTPTimeout)
|
||||
}
|
||||
|
||||
return &DNSProvider{
|
||||
client: client,
|
||||
config: config,
|
||||
client: client,
|
||||
recordIDs: make(map[string]int64),
|
||||
recordIDsMu: sync.Mutex{},
|
||||
}, nil
|
||||
|
||||
@ -41,8 +41,8 @@ type Config struct {
|
||||
}
|
||||
|
||||
type DNSProvider struct {
|
||||
client *DomainserviceClient
|
||||
config *Config
|
||||
client *DomainserviceClient
|
||||
|
||||
recordIDs map[string]int
|
||||
recordIDsMu sync.Mutex
|
||||
@ -81,8 +81,8 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
client.Config.SetTimeout(config.HTTPTimeout)
|
||||
|
||||
return &DNSProvider{
|
||||
client: client,
|
||||
config: config,
|
||||
client: client,
|
||||
recordIDs: make(map[string]int),
|
||||
recordIDsMu: sync.Mutex{},
|
||||
}, nil
|
||||
|
||||
@ -39,8 +39,8 @@ type Config struct {
|
||||
}
|
||||
|
||||
type DNSProvider struct {
|
||||
client *qingcloudsdk.Client
|
||||
config *Config
|
||||
client *qingcloudsdk.Client
|
||||
|
||||
recordIDs map[string]*int64
|
||||
recordIDsMu sync.Mutex
|
||||
@ -75,14 +75,14 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
|
||||
client, err := qingcloudsdk.NewClient(config.AccessKey, config.AccessSecret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("qingcloud: %w", err)
|
||||
} else {
|
||||
client.SetTimeout(config.HTTPTimeout)
|
||||
}
|
||||
|
||||
return &DNSProvider{
|
||||
client: client,
|
||||
config: config,
|
||||
client: client,
|
||||
recordIDs: make(map[string]*int64),
|
||||
recordIDsMu: sync.Mutex{},
|
||||
}, nil
|
||||
|
||||
@ -41,8 +41,8 @@ type Config struct {
|
||||
}
|
||||
|
||||
type DNSProvider struct {
|
||||
client *udnr.UDNRClient
|
||||
config *Config
|
||||
client *udnr.UDNRClient
|
||||
}
|
||||
|
||||
func NewDefaultConfig() *Config {
|
||||
@ -81,8 +81,8 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
client := udnr.NewClient(&cfg, &credential)
|
||||
|
||||
return &DNSProvider{
|
||||
client: client,
|
||||
config: config,
|
||||
client: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@ -39,8 +39,8 @@ type Config struct {
|
||||
}
|
||||
|
||||
type DNSProvider struct {
|
||||
client *xinnetsdk.Client
|
||||
config *Config
|
||||
client *xinnetsdk.Client
|
||||
|
||||
recordIDs map[string]*int64
|
||||
recordIDsMu sync.Mutex
|
||||
@ -75,14 +75,14 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
|
||||
client, err := xinnetsdk.NewClient(config.AgentID, config.AppSecret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("xinnet: %w", err)
|
||||
} else {
|
||||
client.SetTimeout(config.HTTPTimeout)
|
||||
}
|
||||
|
||||
return &DNSProvider{
|
||||
client: client,
|
||||
config: config,
|
||||
client: client,
|
||||
recordIDs: make(map[string]*int64),
|
||||
recordIDsMu: sync.Mutex{},
|
||||
}, nil
|
||||
|
||||
@ -196,8 +196,9 @@ func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*dep
|
||||
|
||||
privkey, err := xcert.ParsePrivateKeyFromPEM(privkeyPEM)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse private key: %w", err)
|
||||
return nil, fmt.Errorf("failed to parse private key: %w", err)
|
||||
}
|
||||
|
||||
privkeyAlg, _, _ := xcryptokey.GetPrivateKeyAlgorithm(privkey)
|
||||
privkeyAlgStr := ""
|
||||
switch privkeyAlg {
|
||||
|
||||
40
pkg/sdk3rd/dnsexit/api_dns_record.go
Normal file
40
pkg/sdk3rd/dnsexit/api_dns_record.go
Normal file
@ -0,0 +1,40 @@
|
||||
package dnsexit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type DnsRecordRequest struct {
|
||||
Domain *string `json:"domain,omitempty"`
|
||||
Add *DnsRecord `json:"add,omitempty"`
|
||||
Update *DnsRecord `json:"update,omitempty"`
|
||||
Delete *DnsRecord `json:"delete,omitempty"`
|
||||
}
|
||||
|
||||
type DnsRecordResponse struct {
|
||||
apiResponseBase
|
||||
|
||||
Details []string `json:"details,omitempty"`
|
||||
}
|
||||
|
||||
func (c *Client) DnsRecord(req *DnsRecordRequest) (*DnsRecordResponse, error) {
|
||||
return c.DnsRecordWithContext(context.Background(), req)
|
||||
}
|
||||
|
||||
func (c *Client) DnsRecordWithContext(ctx context.Context, req *DnsRecordRequest) (*DnsRecordResponse, error) {
|
||||
httpreq, err := c.newRequest(http.MethodPost, "/dns/")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
httpreq.SetBody(req)
|
||||
httpreq.SetContext(ctx)
|
||||
}
|
||||
|
||||
result := &DnsRecordResponse{}
|
||||
if _, err := c.doRequestWithResult(httpreq, result); err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
96
pkg/sdk3rd/dnsexit/client.go
Normal file
96
pkg/sdk3rd/dnsexit/client.go
Normal file
@ -0,0 +1,96 @@
|
||||
package dnsexit
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
client *resty.Client
|
||||
}
|
||||
|
||||
func NewClient(apiKey string) (*Client, error) {
|
||||
if apiKey == "" {
|
||||
return nil, fmt.Errorf("sdkerr: unset apiKey")
|
||||
}
|
||||
|
||||
client := resty.New().
|
||||
SetBaseURL("https://api.dnsexit.com").
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", "certimate").
|
||||
SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
|
||||
req.Header.Set("apikey", apiKey)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return &Client{client}, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
c.client.SetTimeout(timeout)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Client) newRequest(method string, path string) (*resty.Request, error) {
|
||||
if method == "" {
|
||||
return nil, fmt.Errorf("sdkerr: unset method")
|
||||
}
|
||||
if path == "" {
|
||||
return nil, fmt.Errorf("sdkerr: unset path")
|
||||
}
|
||||
|
||||
req := c.client.R()
|
||||
req.Method = method
|
||||
req.URL = path
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func (c *Client) doRequest(req *resty.Request) (*resty.Response, error) {
|
||||
if req == nil {
|
||||
return nil, fmt.Errorf("sdkerr: nil request")
|
||||
}
|
||||
|
||||
// WARN:
|
||||
// PLEASE DO NOT USE `req.SetResult` or `req.SetError` HERE! USE `doRequestWithResult` INSTEAD.
|
||||
|
||||
resp, err := req.Send()
|
||||
if err != nil {
|
||||
return resp, fmt.Errorf("sdkerr: failed to send request: %w", err)
|
||||
} else if resp.IsError() {
|
||||
return resp, fmt.Errorf("sdkerr: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *Client) doRequestWithResult(req *resty.Request, res apiResponse) (*resty.Response, error) {
|
||||
if req == nil {
|
||||
return nil, fmt.Errorf("sdkerr: nil request")
|
||||
}
|
||||
|
||||
resp, err := c.doRequest(req)
|
||||
if err != nil {
|
||||
if resp != nil {
|
||||
json.Unmarshal(resp.Body(), &res)
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
|
||||
if len(resp.Body()) != 0 {
|
||||
if err := json.Unmarshal(resp.Body(), &res); err != nil {
|
||||
return resp, fmt.Errorf("sdkerr: failed to unmarshal response: %w", err)
|
||||
} else {
|
||||
if tcode := res.GetCode(); tcode != 0 {
|
||||
return resp, fmt.Errorf("sdkerr: code='%d', message='%s'", tcode, res.GetMessage())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
38
pkg/sdk3rd/dnsexit/types.go
Normal file
38
pkg/sdk3rd/dnsexit/types.go
Normal file
@ -0,0 +1,38 @@
|
||||
package dnsexit
|
||||
|
||||
type apiResponse interface {
|
||||
GetCode() int32
|
||||
GetMessage() string
|
||||
}
|
||||
|
||||
type apiResponseBase struct {
|
||||
Code *int32 `json:"code,omitempty"`
|
||||
Message *string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
func (r *apiResponseBase) GetCode() int32 {
|
||||
if r.Code == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return *r.Code
|
||||
}
|
||||
|
||||
func (r *apiResponseBase) GetMessage() string {
|
||||
if r.Message == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return *r.Message
|
||||
}
|
||||
|
||||
var _ apiResponse = (*apiResponseBase)(nil)
|
||||
|
||||
type DnsRecord struct {
|
||||
Type *string `json:"type,omitempty"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
Content *string `json:"content,omitempty"`
|
||||
TTL *int `json:"ttl,omitempty"`
|
||||
Priority *int `json:"priority,omitempty"`
|
||||
Overwrite *bool `json:"overwrite,omitempty"`
|
||||
}
|
||||
BIN
ui/public/imgs/providers/dnsexit.png
Normal file
BIN
ui/public/imgs/providers/dnsexit.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
@ -33,6 +33,7 @@ import AccessConfigFieldsProviderDeSEC from "./AccessConfigFieldsProviderDeSEC";
|
||||
import AccessConfigFieldsProviderDigitalOcean from "./AccessConfigFieldsProviderDigitalOcean";
|
||||
import AccessConfigFieldsProviderDingTalkBot from "./AccessConfigFieldsProviderDingTalkBot";
|
||||
import AccessConfigFieldsProviderDiscordBot from "./AccessConfigFieldsProviderDiscordBot";
|
||||
import AccessConfigFieldsProviderDNSExit from "./AccessConfigFieldsProviderDNSExit";
|
||||
import AccessConfigFieldsProviderDNSLA from "./AccessConfigFieldsProviderDNSLA";
|
||||
import AccessConfigFieldsProviderDNSMadeEasy from "./AccessConfigFieldsProviderDNSMadeEasy";
|
||||
import AccessConfigFieldsProviderDogeCloud from "./AccessConfigFieldsProviderDogeCloud";
|
||||
@ -137,6 +138,7 @@ const providerComponentMap: Partial<Record<AccessProviderType, React.ComponentTy
|
||||
[ACCESS_PROVIDERS.DIGITALOCEAN]: AccessConfigFieldsProviderDigitalOcean,
|
||||
[ACCESS_PROVIDERS.DINGTALKBOT]: AccessConfigFieldsProviderDingTalkBot,
|
||||
[ACCESS_PROVIDERS.DISCORDBOT]: AccessConfigFieldsProviderDiscordBot,
|
||||
[ACCESS_PROVIDERS.DNSEXIT]: AccessConfigFieldsProviderDNSExit,
|
||||
[ACCESS_PROVIDERS.DNSLA]: AccessConfigFieldsProviderDNSLA,
|
||||
[ACCESS_PROVIDERS.DNSMADEEASY]: AccessConfigFieldsProviderDNSMadeEasy,
|
||||
[ACCESS_PROVIDERS.DOGECLOUD]: AccessConfigFieldsProviderDogeCloud,
|
||||
|
||||
@ -0,0 +1,52 @@
|
||||
import { getI18n, useTranslation } from "react-i18next";
|
||||
import { Form, Input } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import { useFormNestedFieldsContext } from "./_context";
|
||||
|
||||
const AccessConfigFormFieldsProviderDNSExit = () => {
|
||||
const { i18n, t } = useTranslation();
|
||||
|
||||
const { parentNamePath } = useFormNestedFieldsContext();
|
||||
const formSchema = z.object({
|
||||
[parentNamePath]: getSchema({ i18n }),
|
||||
});
|
||||
const formRule = createSchemaFieldRule(formSchema);
|
||||
const initialValues = getInitialValues();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form.Item
|
||||
name={[parentNamePath, "apiKey"]}
|
||||
initialValue={initialValues.apiKey}
|
||||
label={t("access.form.dnsexit_api_key.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.dnsexit_api_key.tooltip") }}></span>}
|
||||
>
|
||||
<Input.Password autoComplete="new-password" placeholder={t("access.form.dnsexit_api_key.placeholder")} />
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
|
||||
return {
|
||||
apiKey: "",
|
||||
};
|
||||
};
|
||||
|
||||
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
|
||||
const { t } = i18n;
|
||||
|
||||
return z.object({
|
||||
apiKey: z.string().nonempty(t("access.form.dnsexit_api_key.placeholder")),
|
||||
});
|
||||
};
|
||||
|
||||
const _default = Object.assign(AccessConfigFormFieldsProviderDNSExit, {
|
||||
getInitialValues,
|
||||
getSchema,
|
||||
});
|
||||
|
||||
export default _default;
|
||||
@ -46,6 +46,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
|
||||
DIGITALOCEAN: "digitalocean",
|
||||
DINGTALKBOT: "dingtalkbot",
|
||||
DISCORDBOT: "discordbot",
|
||||
DNSEXIT: "dnsexit",
|
||||
DNSLA: "dnsla",
|
||||
DNSMADEEASY: "dnsmadeeasy",
|
||||
DOGECLOUD: "dogecloud",
|
||||
@ -79,6 +80,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
|
||||
LITESSL: "litessl",
|
||||
LOCAL: "local",
|
||||
MATTERMOST: "mattermost",
|
||||
MOHUA: "mohua",
|
||||
NAMECHEAP: "namecheap",
|
||||
NAMEDOTCOM: "namedotcom",
|
||||
NAMESILO: "namesilo",
|
||||
@ -115,7 +117,6 @@ export const ACCESS_PROVIDERS = Object.freeze({
|
||||
WESTCN: "westcn",
|
||||
XINNET: "xinnet",
|
||||
ZEROSSL: "zerossl",
|
||||
MOHUA: "mohua",
|
||||
} as const);
|
||||
|
||||
export type AccessProviderType = (typeof ACCESS_PROVIDERS)[keyof typeof ACCESS_PROVIDERS];
|
||||
@ -168,6 +169,7 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv
|
||||
[ACCESS_PROVIDERS.KSYUN, "provider.ksyun", "/imgs/providers/ksyun.svg", [ACCESS_USAGES.HOSTING]],
|
||||
[ACCESS_PROVIDERS.BYTEPLUS, "provider.byteplus", "/imgs/providers/byteplus.svg", [ACCESS_USAGES.HOSTING]],
|
||||
[ACCESS_PROVIDERS.UNICLOUD, "provider.unicloud", "/imgs/providers/unicloud.png", [ACCESS_USAGES.HOSTING]],
|
||||
[ACCESS_PROVIDERS.MOHUA, "provider.mohua", "/imgs/providers/mohua.png", [ACCESS_USAGES.HOSTING]],
|
||||
[ACCESS_PROVIDERS["1PANEL"], "provider.1panel", "/imgs/providers/1panel.svg", [ACCESS_USAGES.HOSTING]],
|
||||
[ACCESS_PROVIDERS.BAOTAPANEL, "provider.baotapanel", "/imgs/providers/baotapanel.svg", [ACCESS_USAGES.HOSTING]],
|
||||
[ACCESS_PROVIDERS.BAOTAPANELGO, "provider.baotapanelgo", "/imgs/providers/baotapanel.svg", [ACCESS_USAGES.HOSTING]],
|
||||
@ -182,7 +184,6 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv
|
||||
[ACCESS_PROVIDERS.APISIX, "provider.apisix", "/imgs/providers/apisix.svg", [ACCESS_USAGES.HOSTING]],
|
||||
[ACCESS_PROVIDERS.KONG, "provider.kong", "/imgs/providers/kong.png", [ACCESS_USAGES.HOSTING]],
|
||||
[ACCESS_PROVIDERS.PROXMOXVE, "provider.proxmoxve", "/imgs/providers/proxmoxve.svg", [ACCESS_USAGES.HOSTING]],
|
||||
[ACCESS_PROVIDERS.MOHUA, "provider.mohua", "/imgs/providers/mohua.png", [ACCESS_USAGES.HOSTING]],
|
||||
|
||||
[ACCESS_PROVIDERS.AKAMAI, "provider.akamai", "/imgs/providers/akamai.svg", [ACCESS_USAGES.DNS]],
|
||||
[ACCESS_PROVIDERS.ARVANCLOUD, "provider.arvancloud", "/imgs/providers/arvancloud.svg", [ACCESS_USAGES.DNS]],
|
||||
@ -192,6 +193,7 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv
|
||||
[ACCESS_PROVIDERS.CONSTELLIX, "provider.constellix", "/imgs/providers/constellix.png", [ACCESS_USAGES.DNS]],
|
||||
[ACCESS_PROVIDERS.DESEC, "provider.desec", "/imgs/providers/desec.svg", [ACCESS_USAGES.DNS]],
|
||||
[ACCESS_PROVIDERS.DIGITALOCEAN, "provider.digitalocean", "/imgs/providers/digitalocean.svg", [ACCESS_USAGES.DNS]],
|
||||
[ACCESS_PROVIDERS.DNSEXIT, "provider.dnsexit", "/imgs/providers/dnsexit.png", [ACCESS_USAGES.DNS]],
|
||||
[ACCESS_PROVIDERS.DNSLA, "provider.dnsla", "/imgs/providers/dnsla.svg", [ACCESS_USAGES.DNS]],
|
||||
[ACCESS_PROVIDERS.DNSMADEEASY, "provider.dnsmadeeasy", "/imgs/providers/dnsmadeeasy.png", [ACCESS_USAGES.DNS]],
|
||||
[ACCESS_PROVIDERS.DUCKDNS, "provider.duckdns", "/imgs/providers/duckdns.png", [ACCESS_USAGES.DNS]],
|
||||
@ -345,6 +347,7 @@ export const ACME_DNS01_PROVIDERS = Object.freeze({
|
||||
CTCCCLOUD_SMARTDNS: `${ACCESS_PROVIDERS.CTCCCLOUD}-smartdns`,
|
||||
DESEC: `${ACCESS_PROVIDERS.DESEC}`,
|
||||
DIGITALOCEAN: `${ACCESS_PROVIDERS.DIGITALOCEAN}`,
|
||||
DNSEXIT: `${ACCESS_PROVIDERS.DNSEXIT}`,
|
||||
DNSLA: `${ACCESS_PROVIDERS.DNSLA}`,
|
||||
DNSMADEEASY: `${ACCESS_PROVIDERS.DNSMADEEASY}`,
|
||||
DUCKDNS: `${ACCESS_PROVIDERS.DUCKDNS}`,
|
||||
@ -422,6 +425,7 @@ export const acmeDns01ProvidersMap: Map<ACMEDns01Provider["type"] | string, ACME
|
||||
[ACME_DNS01_PROVIDERS.CONSTELLIX, "provider.constellix"],
|
||||
[ACME_DNS01_PROVIDERS.DESEC, "provider.desec"],
|
||||
[ACME_DNS01_PROVIDERS.DIGITALOCEAN, "provider.digitalocean"],
|
||||
[ACME_DNS01_PROVIDERS.DNSEXIT, "provider.dnsexit"],
|
||||
[ACME_DNS01_PROVIDERS.DNSLA, "provider.dnsla"],
|
||||
[ACME_DNS01_PROVIDERS.DNSMADEEASY, "provider.dnsmadeeasy"],
|
||||
[ACME_DNS01_PROVIDERS.DUCKDNS, "provider.duckdns"],
|
||||
@ -696,7 +700,6 @@ export const deploymentProvidersMap: Map<DeploymentProvider["type"] | string, De
|
||||
[DEPLOYMENT_PROVIDERS.HUAWEICLOUD_ELB, "provider.huaweicloud.elb", DEPLOYMENT_CATEGORIES.LOADBALANCE],
|
||||
[DEPLOYMENT_PROVIDERS.HUAWEICLOUD_WAF, "provider.huaweicloud.waf", DEPLOYMENT_CATEGORIES.FIREWALL],
|
||||
[DEPLOYMENT_PROVIDERS.HUAWEICLOUD_SCM, "provider.huaweicloud.scm_upload", DEPLOYMENT_CATEGORIES.SSL],
|
||||
[DEPLOYMENT_PROVIDERS.MOHUA_MVH, "provider.mohua.mvh", DEPLOYMENT_CATEGORIES.WEBSITE],
|
||||
[DEPLOYMENT_PROVIDERS.VOLCENGINE_TOS, "provider.volcengine.tos", DEPLOYMENT_CATEGORIES.STORAGE],
|
||||
[DEPLOYMENT_PROVIDERS.VOLCENGINE_CDN, "provider.volcengine.cdn", DEPLOYMENT_CATEGORIES.CDN],
|
||||
[DEPLOYMENT_PROVIDERS.VOLCENGINE_DCDN, "provider.volcengine.dcdn", DEPLOYMENT_CATEGORIES.CDN],
|
||||
@ -738,6 +741,7 @@ export const deploymentProvidersMap: Map<DeploymentProvider["type"] | string, De
|
||||
[DEPLOYMENT_PROVIDERS.BUNNY_CDN, "provider.bunny.cdn", DEPLOYMENT_CATEGORIES.CDN],
|
||||
[DEPLOYMENT_PROVIDERS.CACHEFLY, "provider.cachefly", DEPLOYMENT_CATEGORIES.CDN],
|
||||
[DEPLOYMENT_PROVIDERS.GCORE_CDN, "provider.gcore.cdn", DEPLOYMENT_CATEGORIES.CDN],
|
||||
[DEPLOYMENT_PROVIDERS.MOHUA_MVH, "provider.mohua.mvh", DEPLOYMENT_CATEGORIES.WEBSITE],
|
||||
[DEPLOYMENT_PROVIDERS.NETLIFY_SITE, "provider.netlify.site", DEPLOYMENT_CATEGORIES.WEBSITE],
|
||||
[DEPLOYMENT_PROVIDERS.CDNFLY, "provider.cdnfly", DEPLOYMENT_CATEGORIES.CDN],
|
||||
[DEPLOYMENT_PROVIDERS.FLEXCDN, "provider.flexcdn", DEPLOYMENT_CATEGORIES.CDN],
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
{
|
||||
{
|
||||
"provider.1panel": "1Panel",
|
||||
"provider.1panel.console": "1Panel - Console itself",
|
||||
"provider.1panel.site": "1Panel - Website",
|
||||
@ -80,6 +80,7 @@
|
||||
"provider.digitalocean": "DigitalOcean",
|
||||
"provider.dingtalkbot": "DingTalk Bot",
|
||||
"provider.discordbot": "Discord Bot",
|
||||
"provider.dnsexit": "DNSExit",
|
||||
"provider.dnsla": "DNS.LA",
|
||||
"provider.dnsmadeeasy": "DNS Made Easy",
|
||||
"provider.dogecloud.cdn": "Doge Cloud - CDN (Content Delivery Network)",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -239,6 +239,9 @@
|
||||
"access.form.dnsmadeeasy_api_secret.label": "DNS Made Easy API Secret",
|
||||
"access.form.dnsmadeeasy_api_secret.placeholder": "请输入 DNS Made Easy API Secret",
|
||||
"access.form.dnsmadeeasy_api_secret.tooltip": "这是什么?请参阅 <a href=\"https://api-docs.dnsmadeeasy.com/#5b98221f-37e9-4845-a349-5e959241b4a5\" target=\"_blank\">https://api-docs.dnsmadeeasy.com/#Authentication</a>",
|
||||
"access.form.dnsexit_api_key.label": "DNSExit API Key",
|
||||
"access.form.dnsexit_api_key.placeholder": "请输入 DNSExit API Key",
|
||||
"access.form.dnsexit_api_key.tooltip": "这是什么?请参阅 <a href=\"https://dnsexit.com/Direct.sv?cmd=userApiKey\" target=\"_blank\">https://dnsexit.com/Direct.sv?cmd=userApiKey</a>",
|
||||
"access.form.dnsla_api_id.label": "DNS.LA API ID",
|
||||
"access.form.dnsla_api_id.placeholder": "请输入 DNS.LA API ID",
|
||||
"access.form.dnsla_api_id.tooltip": "这是什么?请参阅 <a href=\"https://www.dns.la/docs/ApiDoc\" target=\"_blank\">https://www.dns.la/docs/ApiDoc</a>",
|
||||
|
||||
@ -80,6 +80,7 @@
|
||||
"provider.digitalocean": "DigitalOcean",
|
||||
"provider.dingtalkbot": "钉钉群机器人",
|
||||
"provider.discordbot": "Discord 机器人",
|
||||
"provider.dnsexit": "DNSExit",
|
||||
"provider.dnsla": "DNS.LA",
|
||||
"provider.dnsmadeeasy": "DNS Made Easy",
|
||||
"provider.dogecloud": "多吉云",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user