feat: add dns.la dns-01 applicant

This commit is contained in:
Fu Diwei 2025-02-21 17:21:39 +08:00
parent 316a3c950f
commit f81fa2eb63
21 changed files with 677 additions and 12 deletions

View File

@ -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/) | |

View File

@ -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/) | |

View File

@ -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,
})

View File

@ -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"`

View File

@ -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")

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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),

View File

@ -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) {

View File

@ -12,7 +12,8 @@ import (
type Client struct {
apiToken string
client *resty.Client
client *resty.Client
}
func NewClient(apiToken string) *Client {

View File

@ -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
View 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
}

View 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
View 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
}

View 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

View File

@ -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:

View 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;

View File

@ -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;

View File

@ -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"],

View File

@ -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>",

View File

@ -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>",