mirror of
https://github.com/usual2970/certimate.git
synced 2025-06-08 13:39:53 +00:00
feat: add dns.la dns-01 applicant
This commit is contained in:
parent
316a3c950f
commit
f81fa2eb63
@ -97,7 +97,8 @@ make local.run
|
||||
| [AWS Route53](https://aws.amazon.com/route53/) | |
|
||||
| [Azure](https://azure.microsoft.com/) | |
|
||||
| [CloudFlare](https://www.cloudflare.com/) | |
|
||||
| [ClouDNS](https://www.cloudns.net//) | |
|
||||
| [ClouDNS](https://www.cloudns.net/) | |
|
||||
| [DNS.LA](https://www.dns.la/) | |
|
||||
| [Gcore](https://gcore.com/) | |
|
||||
| [GNAME](https://www.gname.com/) | |
|
||||
| [GoDaddy](https://www.godaddy.com/) | |
|
||||
|
@ -96,7 +96,8 @@ The following DNS providers are supported:
|
||||
| [AWS Route53](https://aws.amazon.com/route53/) | |
|
||||
| [Azure DNS](https://azure.microsoft.com/) | |
|
||||
| [CloudFlare](https://www.cloudflare.com/) | |
|
||||
| [ClouDNS](https://www.cloudns.net//) | |
|
||||
| [ClouDNS](https://www.cloudns.net/) | |
|
||||
| [DNS.LA](https://www.dns.la/) | |
|
||||
| [Gcore](https://gcore.com/) | |
|
||||
| [GNAME](https://www.gname.com/) | |
|
||||
| [GoDaddy](https://www.godaddy.com/) | |
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
pCloudflare "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudflare"
|
||||
pClouDNS "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudns"
|
||||
pCMCCCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cmcccloud"
|
||||
pDNSLA "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/dnsla"
|
||||
pGcore "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/gcore"
|
||||
pGname "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/gname"
|
||||
pGoDaddy "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/godaddy"
|
||||
@ -169,6 +170,22 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
||||
return applicant, err
|
||||
}
|
||||
|
||||
case domain.ApplyDNSProviderTypeDNSLA:
|
||||
{
|
||||
access := domain.AccessConfigForDNSLA{}
|
||||
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||
}
|
||||
|
||||
applicant, err := pDNSLA.NewChallengeProvider(&pDNSLA.ChallengeProviderConfig{
|
||||
ApiId: access.ApiId,
|
||||
ApiSecret: access.ApiSecret,
|
||||
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
||||
DnsTTL: options.DnsTTL,
|
||||
})
|
||||
return applicant, err
|
||||
}
|
||||
|
||||
case domain.ApplyDNSProviderTypeGcore:
|
||||
{
|
||||
access := domain.AccessConfigForGcore{}
|
||||
@ -259,7 +276,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
||||
|
||||
applicant, err := pNamecheap.NewChallengeProvider(&pNamecheap.ChallengeProviderConfig{
|
||||
Username: access.Username,
|
||||
ApiKey: access.ApiKey,
|
||||
ApiKey: access.ApiKey,
|
||||
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
||||
DnsTTL: options.DnsTTL,
|
||||
})
|
||||
|
@ -91,6 +91,11 @@ type AccessConfigForCMCCCloud struct {
|
||||
AccessKeySecret string `json:"accessKeySecret"`
|
||||
}
|
||||
|
||||
type AccessConfigForDNSLA struct {
|
||||
ApiId string `json:"apiId"`
|
||||
ApiSecret string `json:"apiSecret"`
|
||||
}
|
||||
|
||||
type AccessConfigForDogeCloud struct {
|
||||
AccessKey string `json:"accessKey"`
|
||||
SecretKey string `json:"secretKey"`
|
||||
|
@ -26,7 +26,7 @@ const (
|
||||
AccessProviderTypeCMCCCloud = AccessProviderType("cmcccloud")
|
||||
AccessProviderTypeCTCCCloud = AccessProviderType("ctcccloud") // 联通云(预留)
|
||||
AccessProviderTypeCUCCCloud = AccessProviderType("cucccloud") // 天翼云(预留)
|
||||
AccessProviderTypeDNSLA = AccessProviderType("dnsla") // DNS.LA(预留)
|
||||
AccessProviderTypeDNSLA = AccessProviderType("dnsla")
|
||||
AccessProviderTypeDogeCloud = AccessProviderType("dogecloud")
|
||||
AccessProviderTypeEdgio = AccessProviderType("edgio")
|
||||
AccessProviderTypeFastly = AccessProviderType("fastly") // Fastly(预留)
|
||||
@ -77,6 +77,7 @@ const (
|
||||
ApplyDNSProviderTypeCloudflare = ApplyDNSProviderType("cloudflare")
|
||||
ApplyDNSProviderTypeClouDNS = ApplyDNSProviderType("cloudns")
|
||||
ApplyDNSProviderTypeCMCCCloud = ApplyDNSProviderType("cmcccloud")
|
||||
ApplyDNSProviderTypeDNSLA = ApplyDNSProviderType("dnsla")
|
||||
ApplyDNSProviderTypeGcore = ApplyDNSProviderType("gcore")
|
||||
ApplyDNSProviderTypeGname = ApplyDNSProviderType("gname")
|
||||
ApplyDNSProviderTypeGoDaddy = ApplyDNSProviderType("godaddy")
|
||||
|
@ -0,0 +1,39 @@
|
||||
package dnsla
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
|
||||
internal "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/dnsla/internal"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
ApiId string `json:"apiId"`
|
||||
ApiSecret string `json:"apiSecret"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) {
|
||||
if config == nil {
|
||||
panic("config is nil")
|
||||
}
|
||||
|
||||
providerConfig := internal.NewDefaultConfig()
|
||||
providerConfig.APIId = config.ApiId
|
||||
providerConfig.APISecret = config.ApiSecret
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
|
||||
provider, err := internal.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
@ -0,0 +1,240 @@
|
||||
package lego_dnsla
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"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"
|
||||
|
||||
dnslasdk "github.com/usual2970/certimate/internal/pkg/vendors/dnsla-sdk"
|
||||
)
|
||||
|
||||
const (
|
||||
envNamespace = "DNSLA_"
|
||||
|
||||
EnvAPIId = envNamespace + "API_ID"
|
||||
EnvAPISecret = envNamespace + "API_KEY"
|
||||
|
||||
EnvTTL = envNamespace + "TTL"
|
||||
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
|
||||
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
|
||||
EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
|
||||
)
|
||||
|
||||
var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
|
||||
|
||||
type Config struct {
|
||||
APIId string
|
||||
APISecret string
|
||||
|
||||
PropagationTimeout time.Duration
|
||||
PollingInterval time.Duration
|
||||
TTL int
|
||||
HTTPTimeout time.Duration
|
||||
}
|
||||
|
||||
type DNSProvider struct {
|
||||
client *dnslasdk.Client
|
||||
config *Config
|
||||
}
|
||||
|
||||
func NewDefaultConfig() *Config {
|
||||
return &Config{
|
||||
TTL: env.GetOrDefaultInt(EnvTTL, 300),
|
||||
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute),
|
||||
PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
|
||||
HTTPTimeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
|
||||
}
|
||||
}
|
||||
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
values, err := env.Get(EnvAPIId, EnvAPISecret)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dnsla: %w", err)
|
||||
}
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.APIId = values[EnvAPIId]
|
||||
config.APISecret = values[EnvAPISecret]
|
||||
|
||||
return NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("dnsla: the configuration of the DNS provider is nil")
|
||||
}
|
||||
|
||||
client := dnslasdk.NewClient(config.APIId, config.APISecret).
|
||||
WithTimeout(config.HTTPTimeout)
|
||||
|
||||
return &DNSProvider{
|
||||
client: client,
|
||||
config: config,
|
||||
}, 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("dnsla: %w", err)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("dnsla: %w", err)
|
||||
}
|
||||
|
||||
if err := d.addOrUpdateDNSRecord(dns01.UnFqdn(authZone), subDomain, info.Value); err != nil {
|
||||
return fmt.Errorf("dnsla: %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("dnsla: %w", err)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("dnsla: %w", err)
|
||||
}
|
||||
|
||||
if err := d.removeDNSRecord(dns01.UnFqdn(authZone), subDomain); err != nil {
|
||||
return fmt.Errorf("dnsla: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||
}
|
||||
|
||||
func (d *DNSProvider) getDNSZone(zoneName string) (*dnslasdk.DomainInfo, error) {
|
||||
pageIndex := 1
|
||||
pageSize := 100
|
||||
for {
|
||||
request := &dnslasdk.ListDomainsRequest{
|
||||
PageIndex: int32(pageIndex),
|
||||
PageSize: int32(pageSize),
|
||||
}
|
||||
response, err := d.client.ListDomains(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if response.Data != nil {
|
||||
for _, item := range response.Data.Results {
|
||||
if strings.TrimRight(item.Domain, ".") == zoneName || strings.TrimRight(item.DisplayDomain, ".") == zoneName {
|
||||
return item, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if response.Data == nil || len(response.Data.Results) < pageSize {
|
||||
break
|
||||
}
|
||||
|
||||
pageIndex++
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("dnsla: zone %s not found", zoneName)
|
||||
}
|
||||
|
||||
func (d *DNSProvider) getDNSZoneAndRecord(zoneName, subDomain string) (*dnslasdk.DomainInfo, *dnslasdk.RecordInfo, error) {
|
||||
zone, err := d.getDNSZone(zoneName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pageIndex := 1
|
||||
pageSize := 100
|
||||
for {
|
||||
request := &dnslasdk.ListRecordsRequest{
|
||||
DomainId: zone.Id,
|
||||
Host: &subDomain,
|
||||
PageIndex: int32(pageIndex),
|
||||
PageSize: int32(pageSize),
|
||||
}
|
||||
response, err := d.client.ListRecords(request)
|
||||
if err != nil {
|
||||
return zone, nil, err
|
||||
}
|
||||
|
||||
if response.Data != nil {
|
||||
for _, record := range response.Data.Results {
|
||||
if record.Type == 16 && (record.Host == subDomain || record.DisplayHost == subDomain) {
|
||||
return zone, record, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if response.Data == nil || len(response.Data.Results) < pageSize {
|
||||
break
|
||||
}
|
||||
|
||||
pageIndex++
|
||||
}
|
||||
|
||||
return zone, nil, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) error {
|
||||
zone, record, err := d.getDNSZoneAndRecord(zoneName, subDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
request := &dnslasdk.CreateRecordRequest{
|
||||
DomainId: zone.Id,
|
||||
Type: 16,
|
||||
Host: subDomain,
|
||||
Data: value,
|
||||
Ttl: int32(d.config.TTL),
|
||||
}
|
||||
_, err := d.client.CreateRecord(request)
|
||||
return err
|
||||
} else {
|
||||
reqType := int32(16)
|
||||
reqTtl := int32(d.config.TTL)
|
||||
request := &dnslasdk.UpdateRecordRequest{
|
||||
Id: record.Id,
|
||||
Type: &reqType,
|
||||
Host: &subDomain,
|
||||
Data: &value,
|
||||
Ttl: &reqTtl,
|
||||
}
|
||||
_, err := d.client.UpdateRecord(request)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error {
|
||||
_, record, err := d.getDNSZoneAndRecord(zoneName, subDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
return nil
|
||||
} else {
|
||||
request := &dnslasdk.DeleteRecordRequest{
|
||||
Id: record.Id,
|
||||
}
|
||||
_, err = d.client.DeleteRecord(request)
|
||||
return err
|
||||
}
|
||||
}
|
@ -43,7 +43,7 @@ type DNSProvider struct {
|
||||
|
||||
func NewDefaultConfig() *Config {
|
||||
return &Config{
|
||||
TTL: env.GetOrDefaultInt(EnvTTL, dns01.DefaultTTL),
|
||||
TTL: env.GetOrDefaultInt(EnvTTL, 300),
|
||||
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute),
|
||||
PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
|
||||
HTTPTimeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
|
||||
|
@ -130,12 +130,12 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||
}
|
||||
|
||||
func (d *DNSProvider) getDNSZone(domain string) (*jdDnsModel.DomainInfo, error) {
|
||||
func (d *DNSProvider) getDNSZone(zoneName string) (*jdDnsModel.DomainInfo, error) {
|
||||
pageNumber := 1
|
||||
pageSize := 10
|
||||
for {
|
||||
request := jdDnsApi.NewDescribeDomainsRequest(d.config.RegionId, pageNumber, pageSize)
|
||||
request.SetDomainName(domain)
|
||||
request.SetDomainName(zoneName)
|
||||
|
||||
response, err := d.client.DescribeDomains(request)
|
||||
if err != nil {
|
||||
@ -143,7 +143,7 @@ func (d *DNSProvider) getDNSZone(domain string) (*jdDnsModel.DomainInfo, error)
|
||||
}
|
||||
|
||||
for _, item := range response.Result.DataList {
|
||||
if item.DomainName == domain {
|
||||
if item.DomainName == zoneName {
|
||||
return &item, nil
|
||||
}
|
||||
}
|
||||
@ -155,7 +155,7 @@ func (d *DNSProvider) getDNSZone(domain string) (*jdDnsModel.DomainInfo, error)
|
||||
pageNumber++
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("jdcloud: zone %s not found", domain)
|
||||
return nil, fmt.Errorf("jdcloud: zone %s not found", zoneName)
|
||||
}
|
||||
|
||||
func (d *DNSProvider) getDNSZoneAndRecord(zoneName, subDomain string) (*jdDnsModel.DomainInfo, *jdDnsModel.RRInfo, error) {
|
||||
|
3
internal/pkg/vendors/baishan-sdk/client.go
vendored
3
internal/pkg/vendors/baishan-sdk/client.go
vendored
@ -12,7 +12,8 @@ import (
|
||||
|
||||
type Client struct {
|
||||
apiToken string
|
||||
client *resty.Client
|
||||
|
||||
client *resty.Client
|
||||
}
|
||||
|
||||
func NewClient(apiToken string) *Client {
|
||||
|
3
internal/pkg/vendors/cachefly-sdk/client.go
vendored
3
internal/pkg/vendors/cachefly-sdk/client.go
vendored
@ -12,7 +12,8 @@ import (
|
||||
|
||||
type Client struct {
|
||||
apiToken string
|
||||
client *resty.Client
|
||||
|
||||
client *resty.Client
|
||||
}
|
||||
|
||||
func NewClient(apiToken string) *Client {
|
||||
|
52
internal/pkg/vendors/dnsla-sdk/api.go
vendored
Normal file
52
internal/pkg/vendors/dnsla-sdk/api.go
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
package dnslasdk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
func (c *Client) ListDomains(req *ListDomainsRequest) (*ListDomainsResponse, error) {
|
||||
resp := ListDomainsResponse{}
|
||||
err := c.sendRequestWithResult(http.MethodGet, "/domainList", req, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *Client) ListRecords(req *ListRecordsRequest) (*ListRecordsResponse, error) {
|
||||
resp := ListRecordsResponse{}
|
||||
err := c.sendRequestWithResult(http.MethodGet, "/recordList", req, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *Client) CreateRecord(req *CreateRecordRequest) (*CreateRecordResponse, error) {
|
||||
resp := CreateRecordResponse{}
|
||||
err := c.sendRequestWithResult(http.MethodPost, "/record", req, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *Client) UpdateRecord(req *UpdateRecordRequest) (*UpdateRecordResponse, error) {
|
||||
resp := UpdateRecordResponse{}
|
||||
err := c.sendRequestWithResult(http.MethodPut, "/record", req, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *Client) DeleteRecord(req *DeleteRecordRequest) (*DeleteRecordResponse, error) {
|
||||
resp := DeleteRecordResponse{}
|
||||
err := c.sendRequestWithResult(http.MethodDelete, fmt.Sprintf("/record?id=%s", url.QueryEscape(req.Id)), req, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
80
internal/pkg/vendors/dnsla-sdk/client.go
vendored
Normal file
80
internal/pkg/vendors/dnsla-sdk/client.go
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
package dnslasdk
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
apiId string
|
||||
apiSecret string
|
||||
|
||||
client *resty.Client
|
||||
}
|
||||
|
||||
func NewClient(apiId, apiSecret string) *Client {
|
||||
client := resty.New()
|
||||
|
||||
return &Client{
|
||||
apiId: apiId,
|
||||
apiSecret: apiSecret,
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) WithTimeout(timeout time.Duration) *Client {
|
||||
c.client.SetTimeout(timeout)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) {
|
||||
req := c.client.R().SetBasicAuth(c.apiId, c.apiSecret)
|
||||
req.Method = method
|
||||
req.URL = "https://api.dns.la/api" + path
|
||||
if strings.EqualFold(method, http.MethodGet) {
|
||||
qs := make(map[string]string)
|
||||
if params != nil {
|
||||
temp := make(map[string]any)
|
||||
jsonData, _ := json.Marshal(params)
|
||||
json.Unmarshal(jsonData, &temp)
|
||||
for k, v := range temp {
|
||||
qs[k] = fmt.Sprintf("%v", v)
|
||||
}
|
||||
}
|
||||
|
||||
req = req.SetQueryParams(qs)
|
||||
} else {
|
||||
req = req.
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetBody(params)
|
||||
}
|
||||
|
||||
resp, err := req.Send()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dnsla api error: failed to send request: %w", err)
|
||||
} else if resp.IsError() {
|
||||
return nil, fmt.Errorf("dnsla api error: unexpected status code: %d, %s", resp.StatusCode(), resp.Body())
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *Client) sendRequestWithResult(method string, path string, params interface{}, result BaseResponse) error {
|
||||
resp, err := c.sendRequest(method, path, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(resp.Body(), &result); err != nil {
|
||||
return fmt.Errorf("dnsla api error: failed to parse response: %w", err)
|
||||
} else if errcode := result.GetCode(); errcode/100 != 2 {
|
||||
return fmt.Errorf("dnsla api error: %d - %s", errcode, result.GetMessage())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
125
internal/pkg/vendors/dnsla-sdk/models.go
vendored
Normal file
125
internal/pkg/vendors/dnsla-sdk/models.go
vendored
Normal file
@ -0,0 +1,125 @@
|
||||
package dnslasdk
|
||||
|
||||
type BaseResponse interface {
|
||||
GetCode() int
|
||||
GetMessage() string
|
||||
}
|
||||
|
||||
type baseResponse struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func (r *baseResponse) GetCode() int {
|
||||
return r.Code
|
||||
}
|
||||
|
||||
func (r *baseResponse) GetMessage() string {
|
||||
return r.Message
|
||||
}
|
||||
|
||||
type DomainInfo struct {
|
||||
Id string `json:"id"`
|
||||
GroupId string `json:"groupId"`
|
||||
GroupName string `json:"groupName"`
|
||||
Domain string `json:"domain"`
|
||||
DisplayDomain string `json:"displayDomain"`
|
||||
CreatedAt int64 `json:"createdAt"`
|
||||
UpdatedAt int64 `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type RecordInfo struct {
|
||||
Id string `json:"id"`
|
||||
DomainId string `json:"domainId"`
|
||||
GroupId string `json:"groupId"`
|
||||
GroupName string `json:"groupName"`
|
||||
LineId string `json:"lineId"`
|
||||
LineCode string `json:"lineCode"`
|
||||
LineName string `json:"lineName"`
|
||||
Type int32 `json:"type"`
|
||||
Host string `json:"host"`
|
||||
DisplayHost string `json:"displayHost"`
|
||||
Data string `json:"data"`
|
||||
DisplayData string `json:"displayData"`
|
||||
Ttl int32 `json:"ttl"`
|
||||
Weight int32 `json:"weight"`
|
||||
Preference int32 `json:"preference"`
|
||||
CreatedAt int64 `json:"createdAt"`
|
||||
UpdatedAt int64 `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type ListDomainsRequest struct {
|
||||
PageIndex int32 `json:"pageIndex"`
|
||||
PageSize int32 `json:"pageSize"`
|
||||
GroupId *string `json:"groupId,omitempty"`
|
||||
}
|
||||
|
||||
type ListDomainsResponse struct {
|
||||
baseResponse
|
||||
Data *struct {
|
||||
Total int32 `json:"total"`
|
||||
Results []*DomainInfo `json:"results"`
|
||||
} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
type ListRecordsRequest struct {
|
||||
PageIndex int32 `json:"pageIndex"`
|
||||
PageSize int32 `json:"pageSize"`
|
||||
DomainId string `json:"domainId"`
|
||||
GroupId *string `json:"groupId,omitempty"`
|
||||
LineId *string `json:"lineId,omitempty"`
|
||||
Type *int32 `json:"type,omitempty"`
|
||||
Host *string `json:"host,omitempty"`
|
||||
Data *string `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
type ListRecordsResponse struct {
|
||||
baseResponse
|
||||
Data *struct {
|
||||
Total int32 `json:"total"`
|
||||
Results []*RecordInfo `json:"results"`
|
||||
} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
type CreateRecordRequest struct {
|
||||
DomainId string `json:"domainId"`
|
||||
GroupId *string `json:"groupId,omitempty"`
|
||||
LineId *string `json:"lineId,omitempty"`
|
||||
Type int32 `json:"type"`
|
||||
Host string `json:"host"`
|
||||
Data string `json:"data"`
|
||||
Ttl int32 `json:"ttl"`
|
||||
Weight *int32 `json:"weight,omitempty"`
|
||||
Preference *int32 `json:"preference,omitempty"`
|
||||
}
|
||||
|
||||
type CreateRecordResponse struct {
|
||||
baseResponse
|
||||
Data *struct {
|
||||
Id string `json:"id"`
|
||||
} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
type UpdateRecordRequest struct {
|
||||
Id string `json:"id"`
|
||||
GroupId *string `json:"groupId,omitempty"`
|
||||
LineId *string `json:"lineId,omitempty"`
|
||||
Type *int32 `json:"type,omitempty"`
|
||||
Host *string `json:"host,omitempty"`
|
||||
Data *string `json:"data,omitempty"`
|
||||
Ttl *int32 `json:"ttl,omitempty"`
|
||||
Weight *int32 `json:"weight,omitempty"`
|
||||
Preference *int32 `json:"preference,omitempty"`
|
||||
}
|
||||
|
||||
type UpdateRecordResponse struct {
|
||||
baseResponse
|
||||
}
|
||||
|
||||
type DeleteRecordRequest struct {
|
||||
Id string `json:"-"`
|
||||
}
|
||||
|
||||
type DeleteRecordResponse struct {
|
||||
baseResponse
|
||||
}
|
1
ui/public/imgs/providers/dnsla.svg
Normal file
1
ui/public/imgs/providers/dnsla.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 122 24" fill="none"><g><path fill-rule="evenodd" clip-rule="evenodd" d="M12.8182 17.2296L8.47407 17.2679C8.34011 17.2679 8.16309 17.2248 8.23007 16.909L10.5409 5.64677H13.0335C17.3729 5.64677 19.3105 8.13461 18.6599 11.7468C18.0044 15.3541 15.6745 17.2296 12.8182 17.2296ZM0.431641 23.4205H15.4687C19.3105 23.4205 23.1236 18.8563 24.2671 16.6076C25.4823 14.225 25.8938 10.4502 25.4775 7.79014C24.9513 4.43156 21.4539 0.0634766 18.6551 0.0634766H5.20639L0.431641 23.4205Z" fill="#1966F0"/><path fill-rule="evenodd" clip-rule="evenodd" d="M48.2748 15.6938L41.1366 0.0634766H36.247L31.4531 23.4205H36.357L39.6008 7.9863L46.6912 23.4396H51.5329L56.3268 0.0634766H51.4803L48.2748 15.6938Z" fill="#1966F0"/><path fill-rule="evenodd" clip-rule="evenodd" d="M56.982 10.5046C57.2882 12.8011 59.3263 14.1933 61.5558 14.2077H67.6127C68.4021 14.2555 68.7275 15.2985 68.6318 15.9396C68.3686 17.6954 67.3065 19.724 66.0817 19.724H54.5898L53.8291 23.4414H68.5696C72.6889 23.4414 75.5595 12.4662 71.2679 10.318C70.5359 9.94963 69.469 9.75825 68.2825 9.75825H62.216C60.6228 9.75825 61.9433 4.50029 63.5652 4.50029H75.7508L76.6646 0.0556666H63.7422C58.3072 0.0508823 56.5035 6.87331 56.982 10.5046Z" fill="#1966F0"/><path fill-rule="evenodd" clip-rule="evenodd" d="M108.376 13.6639L113.845 4.62634L115.73 13.6639H108.376ZM97.9561 23.3569H102.874L106.19 17.9507H116.567L117.687 23.3569H121.973L117.232 0.0668945H111.725L97.9561 23.3569Z" fill="#1966F0"/><path fill-rule="evenodd" clip-rule="evenodd" d="M86.6971 23.3662H96.0743L96.8924 20.6439L92.3378 20.7157C90.4719 20.7396 86.7449 20.754 87.5535 16.8213L90.9886 0.0522461H86.0895L82.5539 17.3475C81.6975 21.5099 83.1184 23.3662 86.6971 23.3662Z" fill="#1966F0"/><path fill-rule="evenodd" clip-rule="evenodd" d="M27.8549 12.6305C26.8502 19.6682 20.0756 22.8641 18.5781 23.4239H28.563L33.3569 0.0668945H20.6976C25.0752 1.49262 28.8931 6.36784 27.8549 12.6305Z" fill="#1966F0"/><path fill-rule="evenodd" clip-rule="evenodd" d="M78.9946 23.4237H74.5596L75.5117 18.936H79.918L78.9946 23.4237Z" fill="#1966F0"/></g></svg>
|
After Width: | Height: | Size: 2.1 KiB |
@ -22,6 +22,7 @@ import AccessFormCdnflyConfig from "./AccessFormCdnflyConfig";
|
||||
import AccessFormCloudflareConfig from "./AccessFormCloudflareConfig";
|
||||
import AccessFormClouDNSConfig from "./AccessFormClouDNSConfig";
|
||||
import AccessFormCMCCCloudConfig from "./AccessFormCMCCCloudConfig";
|
||||
import AccessFormDNSLAConfig from "./AccessFormDNSLAConfig";
|
||||
import AccessFormDogeCloudConfig from "./AccessFormDogeCloudConfig";
|
||||
import AccessFormEdgioConfig from "./AccessFormEdgioConfig";
|
||||
import AccessFormGcoreConfig from "./AccessFormGcoreConfig";
|
||||
@ -124,6 +125,8 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className,
|
||||
return <AccessFormClouDNSConfig {...nestedFormProps} />;
|
||||
case ACCESS_PROVIDERS.CMCCCLOUD:
|
||||
return <AccessFormCMCCCloudConfig {...nestedFormProps} />;
|
||||
case ACCESS_PROVIDERS.DNSLA:
|
||||
return <AccessFormDNSLAConfig {...nestedFormProps} />;
|
||||
case ACCESS_PROVIDERS.DOGECLOUD:
|
||||
return <AccessFormDogeCloudConfig {...nestedFormProps} />;
|
||||
case ACCESS_PROVIDERS.GCORE:
|
||||
|
76
ui/src/components/access/AccessFormDNSLAConfig.tsx
Normal file
76
ui/src/components/access/AccessFormDNSLAConfig.tsx
Normal file
@ -0,0 +1,76 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Form, type FormInstance, Input } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import { type AccessConfigForDNSLA } from "@/domain/access";
|
||||
|
||||
type AccessFormDNSLAConfigFieldValues = Nullish<AccessConfigForDNSLA>;
|
||||
|
||||
export type AccessFormDNSLAConfigProps = {
|
||||
form: FormInstance;
|
||||
formName: string;
|
||||
disabled?: boolean;
|
||||
initialValues?: AccessFormDNSLAConfigFieldValues;
|
||||
onValuesChange?: (values: AccessFormDNSLAConfigFieldValues) => void;
|
||||
};
|
||||
|
||||
const initFormModel = (): AccessFormDNSLAConfigFieldValues => {
|
||||
return {
|
||||
apiId: "",
|
||||
apiSecret: "",
|
||||
};
|
||||
};
|
||||
|
||||
const AccessFormDNSLAConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange: onValuesChange }: AccessFormDNSLAConfigProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const formSchema = z.object({
|
||||
apiId: z
|
||||
.string()
|
||||
.min(1, t("access.form.dnsla_api_id.placeholder"))
|
||||
.max(64, t("common.errmsg.string_max", { max: 64 }))
|
||||
.trim(),
|
||||
apiSecret: z
|
||||
.string()
|
||||
.min(1, t("access.form.dnsla_api_secret.placeholder"))
|
||||
.max(64, t("common.errmsg.string_max", { max: 64 }))
|
||||
.trim(),
|
||||
});
|
||||
const formRule = createSchemaFieldRule(formSchema);
|
||||
|
||||
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
|
||||
onValuesChange?.(values);
|
||||
};
|
||||
|
||||
return (
|
||||
<Form
|
||||
form={formInst}
|
||||
disabled={disabled}
|
||||
initialValues={initialValues ?? initFormModel()}
|
||||
layout="vertical"
|
||||
name={formName}
|
||||
onValuesChange={handleFormChange}
|
||||
>
|
||||
<Form.Item
|
||||
name="apiId"
|
||||
label={t("access.form.dnsla_api_id.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.dnsla_api_id.tooltip") }}></span>}
|
||||
>
|
||||
<Input autoComplete="new-password" placeholder={t("access.form.dnsla_api_id.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="apiSecret"
|
||||
label={t("access.form.dnsla_api_secret.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.dnsla_api_secret.tooltip") }}></span>}
|
||||
>
|
||||
<Input.Password autoComplete="new-password" placeholder={t("access.form.dnsla_api_secret.placeholder")} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
export default AccessFormDNSLAConfig;
|
@ -19,6 +19,7 @@ export interface AccessModel extends BaseModel {
|
||||
| AccessConfigForCloudflare
|
||||
| AccessConfigForClouDNS
|
||||
| AccessConfigForCMCCCloud
|
||||
| AccessConfigForDNSLA
|
||||
| AccessConfigForDogeCloud
|
||||
| AccessConfigForEdgio
|
||||
| AccessConfigForGcore
|
||||
@ -112,6 +113,11 @@ export type AccessConfigForCMCCCloud = {
|
||||
accessKeySecret: string;
|
||||
};
|
||||
|
||||
export type AccessConfigForDNSLA = {
|
||||
apiId: string;
|
||||
apiSecret: string;
|
||||
};
|
||||
|
||||
export type AccessConfigForDogeCloud = {
|
||||
accessKey: string;
|
||||
secretKey: string;
|
||||
|
@ -17,6 +17,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
|
||||
CLOUDFLARE: "cloudflare",
|
||||
CLOUDNS: "cloudns",
|
||||
CMCCCLOUD: "cmcccloud",
|
||||
DNSLA: "dnsla",
|
||||
DOGECLOUD: "dogecloud",
|
||||
GCORE: "gcore",
|
||||
GNAME: "gname",
|
||||
@ -91,6 +92,7 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv
|
||||
[ACCESS_PROVIDERS.AZURE, "provider.azure", "/imgs/providers/azure.svg", [ACCESS_USAGES.APPLY]],
|
||||
[ACCESS_PROVIDERS.CLOUDFLARE, "provider.cloudflare", "/imgs/providers/cloudflare.svg", [ACCESS_USAGES.APPLY]],
|
||||
[ACCESS_PROVIDERS.CLOUDNS, "provider.cloudns", "/imgs/providers/cloudns.png", [ACCESS_USAGES.APPLY]],
|
||||
[ACCESS_PROVIDERS.DNSLA, "provider.dnsla", "/imgs/providers/dnsla.svg", [ACCESS_USAGES.APPLY]],
|
||||
[ACCESS_PROVIDERS.GNAME, "provider.gname", "/imgs/providers/gname.png", [ACCESS_USAGES.APPLY]],
|
||||
[ACCESS_PROVIDERS.GODADDY, "provider.godaddy", "/imgs/providers/godaddy.svg", [ACCESS_USAGES.APPLY]],
|
||||
[ACCESS_PROVIDERS.NAMECHEAP, "provider.namecheap", "/imgs/providers/namecheap.svg", [ACCESS_USAGES.APPLY]],
|
||||
@ -132,6 +134,7 @@ export const APPLY_DNS_PROVIDERS = Object.freeze({
|
||||
CLOUDFLARE: `${ACCESS_PROVIDERS.CLOUDFLARE}`,
|
||||
CLOUDNS: `${ACCESS_PROVIDERS.CLOUDNS}`,
|
||||
CMCCCLOUD: `${ACCESS_PROVIDERS.CMCCCLOUD}`,
|
||||
DNSLA: `${ACCESS_PROVIDERS.DNSLA}`,
|
||||
GCORE: `${ACCESS_PROVIDERS.GCORE}`,
|
||||
GNAME: `${ACCESS_PROVIDERS.GNAME}`,
|
||||
GODADDY: `${ACCESS_PROVIDERS.GODADDY}`,
|
||||
@ -177,7 +180,7 @@ export const applyDNSProvidersMap: Map<ApplyDNSProvider["type"] | string, ApplyD
|
||||
[APPLY_DNS_PROVIDERS.AZURE_DNS, "provider.azure.dns"],
|
||||
[APPLY_DNS_PROVIDERS.CLOUDFLARE, "provider.cloudflare"],
|
||||
[APPLY_DNS_PROVIDERS.CLOUDNS, "provider.cloudns"],
|
||||
[APPLY_DNS_PROVIDERS.CMCCCLOUD, "provider.cmcc"],
|
||||
[APPLY_DNS_PROVIDERS.DNSLA, "provider.dnsla"],
|
||||
[APPLY_DNS_PROVIDERS.GCORE, "provider.gcore"],
|
||||
[APPLY_DNS_PROVIDERS.GNAME, "provider.gname"],
|
||||
[APPLY_DNS_PROVIDERS.GODADDY, "provider.godaddy"],
|
||||
@ -185,6 +188,7 @@ export const applyDNSProvidersMap: Map<ApplyDNSProvider["type"] | string, ApplyD
|
||||
[APPLY_DNS_PROVIDERS.NAMEDOTCOM, "provider.namedotcom"],
|
||||
[APPLY_DNS_PROVIDERS.NAMESILO, "provider.namesilo"],
|
||||
[APPLY_DNS_PROVIDERS.NS1, "provider.ns1"],
|
||||
[APPLY_DNS_PROVIDERS.CMCCCLOUD, "provider.cmcc"],
|
||||
[APPLY_DNS_PROVIDERS.RAINYUN, "provider.rainyun"],
|
||||
[APPLY_DNS_PROVIDERS.WESTCN, "provider.westcn"],
|
||||
[APPLY_DNS_PROVIDERS.POWERDNS, "provider.powerdns"],
|
||||
|
@ -106,6 +106,12 @@
|
||||
"access.form.cmcccloud_access_key_secret.label": "CMCC ECloud AccessKeySecret",
|
||||
"access.form.cmcccloud_access_key_secret.placeholder": "Please enter CMCC ECloud AccessKeySecret",
|
||||
"access.form.cmcccloud_access_key_secret.tooltip": "For more information, see <a href=\"https://ecloud.10086.cn/op-help-center/doc/article/49739\" target=\"_blank\">https://ecloud.10086.cn/op-help-center/doc/article/49739</a>",
|
||||
"access.form.dnsla_api_id.label": "DNS.LA API ID",
|
||||
"access.form.dnsla_api_id.placeholder": "Please enter DNS.LA API ID",
|
||||
"access.form.dnsla_api_id.tooltip": "For more information, see <a href=\"https://www.dns.la/docs/ApiDoc\" target=\"_blank\">https://www.dns.la/docs/ApiDoc</a>",
|
||||
"access.form.dnsla_api_secret.label": "DNS.LA API secret",
|
||||
"access.form.dnsla_api_secret.placeholder": "Please enter DNS.LA API secret",
|
||||
"access.form.dnsla_api_secret.tooltip": "For more information, see <a href=\"https://www.dns.la/docs/ApiDoc\" target=\"_blank\">https://www.dns.la/docs/ApiDoc</a>",
|
||||
"access.form.dogecloud_access_key.label": "Doge Cloud AccessKey",
|
||||
"access.form.dogecloud_access_key.placeholder": "Please enter Doge Cloud AccessKey",
|
||||
"access.form.dogecloud_access_key.tooltip": "For more information, see <a href=\"https://console.dogecloud.com/\" target=\"_blank\">https://console.dogecloud.com/</a>",
|
||||
|
@ -106,6 +106,12 @@
|
||||
"access.form.cmcccloud_access_key_secret.label": "移动云 AccessKeySecret",
|
||||
"access.form.cmcccloud_access_key_secret.placeholder": "请输入移动云 AccessKeySecret",
|
||||
"access.form.cmcccloud_access_key_secret.tooltip": "这是什么?请参阅 <a href=\"https://ecloud.10086.cn/op-help-center/doc/article/49739\" target=\"_blank\">https://ecloud.10086.cn/op-help-center/doc/article/49739</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>",
|
||||
"access.form.dnsla_api_secret.label": "DNS.LA API 密钥",
|
||||
"access.form.dnsla_api_secret.placeholder": "请输入 DNS.LA API 密钥",
|
||||
"access.form.dnsla_api_secret.tooltip": "这是什么?请参阅 <a href=\"https://www.dns.la/docs/ApiDoc\" target=\"_blank\">https://www.dns.la/docs/ApiDoc</a>",
|
||||
"access.form.dogecloud_access_key.label": "多吉云 AccessKey",
|
||||
"access.form.dogecloud_access_key.placeholder": "请输入多吉云 AccessKey",
|
||||
"access.form.dogecloud_access_key.tooltip": "这是什么?请参阅 <a href=\"https://console.dogecloud.com/\" target=\"_blank\">https://console.dogecloud.com/</a>",
|
||||
|
Loading…
x
Reference in New Issue
Block a user