mirror of
https://github.com/usual2970/certimate.git
synced 2025-06-16 09:29:52 +00:00
feat: new acme dns-01 provider: statecloud smartdns
This commit is contained in:
parent
fb62f1e105
commit
9c8ab98efb
@ -17,6 +17,7 @@ import (
|
||||
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"
|
||||
pConstellix "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/constellix"
|
||||
pCTCCCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/ctcccloud"
|
||||
pDeSEC "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/desec"
|
||||
pDigitalOcean "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/digitalocean"
|
||||
pDNSLA "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/dnsla"
|
||||
@ -220,7 +221,7 @@ func createApplicantProvider(options *applicantProviderOptions) (challenge.Provi
|
||||
return applicant, err
|
||||
}
|
||||
|
||||
case domain.ACMEDns01ProviderTypeCMCCCloud:
|
||||
case domain.ACMEDns01ProviderTypeCMCCCloud, domain.ACMEDns01ProviderTypeCMCCCloudDNS:
|
||||
{
|
||||
access := domain.AccessConfigForCMCCCloud{}
|
||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||
@ -252,6 +253,22 @@ func createApplicantProvider(options *applicantProviderOptions) (challenge.Provi
|
||||
return applicant, err
|
||||
}
|
||||
|
||||
case domain.ACMEDns01ProviderTypeCTCCCloud, domain.ACMEDns01ProviderTypeCTCCCloudSmartDNS:
|
||||
{
|
||||
access := domain.AccessConfigForCTCCCloud{}
|
||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||
}
|
||||
|
||||
applicant, err := pCTCCCloud.NewChallengeProvider(&pCTCCCloud.ChallengeProviderConfig{
|
||||
AccessKeyId: access.AccessKeyId,
|
||||
SecretAccessKey: access.SecretAccessKey,
|
||||
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
||||
DnsTTL: options.DnsTTL,
|
||||
})
|
||||
return applicant, err
|
||||
}
|
||||
|
||||
case domain.ACMEDns01ProviderTypeDeSEC:
|
||||
{
|
||||
access := domain.AccessConfigForDeSEC{}
|
||||
|
@ -120,6 +120,11 @@ type AccessConfigForConstellix struct {
|
||||
SecretKey string `json:"secretKey"`
|
||||
}
|
||||
|
||||
type AccessConfigForCTCCCloud struct {
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
SecretAccessKey string `json:"secretAccessKey"`
|
||||
}
|
||||
|
||||
type AccessConfigForDeSEC struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ const (
|
||||
AccessProviderTypeClouDNS = AccessProviderType("cloudns")
|
||||
AccessProviderTypeCMCCCloud = AccessProviderType("cmcccloud")
|
||||
AccessProviderTypeConstellix = AccessProviderType("constellix")
|
||||
AccessProviderTypeCTCCCloud = AccessProviderType("ctcccloud") // 天翼云(预留)
|
||||
AccessProviderTypeCTCCCloud = AccessProviderType("ctcccloud")
|
||||
AccessProviderTypeCUCCCloud = AccessProviderType("cucccloud") // 联通云(预留)
|
||||
AccessProviderTypeDeSEC = AccessProviderType("desec")
|
||||
AccessProviderTypeDigitalOcean = AccessProviderType("digitalocean")
|
||||
@ -119,51 +119,54 @@ ACME DNS-01 提供商常量值。
|
||||
NOTICE: If you add new constant, please keep ASCII order.
|
||||
*/
|
||||
const (
|
||||
ACMEDns01ProviderTypeACMEHttpReq = ACMEDns01ProviderType(AccessProviderTypeACMEHttpReq)
|
||||
ACMEDns01ProviderTypeAliyun = ACMEDns01ProviderType(AccessProviderTypeAliyun) // 兼容旧值,等同于 [ACMEDns01ProviderTypeAliyunDNS]
|
||||
ACMEDns01ProviderTypeAliyunDNS = ACMEDns01ProviderType(AccessProviderTypeAliyun + "-dns")
|
||||
ACMEDns01ProviderTypeAliyunESA = ACMEDns01ProviderType(AccessProviderTypeAliyun + "-esa")
|
||||
ACMEDns01ProviderTypeAWS = ACMEDns01ProviderType(AccessProviderTypeAWS) // 兼容旧值,等同于 [ACMEDns01ProviderTypeAWSRoute53]
|
||||
ACMEDns01ProviderTypeAWSRoute53 = ACMEDns01ProviderType(AccessProviderTypeAWS + "-route53")
|
||||
ACMEDns01ProviderTypeAzure = ACMEDns01ProviderType(AccessProviderTypeAzure) // 兼容旧值,等同于 [ACMEDns01ProviderTypeAzure]
|
||||
ACMEDns01ProviderTypeAzureDNS = ACMEDns01ProviderType(AccessProviderTypeAzure + "-dns")
|
||||
ACMEDns01ProviderTypeBaiduCloud = ACMEDns01ProviderType(AccessProviderTypeBaiduCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeBaiduCloudDNS]
|
||||
ACMEDns01ProviderTypeBaiduCloudDNS = ACMEDns01ProviderType(AccessProviderTypeBaiduCloud + "-dns")
|
||||
ACMEDns01ProviderTypeBunny = ACMEDns01ProviderType(AccessProviderTypeBunny)
|
||||
ACMEDns01ProviderTypeCloudflare = ACMEDns01ProviderType(AccessProviderTypeCloudflare)
|
||||
ACMEDns01ProviderTypeClouDNS = ACMEDns01ProviderType(AccessProviderTypeClouDNS)
|
||||
ACMEDns01ProviderTypeCMCCCloud = ACMEDns01ProviderType(AccessProviderTypeCMCCCloud)
|
||||
ACMEDns01ProviderTypeConstellix = ACMEDns01ProviderType(AccessProviderTypeConstellix)
|
||||
ACMEDns01ProviderTypeDeSEC = ACMEDns01ProviderType(AccessProviderTypeDeSEC)
|
||||
ACMEDns01ProviderTypeDigitalOcean = ACMEDns01ProviderType(AccessProviderTypeDigitalOcean)
|
||||
ACMEDns01ProviderTypeDNSLA = ACMEDns01ProviderType(AccessProviderTypeDNSLA)
|
||||
ACMEDns01ProviderTypeDuckDNS = ACMEDns01ProviderType(AccessProviderTypeDuckDNS)
|
||||
ACMEDns01ProviderTypeDynv6 = ACMEDns01ProviderType(AccessProviderTypeDynv6)
|
||||
ACMEDns01ProviderTypeGcore = ACMEDns01ProviderType(AccessProviderTypeGcore)
|
||||
ACMEDns01ProviderTypeGname = ACMEDns01ProviderType(AccessProviderTypeGname)
|
||||
ACMEDns01ProviderTypeGoDaddy = ACMEDns01ProviderType(AccessProviderTypeGoDaddy)
|
||||
ACMEDns01ProviderTypeHetzner = ACMEDns01ProviderType(AccessProviderTypeHetzner)
|
||||
ACMEDns01ProviderTypeHuaweiCloud = ACMEDns01ProviderType(AccessProviderTypeHuaweiCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeHuaweiCloudDNS]
|
||||
ACMEDns01ProviderTypeHuaweiCloudDNS = ACMEDns01ProviderType(AccessProviderTypeHuaweiCloud + "-dns")
|
||||
ACMEDns01ProviderTypeJDCloud = ACMEDns01ProviderType(AccessProviderTypeJDCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeJDCloudDNS]
|
||||
ACMEDns01ProviderTypeJDCloudDNS = ACMEDns01ProviderType(AccessProviderTypeJDCloud + "-dns")
|
||||
ACMEDns01ProviderTypeNamecheap = ACMEDns01ProviderType(AccessProviderTypeNamecheap)
|
||||
ACMEDns01ProviderTypeNameDotCom = ACMEDns01ProviderType(AccessProviderTypeNameDotCom)
|
||||
ACMEDns01ProviderTypeNameSilo = ACMEDns01ProviderType(AccessProviderTypeNameSilo)
|
||||
ACMEDns01ProviderTypeNetcup = ACMEDns01ProviderType(AccessProviderTypeNetcup)
|
||||
ACMEDns01ProviderTypeNetlify = ACMEDns01ProviderType(AccessProviderTypeNetlify)
|
||||
ACMEDns01ProviderTypeNS1 = ACMEDns01ProviderType(AccessProviderTypeNS1)
|
||||
ACMEDns01ProviderTypePorkbun = ACMEDns01ProviderType(AccessProviderTypePorkbun)
|
||||
ACMEDns01ProviderTypePowerDNS = ACMEDns01ProviderType(AccessProviderTypePowerDNS)
|
||||
ACMEDns01ProviderTypeRainYun = ACMEDns01ProviderType(AccessProviderTypeRainYun)
|
||||
ACMEDns01ProviderTypeTencentCloud = ACMEDns01ProviderType(AccessProviderTypeTencentCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeTencentCloudDNS]
|
||||
ACMEDns01ProviderTypeTencentCloudDNS = ACMEDns01ProviderType(AccessProviderTypeTencentCloud + "-dns")
|
||||
ACMEDns01ProviderTypeTencentCloudEO = ACMEDns01ProviderType(AccessProviderTypeTencentCloud + "-eo")
|
||||
ACMEDns01ProviderTypeUCloudUDNR = ACMEDns01ProviderType(AccessProviderTypeUCloud + "-udnr")
|
||||
ACMEDns01ProviderTypeVercel = ACMEDns01ProviderType(AccessProviderTypeVercel)
|
||||
ACMEDns01ProviderTypeVolcEngine = ACMEDns01ProviderType(AccessProviderTypeVolcEngine) // 兼容旧值,等同于 [ACMEDns01ProviderTypeVolcEngineDNS]
|
||||
ACMEDns01ProviderTypeVolcEngineDNS = ACMEDns01ProviderType(AccessProviderTypeVolcEngine + "-dns")
|
||||
ACMEDns01ProviderTypeWestcn = ACMEDns01ProviderType(AccessProviderTypeWestcn)
|
||||
ACMEDns01ProviderTypeACMEHttpReq = ACMEDns01ProviderType(AccessProviderTypeACMEHttpReq)
|
||||
ACMEDns01ProviderTypeAliyun = ACMEDns01ProviderType(AccessProviderTypeAliyun) // 兼容旧值,等同于 [ACMEDns01ProviderTypeAliyunDNS]
|
||||
ACMEDns01ProviderTypeAliyunDNS = ACMEDns01ProviderType(AccessProviderTypeAliyun + "-dns")
|
||||
ACMEDns01ProviderTypeAliyunESA = ACMEDns01ProviderType(AccessProviderTypeAliyun + "-esa")
|
||||
ACMEDns01ProviderTypeAWS = ACMEDns01ProviderType(AccessProviderTypeAWS) // 兼容旧值,等同于 [ACMEDns01ProviderTypeAWSRoute53]
|
||||
ACMEDns01ProviderTypeAWSRoute53 = ACMEDns01ProviderType(AccessProviderTypeAWS + "-route53")
|
||||
ACMEDns01ProviderTypeAzure = ACMEDns01ProviderType(AccessProviderTypeAzure) // 兼容旧值,等同于 [ACMEDns01ProviderTypeAzure]
|
||||
ACMEDns01ProviderTypeAzureDNS = ACMEDns01ProviderType(AccessProviderTypeAzure + "-dns")
|
||||
ACMEDns01ProviderTypeBaiduCloud = ACMEDns01ProviderType(AccessProviderTypeBaiduCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeBaiduCloudDNS]
|
||||
ACMEDns01ProviderTypeBaiduCloudDNS = ACMEDns01ProviderType(AccessProviderTypeBaiduCloud + "-dns")
|
||||
ACMEDns01ProviderTypeBunny = ACMEDns01ProviderType(AccessProviderTypeBunny)
|
||||
ACMEDns01ProviderTypeCloudflare = ACMEDns01ProviderType(AccessProviderTypeCloudflare)
|
||||
ACMEDns01ProviderTypeClouDNS = ACMEDns01ProviderType(AccessProviderTypeClouDNS)
|
||||
ACMEDns01ProviderTypeCMCCCloud = ACMEDns01ProviderType(AccessProviderTypeCMCCCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeCMCCCloudDNS]
|
||||
ACMEDns01ProviderTypeCMCCCloudDNS = ACMEDns01ProviderType(AccessProviderTypeCMCCCloud + "-dns")
|
||||
ACMEDns01ProviderTypeConstellix = ACMEDns01ProviderType(AccessProviderTypeConstellix)
|
||||
ACMEDns01ProviderTypeCTCCCloud = ACMEDns01ProviderType(AccessProviderTypeCTCCCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeCTCCCloudSmartDNS]
|
||||
ACMEDns01ProviderTypeCTCCCloudSmartDNS = ACMEDns01ProviderType(AccessProviderTypeCTCCCloud + "-smartdns")
|
||||
ACMEDns01ProviderTypeDeSEC = ACMEDns01ProviderType(AccessProviderTypeDeSEC)
|
||||
ACMEDns01ProviderTypeDigitalOcean = ACMEDns01ProviderType(AccessProviderTypeDigitalOcean)
|
||||
ACMEDns01ProviderTypeDNSLA = ACMEDns01ProviderType(AccessProviderTypeDNSLA)
|
||||
ACMEDns01ProviderTypeDuckDNS = ACMEDns01ProviderType(AccessProviderTypeDuckDNS)
|
||||
ACMEDns01ProviderTypeDynv6 = ACMEDns01ProviderType(AccessProviderTypeDynv6)
|
||||
ACMEDns01ProviderTypeGcore = ACMEDns01ProviderType(AccessProviderTypeGcore)
|
||||
ACMEDns01ProviderTypeGname = ACMEDns01ProviderType(AccessProviderTypeGname)
|
||||
ACMEDns01ProviderTypeGoDaddy = ACMEDns01ProviderType(AccessProviderTypeGoDaddy)
|
||||
ACMEDns01ProviderTypeHetzner = ACMEDns01ProviderType(AccessProviderTypeHetzner)
|
||||
ACMEDns01ProviderTypeHuaweiCloud = ACMEDns01ProviderType(AccessProviderTypeHuaweiCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeHuaweiCloudDNS]
|
||||
ACMEDns01ProviderTypeHuaweiCloudDNS = ACMEDns01ProviderType(AccessProviderTypeHuaweiCloud + "-dns")
|
||||
ACMEDns01ProviderTypeJDCloud = ACMEDns01ProviderType(AccessProviderTypeJDCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeJDCloudDNS]
|
||||
ACMEDns01ProviderTypeJDCloudDNS = ACMEDns01ProviderType(AccessProviderTypeJDCloud + "-dns")
|
||||
ACMEDns01ProviderTypeNamecheap = ACMEDns01ProviderType(AccessProviderTypeNamecheap)
|
||||
ACMEDns01ProviderTypeNameDotCom = ACMEDns01ProviderType(AccessProviderTypeNameDotCom)
|
||||
ACMEDns01ProviderTypeNameSilo = ACMEDns01ProviderType(AccessProviderTypeNameSilo)
|
||||
ACMEDns01ProviderTypeNetcup = ACMEDns01ProviderType(AccessProviderTypeNetcup)
|
||||
ACMEDns01ProviderTypeNetlify = ACMEDns01ProviderType(AccessProviderTypeNetlify)
|
||||
ACMEDns01ProviderTypeNS1 = ACMEDns01ProviderType(AccessProviderTypeNS1)
|
||||
ACMEDns01ProviderTypePorkbun = ACMEDns01ProviderType(AccessProviderTypePorkbun)
|
||||
ACMEDns01ProviderTypePowerDNS = ACMEDns01ProviderType(AccessProviderTypePowerDNS)
|
||||
ACMEDns01ProviderTypeRainYun = ACMEDns01ProviderType(AccessProviderTypeRainYun)
|
||||
ACMEDns01ProviderTypeTencentCloud = ACMEDns01ProviderType(AccessProviderTypeTencentCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeTencentCloudDNS]
|
||||
ACMEDns01ProviderTypeTencentCloudDNS = ACMEDns01ProviderType(AccessProviderTypeTencentCloud + "-dns")
|
||||
ACMEDns01ProviderTypeTencentCloudEO = ACMEDns01ProviderType(AccessProviderTypeTencentCloud + "-eo")
|
||||
ACMEDns01ProviderTypeUCloudUDNR = ACMEDns01ProviderType(AccessProviderTypeUCloud + "-udnr")
|
||||
ACMEDns01ProviderTypeVercel = ACMEDns01ProviderType(AccessProviderTypeVercel)
|
||||
ACMEDns01ProviderTypeVolcEngine = ACMEDns01ProviderType(AccessProviderTypeVolcEngine) // 兼容旧值,等同于 [ACMEDns01ProviderTypeVolcEngineDNS]
|
||||
ACMEDns01ProviderTypeVolcEngineDNS = ACMEDns01ProviderType(AccessProviderTypeVolcEngine + "-dns")
|
||||
ACMEDns01ProviderTypeWestcn = ACMEDns01ProviderType(AccessProviderTypeWestcn)
|
||||
)
|
||||
|
||||
type DeploymentProviderType string
|
||||
|
@ -1,7 +1,6 @@
|
||||
package cmcccloud
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
@ -18,7 +17,7 @@ type ChallengeProviderConfig struct {
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("config is nil")
|
||||
panic("config is nil")
|
||||
}
|
||||
|
||||
providerConfig := internal.NewDefaultConfig()
|
||||
|
@ -18,8 +18,9 @@ import (
|
||||
const (
|
||||
envNamespace = "CMCCCLOUD_"
|
||||
|
||||
EnvAccessKey = envNamespace + "ACCESS_KEY"
|
||||
EnvSecretKey = envNamespace + "SECRET_KEY"
|
||||
EnvAccessKey = envNamespace + "ACCESS_KEY"
|
||||
EnvSecretKey = envNamespace + "SECRET_KEY"
|
||||
|
||||
EnvTTL = envNamespace + "TTL"
|
||||
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
|
||||
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
|
||||
@ -30,13 +31,14 @@ const (
|
||||
var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
|
||||
|
||||
type Config struct {
|
||||
AccessKey string
|
||||
SecretKey string
|
||||
ReadTimeOut int
|
||||
ConnectTimeout int
|
||||
AccessKey string
|
||||
SecretKey string
|
||||
|
||||
PropagationTimeout time.Duration
|
||||
PollingInterval time.Duration
|
||||
TTL int32
|
||||
ReadTimeOut int
|
||||
ConnectTimeout int
|
||||
}
|
||||
|
||||
type DNSProvider struct {
|
||||
|
@ -0,0 +1,39 @@
|
||||
package ctcccloud
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/ctcccloud/internal"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
SecretAccessKey string `json:"secretAccessKey"`
|
||||
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.AccessKeyId = config.AccessKeyId
|
||||
providerConfig.SecretAccessKey = config.SecretAccessKey
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
|
||||
provider, err := internal.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
@ -0,0 +1,203 @@
|
||||
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"
|
||||
|
||||
ctyundns "github.com/usual2970/certimate/internal/pkg/sdk3rd/ctyun/dns"
|
||||
typeutil "github.com/usual2970/certimate/internal/pkg/utils/type"
|
||||
)
|
||||
|
||||
const (
|
||||
envNamespace = "CTYUNSMARTDNS_"
|
||||
|
||||
EnvAccessKeyID = envNamespace + "ACCESS_KEY_ID"
|
||||
EnvSecretAccessKey = envNamespace + "SECRET_ACCESS_KEY"
|
||||
|
||||
EnvTTL = envNamespace + "TTL"
|
||||
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
|
||||
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
|
||||
EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
|
||||
)
|
||||
|
||||
var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
|
||||
|
||||
type Config struct {
|
||||
AccessKeyId string
|
||||
SecretAccessKey string
|
||||
|
||||
PropagationTimeout time.Duration
|
||||
PollingInterval time.Duration
|
||||
TTL int
|
||||
HTTPTimeout time.Duration
|
||||
}
|
||||
|
||||
type DNSProvider struct {
|
||||
client *ctyundns.Client
|
||||
config *Config
|
||||
}
|
||||
|
||||
func NewDefaultConfig() *Config {
|
||||
return &Config{
|
||||
TTL: env.GetOrDefaultInt(EnvTTL, 600),
|
||||
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute),
|
||||
HTTPTimeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
|
||||
}
|
||||
}
|
||||
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
values, err := env.Get(EnvAccessKeyID, EnvSecretAccessKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ctyun: %w", err)
|
||||
}
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.AccessKeyId = values[EnvAccessKeyID]
|
||||
config.SecretAccessKey = values[EnvSecretAccessKey]
|
||||
|
||||
return NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("ctyun: the configuration of the DNS provider is nil")
|
||||
}
|
||||
|
||||
client, err := ctyundns.NewClient(config.AccessKeyId, config.SecretAccessKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
client.SetTimeout(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("ctyun: could not find zone for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ctyun: %w", err)
|
||||
}
|
||||
|
||||
if err := d.addOrUpdateDNSRecord(dns01.UnFqdn(authZone), subDomain, info.Value); err != nil {
|
||||
return fmt.Errorf("ctyun: %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("ctyun: could not find zone for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ctyun: %w", err)
|
||||
}
|
||||
|
||||
if err := d.removeDNSRecord(dns01.UnFqdn(authZone), subDomain); err != nil {
|
||||
return fmt.Errorf("ctyun: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||
}
|
||||
|
||||
func (d *DNSProvider) findDNSRecordId(zoneName, subDomain string) (int32, error) {
|
||||
// 查询解析记录列表
|
||||
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=122&api=11264&data=181&isNormal=1&vid=259
|
||||
request := &ctyundns.QueryRecordListRequest{}
|
||||
request.Domain = typeutil.ToPtr(zoneName)
|
||||
request.Host = typeutil.ToPtr(subDomain)
|
||||
request.Type = typeutil.ToPtr("TXT")
|
||||
|
||||
response, err := d.client.QueryRecordList(request)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if response.ReturnObj == nil || response.ReturnObj.Records == nil || len(response.ReturnObj.Records) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return response.ReturnObj.Records[0].RecordId, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) error {
|
||||
recordId, err := d.findDNSRecordId(zoneName, subDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if recordId == 0 {
|
||||
// 新增解析记录
|
||||
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=122&api=11259&data=181&isNormal=1&vid=259
|
||||
request := &ctyundns.AddRecordRequest{
|
||||
Domain: typeutil.ToPtr(zoneName),
|
||||
Host: typeutil.ToPtr(subDomain),
|
||||
Type: typeutil.ToPtr("TXT"),
|
||||
LineCode: typeutil.ToPtr("Default"),
|
||||
Value: typeutil.ToPtr(value),
|
||||
State: typeutil.ToPtr(int32(1)),
|
||||
TTL: typeutil.ToPtr(int32(d.config.TTL)),
|
||||
}
|
||||
_, err := d.client.AddRecord(request)
|
||||
return err
|
||||
} else {
|
||||
// 修改解析记录
|
||||
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=122&api=11261&data=181&isNormal=1&vid=259
|
||||
request := &ctyundns.UpdateRecordRequest{
|
||||
RecordId: typeutil.ToPtr(recordId),
|
||||
Domain: typeutil.ToPtr(zoneName),
|
||||
Host: typeutil.ToPtr(subDomain),
|
||||
Type: typeutil.ToPtr("TXT"),
|
||||
LineCode: typeutil.ToPtr("Default"),
|
||||
Value: typeutil.ToPtr(value),
|
||||
State: typeutil.ToPtr(int32(1)),
|
||||
TTL: typeutil.ToPtr(int32(d.config.TTL)),
|
||||
}
|
||||
_, err := d.client.UpdateRecord(request)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error {
|
||||
recordId, err := d.findDNSRecordId(zoneName, subDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if recordId == 0 {
|
||||
return nil
|
||||
} else {
|
||||
// 删除解析记录
|
||||
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=122&api=11262&data=181&isNormal=1&vid=259
|
||||
request := &ctyundns.DeleteRecordRequest{
|
||||
RecordId: typeutil.ToPtr(recordId),
|
||||
}
|
||||
_, err = d.client.DeleteRecord(request)
|
||||
return err
|
||||
}
|
||||
}
|
46
internal/pkg/sdk3rd/ctyun/dns/api_add_record.go
Normal file
46
internal/pkg/sdk3rd/ctyun/dns/api_add_record.go
Normal file
@ -0,0 +1,46 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type AddRecordRequest struct {
|
||||
Domain *string `json:"domain,omitempty"`
|
||||
Host *string `json:"host,omitempty"`
|
||||
Type *string `json:"type,omitempty"`
|
||||
LineCode *string `json:"lineCode,omitempty"`
|
||||
Value *string `json:"value,omitempty"`
|
||||
TTL *int32 `json:"ttl,omitempty"`
|
||||
State *int32 `json:"state,omitempty"`
|
||||
Remark *string `json:"remark"`
|
||||
}
|
||||
|
||||
type AddRecordResponse struct {
|
||||
baseResult
|
||||
|
||||
ReturnObj *struct {
|
||||
RecordId int32 `json:"recordId"`
|
||||
} `json:"returnObj,omitempty"`
|
||||
}
|
||||
|
||||
func (c *Client) AddRecord(req *AddRecordRequest) (*AddRecordResponse, error) {
|
||||
return c.AddRecordWithContext(context.Background(), req)
|
||||
}
|
||||
|
||||
func (c *Client) AddRecordWithContext(ctx context.Context, req *AddRecordRequest) (*AddRecordResponse, error) {
|
||||
request, err := c.newRequest(http.MethodPost, "/v2/addRecord")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
request.SetContext(ctx)
|
||||
request.SetBody(req)
|
||||
}
|
||||
|
||||
result := &AddRecordResponse{}
|
||||
if _, err := c.doRequestWithResult(request, result); err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
35
internal/pkg/sdk3rd/ctyun/dns/api_delete_record.go
Normal file
35
internal/pkg/sdk3rd/ctyun/dns/api_delete_record.go
Normal file
@ -0,0 +1,35 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type DeleteRecordRequest struct {
|
||||
RecordId *int32 `json:"recordId,omitempty"`
|
||||
}
|
||||
|
||||
type DeleteRecordResponse struct {
|
||||
baseResult
|
||||
}
|
||||
|
||||
func (c *Client) DeleteRecord(req *DeleteRecordRequest) (*DeleteRecordResponse, error) {
|
||||
return c.DeleteRecordWithContext(context.Background(), req)
|
||||
}
|
||||
|
||||
func (c *Client) DeleteRecordWithContext(ctx context.Context, req *DeleteRecordRequest) (*DeleteRecordResponse, error) {
|
||||
request, err := c.newRequest(http.MethodPost, "/v2/deleteRecord")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
request.SetContext(ctx)
|
||||
request.SetBody(req)
|
||||
}
|
||||
|
||||
result := &DeleteRecordResponse{}
|
||||
if _, err := c.doRequestWithResult(request, result); err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
53
internal/pkg/sdk3rd/ctyun/dns/api_query_record_list.go
Normal file
53
internal/pkg/sdk3rd/ctyun/dns/api_query_record_list.go
Normal file
@ -0,0 +1,53 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type QueryRecordListRequest struct {
|
||||
Domain *string `json:"domain,omitempty"`
|
||||
Host *string `json:"host,omitempty"`
|
||||
Type *string `json:"type,omitempty"`
|
||||
LineCode *string `json:"lineCode,omitempty"`
|
||||
Value *string `json:"value,omitempty"`
|
||||
State *int32 `json:"state,omitempty"`
|
||||
}
|
||||
|
||||
type QueryRecordListResponse struct {
|
||||
baseResult
|
||||
|
||||
ReturnObj *struct {
|
||||
Records []*struct {
|
||||
RecordId int32 `json:"recordId"`
|
||||
Host string `json:"host"`
|
||||
Type string `json:"type"`
|
||||
LineCode string `json:"lineCode"`
|
||||
Value string `json:"value"`
|
||||
TTL int32 `json:"ttl"`
|
||||
State int32 `json:"state"`
|
||||
Remark string `json:"remark"`
|
||||
} `json:"records,omitempty"`
|
||||
} `json:"returnObj,omitempty"`
|
||||
}
|
||||
|
||||
func (c *Client) QueryRecordList(req *QueryRecordListRequest) (*QueryRecordListResponse, error) {
|
||||
return c.QueryRecordListWithContext(context.Background(), req)
|
||||
}
|
||||
|
||||
func (c *Client) QueryRecordListWithContext(ctx context.Context, req *QueryRecordListRequest) (*QueryRecordListResponse, error) {
|
||||
request, err := c.newRequest(http.MethodGet, "/v2/queryRecordList")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
request.SetContext(ctx)
|
||||
request.SetBody(req)
|
||||
}
|
||||
|
||||
result := &QueryRecordListResponse{}
|
||||
if _, err := c.doRequestWithResult(request, result); err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
47
internal/pkg/sdk3rd/ctyun/dns/api_update_record.go
Normal file
47
internal/pkg/sdk3rd/ctyun/dns/api_update_record.go
Normal file
@ -0,0 +1,47 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type UpdateRecordRequest struct {
|
||||
RecordId *int32 `json:"recordId,omitempty"`
|
||||
Domain *string `json:"domain,omitempty"`
|
||||
Host *string `json:"host,omitempty"`
|
||||
Type *string `json:"type,omitempty"`
|
||||
LineCode *string `json:"lineCode,omitempty"`
|
||||
Value *string `json:"value,omitempty"`
|
||||
TTL *int32 `json:"ttl,omitempty"`
|
||||
State *int32 `json:"state,omitempty"`
|
||||
Remark *string `json:"remark"`
|
||||
}
|
||||
|
||||
type UpdateRecordResponse struct {
|
||||
baseResult
|
||||
|
||||
ReturnObj *struct {
|
||||
RecordId int32 `json:"recordId"`
|
||||
} `json:"returnObj,omitempty"`
|
||||
}
|
||||
|
||||
func (c *Client) UpdateRecord(req *UpdateRecordRequest) (*UpdateRecordResponse, error) {
|
||||
return c.UpdateRecordWithContext(context.Background(), req)
|
||||
}
|
||||
|
||||
func (c *Client) UpdateRecordWithContext(ctx context.Context, req *UpdateRecordRequest) (*UpdateRecordResponse, error) {
|
||||
request, err := c.newRequest(http.MethodPost, "/v2/updateRecord")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
request.SetContext(ctx)
|
||||
request.SetBody(req)
|
||||
}
|
||||
|
||||
result := &UpdateRecordResponse{}
|
||||
if _, err := c.doRequestWithResult(request, result); err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
40
internal/pkg/sdk3rd/ctyun/dns/client.go
Normal file
40
internal/pkg/sdk3rd/ctyun/dns/client.go
Normal file
@ -0,0 +1,40 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/usual2970/certimate/internal/pkg/sdk3rd/ctyun/openapi"
|
||||
)
|
||||
|
||||
const endpoint = "https://smartdns-global.ctapi.ctyun.cn"
|
||||
|
||||
type Client struct {
|
||||
client *openapi.Client
|
||||
}
|
||||
|
||||
func NewClient(accessKeyId, secretAccessKey string) (*Client, error) {
|
||||
client, err := openapi.NewClient(endpoint, accessKeyId, secretAccessKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Client{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) {
|
||||
return c.client.NewRequest(method, path)
|
||||
}
|
||||
|
||||
func (c *Client) doRequest(request *resty.Request) (*resty.Response, error) {
|
||||
return c.client.DoRequest(request)
|
||||
}
|
||||
|
||||
func (c *Client) doRequestWithResult(request *resty.Request, result any) (*resty.Response, error) {
|
||||
return c.client.DoRequestWithResult(request, result)
|
||||
}
|
11
internal/pkg/sdk3rd/ctyun/dns/types.go
Normal file
11
internal/pkg/sdk3rd/ctyun/dns/types.go
Normal file
@ -0,0 +1,11 @@
|
||||
package dns
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type baseResult struct {
|
||||
StatusCode json.RawMessage `json:"statusCode,omitempty"`
|
||||
Message *string `json:"message,omitempty"`
|
||||
Error *string `json:"error,omitempty"`
|
||||
ErrorMessage *string `json:"errorMessage,omitempty"`
|
||||
RequestId *string `json:"requestId,omitempty"`
|
||||
}
|
167
internal/pkg/sdk3rd/ctyun/openapi/client.go
Normal file
167
internal/pkg/sdk3rd/ctyun/openapi/client.go
Normal file
@ -0,0 +1,167 @@
|
||||
package openapi
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
client *resty.Client
|
||||
}
|
||||
|
||||
func NewClient(endpoint, accessKeyId, secretAccessKey string) (*Client, error) {
|
||||
if endpoint == "" {
|
||||
return nil, fmt.Errorf("sdk error: unset endpoint")
|
||||
}
|
||||
if _, err := url.Parse(endpoint); err != nil {
|
||||
return nil, fmt.Errorf("sdk error: invalid endpoint: %w", err)
|
||||
}
|
||||
if accessKeyId == "" {
|
||||
return nil, fmt.Errorf("sdk error: unset accessKey")
|
||||
}
|
||||
if secretAccessKey == "" {
|
||||
return nil, fmt.Errorf("sdk error: unset secretKey")
|
||||
}
|
||||
|
||||
client := resty.New().
|
||||
SetBaseURL(endpoint).
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", "certimate").
|
||||
SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
|
||||
// 生成时间戳及流水号
|
||||
now := time.Now()
|
||||
eopDate := now.Format("20060102T150405Z")
|
||||
eopReqId := uuid.New().String()
|
||||
|
||||
// 获取查询参数
|
||||
queryStr := ""
|
||||
if req.URL != nil {
|
||||
queryStr = req.URL.Query().Encode()
|
||||
}
|
||||
|
||||
// 获取请求正文
|
||||
payloadStr := ""
|
||||
if req.Body != nil {
|
||||
reader, err := req.GetBody()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer reader.Close()
|
||||
payload, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
payloadStr = string(payload)
|
||||
}
|
||||
|
||||
// 构造代签字符串
|
||||
payloadHash := sha256.Sum256([]byte(payloadStr))
|
||||
payloadHashHex := hex.EncodeToString(payloadHash[:])
|
||||
dataToSign := fmt.Sprintf("ctyun-eop-request-id:%s\neop-date:%s\n\n%s\n%s", eopReqId, eopDate, queryStr, payloadHashHex)
|
||||
|
||||
// 生成 ktime
|
||||
hasher := hmac.New(sha256.New, []byte(secretAccessKey))
|
||||
hasher.Write([]byte(eopDate))
|
||||
ktime := hasher.Sum(nil)
|
||||
|
||||
// 生成 kak
|
||||
hasher = hmac.New(sha256.New, ktime)
|
||||
hasher.Write([]byte(accessKeyId))
|
||||
kak := hasher.Sum(nil)
|
||||
|
||||
// 生成 kdata
|
||||
hasher = hmac.New(sha256.New, kak)
|
||||
hasher.Write([]byte(now.Format("20060102")))
|
||||
kdate := hasher.Sum(nil)
|
||||
|
||||
// 构造签名
|
||||
hasher = hmac.New(sha256.New, kdate)
|
||||
hasher.Write([]byte(dataToSign))
|
||||
sign := hasher.Sum(nil)
|
||||
signStr := base64.StdEncoding.EncodeToString(sign)
|
||||
|
||||
// 设置请求头
|
||||
req.Header.Set("ctyun-eop-request-id", eopReqId)
|
||||
req.Header.Set("eop-date", eopDate)
|
||||
req.Header.Set("eop-authorization", fmt.Sprintf("%s Headers=ctyun-eop-request-id;eop-date Signature=%s", accessKeyId, signStr))
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return &Client{
|
||||
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("sdk error: unset method")
|
||||
}
|
||||
if path == "" {
|
||||
return nil, fmt.Errorf("sdk error: unset path")
|
||||
}
|
||||
|
||||
req := c.client.R()
|
||||
req.Method = method
|
||||
req.URL = path
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func (c *Client) DoRequest(request *resty.Request) (*resty.Response, error) {
|
||||
if request == nil {
|
||||
return nil, fmt.Errorf("sdk error: nil request")
|
||||
}
|
||||
|
||||
// WARN:
|
||||
// PLEASE DO NOT USE `req.SetResult` or `req.SetError` here.
|
||||
|
||||
resp, err := request.Send()
|
||||
if err != nil {
|
||||
return resp, fmt.Errorf("sdk error: failed to send request: %w", err)
|
||||
} else if resp.IsError() {
|
||||
return resp, fmt.Errorf("sdk error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *Client) DoRequestWithResult(request *resty.Request, result any) (*resty.Response, error) {
|
||||
if request == nil {
|
||||
return nil, fmt.Errorf("sdk error: nil request")
|
||||
}
|
||||
|
||||
response, err := c.DoRequest(request)
|
||||
if err != nil {
|
||||
if response != nil {
|
||||
json.Unmarshal(response.Body(), &result)
|
||||
}
|
||||
return response, err
|
||||
}
|
||||
|
||||
if len(response.Body()) != 0 {
|
||||
if err := json.Unmarshal(response.Body(), &result); err != nil {
|
||||
return response, fmt.Errorf("sdk error: failed to unmarshal response: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
1
ui/public/imgs/providers/ctcccloud.svg
Normal file
1
ui/public/imgs/providers/ctcccloud.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M853.205 403.712A169.6 169.6 0 0 0 661.59 299.605a260.864 260.864 0 0 0-193.962-86.272 261.76 261.76 0 0 0-232.363 141.952h-0.427C105.26 355.285 0 461.184 0 591.957c0 130.688 105.216 236.715 234.88 236.715h368.768c3.157 0 4.693-3.84 2.39-6.059-25.558-25.173-46.251-55.466-60.545-89.088a5.803 5.803 0 0 0-5.29-3.584H234.965a137.045 137.045 0 0 1-131.968-100.437 138.667 138.667 0 0 1 56.96-153.173 135.723 135.723 0 0 1 139.862-5.803c4.693-82.432 67.285-152.32 151.466-162.347 78.038-9.301 149.888 35.584 178.859 105.088h0.043a85.163 85.163 0 0 1 106.24-18.432 86.656 86.656 0 0 1 40.021 100.95 121.813 121.813 0 0 1 144.47 70.357c23.722 55.85 1.791 122.71-50.134 153.643-59.477 35.413-135.68 14.08-168.747-46.464a122.496 122.496 0 0 1-13.866-42.24h60.544c3.968 0 5.973-4.822 3.157-7.68L635.179 505.899a5.547 5.547 0 0 0-7.894 0L510.72 623.403c-2.773 2.858-0.81 7.68 3.157 7.68h57.344c0.555 28.8 6.571 56.277 17.024 81.408a226.432 226.432 0 0 0 209.238 140.842c125.184 0 226.517-102.229 226.517-228.309a227.968 227.968 0 0 0-170.795-221.312" fill="#DF0629"></path></svg>
|
After Width: | Height: | Size: 1.2 KiB |
@ -30,6 +30,7 @@ import AccessFormCloudflareConfig from "./AccessFormCloudflareConfig";
|
||||
import AccessFormClouDNSConfig from "./AccessFormClouDNSConfig";
|
||||
import AccessFormCMCCCloudConfig from "./AccessFormCMCCCloudConfig";
|
||||
import AccessFormConstellixConfig from "./AccessFormConstellixConfig";
|
||||
import AccessFormCTCCCloudConfig from "./AccessFormCTCCCloudConfig";
|
||||
import AccessFormDeSECConfig from "./AccessFormDeSECConfig";
|
||||
import AccessFormDigitalOceanConfig from "./AccessFormDigitalOceanConfig";
|
||||
import AccessFormDingTalkBotConfig from "./AccessFormDingTalkBotConfig";
|
||||
@ -225,6 +226,8 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className,
|
||||
return <AccessFormCMCCCloudConfig {...nestedFormProps} />;
|
||||
case ACCESS_PROVIDERS.CONSTELLIX:
|
||||
return <AccessFormConstellixConfig {...nestedFormProps} />;
|
||||
case ACCESS_PROVIDERS.CTCCCLOUD:
|
||||
return <AccessFormCTCCCloudConfig {...nestedFormProps} />;
|
||||
case ACCESS_PROVIDERS.DESEC:
|
||||
return <AccessFormDeSECConfig {...nestedFormProps} />;
|
||||
case ACCESS_PROVIDERS.DIGITALOCEAN:
|
||||
|
73
ui/src/components/access/AccessFormCTCCCloudConfig.tsx
Normal file
73
ui/src/components/access/AccessFormCTCCCloudConfig.tsx
Normal file
@ -0,0 +1,73 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Form, type FormInstance, Input } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
import { type AccessConfigForCTCCCloud } from "@/domain/access";
|
||||
|
||||
type AccessFormCTCCCloudConfigFieldValues = Nullish<AccessConfigForCTCCCloud>;
|
||||
|
||||
export type AccessFormCTCCCloudConfigProps = {
|
||||
form: FormInstance;
|
||||
formName: string;
|
||||
disabled?: boolean;
|
||||
initialValues?: AccessFormCTCCCloudConfigFieldValues;
|
||||
onValuesChange?: (values: AccessFormCTCCCloudConfigFieldValues) => void;
|
||||
};
|
||||
|
||||
const initFormModel = (): AccessFormCTCCCloudConfigFieldValues => {
|
||||
return {
|
||||
accessKeyId: "",
|
||||
secretAccessKey: "",
|
||||
};
|
||||
};
|
||||
|
||||
const AccessFormCTCCCloudConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange: onValuesChange }: AccessFormCTCCCloudConfigProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const formSchema = z.object({
|
||||
accessKeyId: z
|
||||
.string()
|
||||
.min(1, t("access.form.ctcccloud_access_key_id.placeholder"))
|
||||
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||
secretAccessKey: z
|
||||
.string()
|
||||
.min(1, t("access.form.ctcccloud_secret_access_key.placeholder"))
|
||||
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||
});
|
||||
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="accessKeyId"
|
||||
label={t("access.form.ctcccloud_access_key_id.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.ctcccloud_access_key_id.tooltip") }}></span>}
|
||||
>
|
||||
<Input autoComplete="new-password" placeholder={t("access.form.ctcccloud_access_key_id.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="secretAccessKey"
|
||||
label={t("access.form.ctcccloud_secret_access_key.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.ctcccloud_secret_access_key.tooltip") }}></span>}
|
||||
>
|
||||
<Input.Password autoComplete="new-password" placeholder={t("access.form.ctcccloud_secret_access_key.placeholder")} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
export default AccessFormCTCCCloudConfig;
|
@ -25,6 +25,7 @@ export interface AccessModel extends BaseModel {
|
||||
| AccessConfigForClouDNS
|
||||
| AccessConfigForCMCCCloud
|
||||
| AccessConfigForConstellix
|
||||
| AccessConfigForCTCCCloud
|
||||
| AccessConfigForDeSEC
|
||||
| AccessConfigForDigitalOcean
|
||||
| AccessConfigForDingTalkBot
|
||||
@ -185,6 +186,11 @@ export type AccessConfigForConstellix = {
|
||||
secretKey: string;
|
||||
};
|
||||
|
||||
export type AccessConfigForCTCCCloud = {
|
||||
accessKeyId: string;
|
||||
secretAccessKey: string;
|
||||
};
|
||||
|
||||
export type AccessConfigForDeSEC = {
|
||||
token: string;
|
||||
};
|
||||
|
@ -24,6 +24,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
|
||||
CLOUDNS: "cloudns",
|
||||
CMCCCLOUD: "cmcccloud",
|
||||
CONSTELLIX: "constellix",
|
||||
CTCCCLOUD: "ctcccloud",
|
||||
DESEC: "desec",
|
||||
DIGITALOCEAN: "digitalocean",
|
||||
DINGTALKBOT: "dingtalkbot",
|
||||
@ -164,6 +165,7 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv
|
||||
[ACCESS_PROVIDERS.PORKBUN, "provider.porkbun", "/imgs/providers/porkbun.svg", [ACCESS_USAGES.DNS]],
|
||||
[ACCESS_PROVIDERS.VERCEL, "provider.vercel", "/imgs/providers/vercel.svg", [ACCESS_USAGES.DNS]],
|
||||
[ACCESS_PROVIDERS.CMCCCLOUD, "provider.cmcccloud", "/imgs/providers/cmcccloud.svg", [ACCESS_USAGES.DNS]],
|
||||
[ACCESS_PROVIDERS.CTCCCLOUD, "provider.ctcccloud", "/imgs/providers/ctcccloud.svg", [ACCESS_USAGES.DNS]],
|
||||
[ACCESS_PROVIDERS.WESTCN, "provider.westcn", "/imgs/providers/westcn.svg", [ACCESS_USAGES.DNS]],
|
||||
[ACCESS_PROVIDERS.POWERDNS, "provider.powerdns", "/imgs/providers/powerdns.svg", [ACCESS_USAGES.DNS]],
|
||||
[ACCESS_PROVIDERS.ACMEHTTPREQ, "provider.acmehttpreq", "/imgs/providers/acmehttpreq.svg", [ACCESS_USAGES.DNS]],
|
||||
@ -267,8 +269,11 @@ export const ACME_DNS01_PROVIDERS = Object.freeze({
|
||||
BUNNY: `${ACCESS_PROVIDERS.BUNNY}`,
|
||||
CLOUDFLARE: `${ACCESS_PROVIDERS.CLOUDFLARE}`,
|
||||
CLOUDNS: `${ACCESS_PROVIDERS.CLOUDNS}`,
|
||||
CMCCCLOUD: `${ACCESS_PROVIDERS.CMCCCLOUD}`,
|
||||
CMCCCLOUD: `${ACCESS_PROVIDERS.CMCCCLOUD}`, // 兼容旧值,等同于 `CMCCCLOUD_DNS`
|
||||
CMCCCLOUD_DNS: `${ACCESS_PROVIDERS.CMCCCLOUD}-dns`,
|
||||
CONSTELLIX: `${ACCESS_PROVIDERS.CONSTELLIX}`,
|
||||
CTCCCLOUD: `${ACCESS_PROVIDERS.CTCCCLOUD}`, // 兼容旧值,等同于 `CTCCCLOUD_SMARTDNS`
|
||||
CTCCCLOUD_SMARTDNS: `${ACCESS_PROVIDERS.CTCCCLOUD}-smartdns`,
|
||||
DESEC: `${ACCESS_PROVIDERS.DESEC}`,
|
||||
DIGITALOCEAN: `${ACCESS_PROVIDERS.DIGITALOCEAN}`,
|
||||
DNSLA: `${ACCESS_PROVIDERS.DNSLA}`,
|
||||
@ -347,7 +352,8 @@ export const acmeDns01ProvidersMap: Map<ACMEDns01Provider["type"] | string, ACME
|
||||
[ACME_DNS01_PROVIDERS.NS1, "provider.ns1"],
|
||||
[ACME_DNS01_PROVIDERS.PORKBUN, "provider.porkbun"],
|
||||
[ACME_DNS01_PROVIDERS.VERCEL, "provider.vercel"],
|
||||
[ACME_DNS01_PROVIDERS.CMCCCLOUD, "provider.cmcccloud"],
|
||||
[ACME_DNS01_PROVIDERS.CMCCCLOUD_DNS, "provider.cmcccloud.dns"],
|
||||
[ACME_DNS01_PROVIDERS.CTCCCLOUD_SMARTDNS, "provider.ctcccloud.smartdns"],
|
||||
[ACME_DNS01_PROVIDERS.RAINYUN, "provider.rainyun"],
|
||||
[ACME_DNS01_PROVIDERS.UCLOUD_UDNR, "provider.ucloud.udnr"],
|
||||
[ACME_DNS01_PROVIDERS.WESTCN, "provider.westcn"],
|
||||
|
@ -157,6 +157,12 @@
|
||||
"access.form.constellix_secret_key.label": "Constellix API secret key",
|
||||
"access.form.constellix_secret_key.placeholder": "Please enter Constellix API secret key",
|
||||
"access.form.constellix_secret_key.tooltip": "For more information, see <a href=\"https://support.constellix.com/hc/en-us/articles/34574197390491-How-to-Generate-an-API-Key\" target=\"_blank\">https://support.constellix.com/hc/en-us/articles/34574197390491-How-to-Generate-an-API-Key</a>",
|
||||
"access.form.ctcccloud_access_key_id.label": "CTCC StateCloud AccessKeyId",
|
||||
"access.form.ctcccloud_access_key_id.placeholder": "Please enter CTCC StateCloud AccessKeyId",
|
||||
"access.form.ctcccloud_access_key_id.tooltip": "For more information, see <a href=\"https://www.ctyun.cn/document/10015882/10015953\" target=\"_blank\">https://www.ctyun.cn/document/10015882/10015953</a>",
|
||||
"access.form.ctcccloud_secret_access_key.label": "CTCC StateCloud SecretAccessKey",
|
||||
"access.form.ctcccloud_secret_access_key.placeholder": "Please enter CTCC StateCloud SecretAccessKey",
|
||||
"access.form.ctcccloud_secret_access_key.tooltip": "For more information, see <a href=\"https://www.ctyun.cn/document/10015882/10015953\" target=\"_blank\">https://www.ctyun.cn/document/10015882/10015953</a>",
|
||||
"access.form.desec_token.label": "deSEC token",
|
||||
"access.form.desec_token.placeholder": "Please enter deSEC token",
|
||||
"access.form.desec_token.tooltip": "For more information, see <a href=\"https://desec.readthedocs.io/en/latest/auth/tokens.html#manage-tokens\" target=\"_blank\">https://desec.readthedocs.io/en/latest/auth/tokens.html</a>",
|
||||
|
@ -13,7 +13,7 @@
|
||||
"provider.aliyun.clb": "Alibaba Cloud - CLB (Classic Load Balancer)",
|
||||
"provider.aliyun.dcdn": "Alibaba Cloud - DCDN (Dynamic Route for Content Delivery Network)",
|
||||
"provider.aliyun.ddos": "Alibaba Cloud - Anti-DDoS Proxy",
|
||||
"provider.aliyun.dns": "Alibaba Cloud - DNS (Domain Name Service)",
|
||||
"provider.aliyun.dns": "Alibaba Cloud - DNS",
|
||||
"provider.aliyun.esa": "Alibaba Cloud - ESA (Edge Security Acceleration)",
|
||||
"provider.aliyun.fc": "Alibaba Cloud - FC (Function Compute)",
|
||||
"provider.aliyun.ga": "Alibaba Cloud - GA (Global Accelerator)",
|
||||
@ -38,7 +38,7 @@
|
||||
"provider.baiducloud.blb": "Baidu Cloud - BLB (Load Balancer)",
|
||||
"provider.baiducloud.cdn": "Baidu Cloud - CDN (Content Delivery Network)",
|
||||
"provider.baiducloud.cert_upload": "Baidu Cloud - Upload to SSL Certificate Service",
|
||||
"provider.baiducloud.dns": "Baidu Cloud - DNS (Domain Name Service)",
|
||||
"provider.baiducloud.dns": "Baidu Cloud - DNS",
|
||||
"provider.baishan": "Baishan",
|
||||
"provider.baishan.cdn": "Baishan - CDN (Content Delivery Network)",
|
||||
"provider.baotapanel": "aaPanel (aka BaoTaPanel)",
|
||||
@ -57,8 +57,10 @@
|
||||
"provider.cloudflare": "Cloudflare",
|
||||
"provider.cloudns": "ClouDNS",
|
||||
"provider.cmcccloud": "China Mobile Cloud (ECloud)",
|
||||
"provider.cmcccloud.dns": "China Mobile Cloud (ECloud) - DNS",
|
||||
"provider.constellix": "Constellix",
|
||||
"provider.ctcccloud": "China Telecom Cloud (State Cloud)",
|
||||
"provider.ctcccloud": "China Telecom Cloud (StateCloud)",
|
||||
"provider.ctcccloud.smartdns": "China Telecom Cloud (StateCloud) - Smart DNS",
|
||||
"provider.cucccloud": "China Unicom Cloud",
|
||||
"provider.desec": "deSEC",
|
||||
"provider.digitalocean": "DigitalOcean",
|
||||
@ -83,7 +85,7 @@
|
||||
"provider.hetzner": "Hetzner",
|
||||
"provider.huaweicloud": "Huawei Cloud",
|
||||
"provider.huaweicloud.cdn": "Huawei Cloud - CDN (Content Delivery Network)",
|
||||
"provider.huaweicloud.dns": "Huawei Cloud - DNS (Domain Name Service)",
|
||||
"provider.huaweicloud.dns": "Huawei Cloud - DNS",
|
||||
"provider.huaweicloud.elb": "Huawei Cloud - ELB (Elastic Load Balance)",
|
||||
"provider.huaweicloud.scm_upload": "Huawei Cloud - Upload to SCM (SSL Certificate Manager)",
|
||||
"provider.huaweicloud.waf": "Huawei Cloud - WAF (Web Application Firewall)",
|
||||
@ -130,7 +132,7 @@
|
||||
"provider.tencentcloud.clb": "Tencent Cloud - CLB (Cloud Load Balancer)",
|
||||
"provider.tencentcloud.cos": "Tencent Cloud - COS (Cloud Object Storage)",
|
||||
"provider.tencentcloud.css": "Tencent Cloud - CSS (Cloud Streaming Service)",
|
||||
"provider.tencentcloud.dns": "Tencent Cloud - DNS (Domain Name Service)",
|
||||
"provider.tencentcloud.dns": "Tencent Cloud - DNS",
|
||||
"provider.tencentcloud.ecdn": "Tencent Cloud - ECDN (Enterprise Content Delivery Network)",
|
||||
"provider.tencentcloud.eo": "Tencent Cloud - EdgeOne",
|
||||
"provider.tencentcloud.gaap": "Tencent Cloud - GAAP (Global Application Acceleration Platform)",
|
||||
@ -155,7 +157,7 @@
|
||||
"provider.volcengine.certcenter_upload": "Volcengine - Upload to Certificate Center",
|
||||
"provider.volcengine.clb": "Volcengine - CLB (Cloud Load Balancer)",
|
||||
"provider.volcengine.dcdn": "Volcengine - DCDN (Dynamic Content Delivery Network)",
|
||||
"provider.volcengine.dns": "Volcengine - DNS (Domain Name Service)",
|
||||
"provider.volcengine.dns": "Volcengine - DNS",
|
||||
"provider.volcengine.imagex": "Volcengine - ImageX",
|
||||
"provider.volcengine.live": "Volcengine - Live",
|
||||
"provider.volcengine.tos": "Volcengine - TOS (Tinder Object Storage)",
|
||||
|
@ -157,6 +157,12 @@
|
||||
"access.form.constellix_secret_key.label": "Constellix Secret Key",
|
||||
"access.form.constellix_secret_key.placeholder": "请输入 Constellix Secret Key",
|
||||
"access.form.constellix_secret_key.tooltip": "这是什么?请参阅 <a href=\"https://support.constellix.com/hc/en-us/articles/34574197390491-How-to-Generate-an-API-Key\" target=\"_blank\">https://support.constellix.com/hc/en-us/articles/34574197390491-How-to-Generate-an-API-Key</a>",
|
||||
"access.form.ctcccloud_access_key_id.label": "天翼云 AccessKeyId",
|
||||
"access.form.ctcccloud_access_key_id.placeholder": "请输入天翼云 AccessKeyId",
|
||||
"access.form.ctcccloud_access_key_id.tooltip": "这是什么?请参阅 <a href=\"https://www.ctyun.cn/document/10015882/10015953\" target=\"_blank\">https://www.ctyun.cn/document/10015882/10015953</a>",
|
||||
"access.form.ctcccloud_secret_access_key.label": "天翼云 SecretAccessKey",
|
||||
"access.form.ctcccloud_secret_access_key.placeholder": "请输入天翼云 SecretAccessKey",
|
||||
"access.form.ctcccloud_secret_access_key.tooltip": "这是什么?请参阅 <a href=\"https://www.ctyun.cn/document/10015882/10015953\" target=\"_blank\">https://www.ctyun.cn/document/10015882/10015953</a>",
|
||||
"access.form.desec_token.label": "deSEC Token",
|
||||
"access.form.desec_token.placeholder": "请输入 deSEC Token",
|
||||
"access.form.desec_token.tooltip": "这是什么?请参阅 <a href=\"https://desec.readthedocs.io/en/latest/auth/tokens.html#manage-tokens\" target=\"_blank\">https://desec.readthedocs.io/en/latest/auth/tokens.html</a>",
|
||||
|
@ -57,9 +57,11 @@
|
||||
"provider.cloudflare": "Cloudflare",
|
||||
"provider.cloudns": "ClouDNS",
|
||||
"provider.cmcccloud": "移动云",
|
||||
"provider.cmcccloud.dns": "移动云 - 云解析 DNS",
|
||||
"provider.constellix": "Constellix",
|
||||
"provider.ctcccloud": "联通云",
|
||||
"provider.cucccloud": "天翼云",
|
||||
"provider.ctcccloud": "天翼云",
|
||||
"provider.ctcccloud.smartdns": "天翼云 - 智能 DNS",
|
||||
"provider.cucccloud": "联通云",
|
||||
"provider.desec": "deSEC",
|
||||
"provider.digitalocean": "DigitalOcean",
|
||||
"provider.dingtalkbot": "钉钉群机器人",
|
||||
|
Loading…
x
Reference in New Issue
Block a user