feat: support configuring dns ttl in application

This commit is contained in:
Fu Diwei 2025-01-19 05:01:36 +08:00
parent c1f77dd92f
commit 60a13aaf17
20 changed files with 282 additions and 177 deletions

View File

@ -35,15 +35,17 @@ type Applicant interface {
} }
type applicantOptions struct { type applicantOptions struct {
Domains []string Domains []string
ContactEmail string ContactEmail string
Provider domain.ApplyDNSProviderType Provider domain.ApplyDNSProviderType
ProviderAccessConfig map[string]any ProviderAccessConfig map[string]any
ProviderApplyConfig map[string]any ProviderApplyConfig map[string]any
KeyAlgorithm string KeyAlgorithm string
Nameservers []string Nameservers []string
PropagationTimeout int32 DnsPropagationTimeout int32
DisableFollowCNAME bool DnsTTL int32
DisableFollowCNAME bool
SkipBeforeExpiryDays int32
} }
func NewWithApplyNode(node *domain.WorkflowNode) (Applicant, error) { func NewWithApplyNode(node *domain.WorkflowNode) (Applicant, error) {
@ -65,15 +67,17 @@ func NewWithApplyNode(node *domain.WorkflowNode) (Applicant, error) {
} }
options := &applicantOptions{ options := &applicantOptions{
Domains: slices.Filter(strings.Split(nodeConfig.Domains, ";"), func(s string) bool { return s != "" }), Domains: slices.Filter(strings.Split(nodeConfig.Domains, ";"), func(s string) bool { return s != "" }),
ContactEmail: nodeConfig.ContactEmail, ContactEmail: nodeConfig.ContactEmail,
Provider: domain.ApplyDNSProviderType(nodeConfig.Provider), Provider: domain.ApplyDNSProviderType(nodeConfig.Provider),
ProviderAccessConfig: accessConfig, ProviderAccessConfig: accessConfig,
ProviderApplyConfig: nodeConfig.ProviderConfig, ProviderApplyConfig: nodeConfig.ProviderConfig,
KeyAlgorithm: nodeConfig.KeyAlgorithm, KeyAlgorithm: nodeConfig.KeyAlgorithm,
Nameservers: slices.Filter(strings.Split(nodeConfig.Nameservers, ";"), func(s string) bool { return s != "" }), Nameservers: slices.Filter(strings.Split(nodeConfig.Nameservers, ";"), func(s string) bool { return s != "" }),
PropagationTimeout: nodeConfig.PropagationTimeout, DnsPropagationTimeout: nodeConfig.DnsPropagationTimeout,
DisableFollowCNAME: nodeConfig.DisableFollowCNAME, DnsTTL: nodeConfig.DnsTTL,
DisableFollowCNAME: nodeConfig.DisableFollowCNAME,
SkipBeforeExpiryDays: nodeConfig.SkipBeforeExpiryDays,
} }
applicant, err := createApplicant(options) applicant, err := createApplicant(options)

View File

@ -36,11 +36,11 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
} }
applicant, err := providerACMEHttpReq.NewChallengeProvider(&providerACMEHttpReq.ACMEHttpReqApplicantConfig{ applicant, err := providerACMEHttpReq.NewChallengeProvider(&providerACMEHttpReq.ACMEHttpReqApplicantConfig{
Endpoint: access.Endpoint, Endpoint: access.Endpoint,
Mode: access.Mode, Mode: access.Mode,
Username: access.Username, Username: access.Username,
Password: access.Password, Password: access.Password,
PropagationTimeout: options.PropagationTimeout, DnsPropagationTimeout: options.DnsPropagationTimeout,
}) })
return applicant, err return applicant, err
} }
@ -53,9 +53,10 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
} }
applicant, err := providerAliyun.NewChallengeProvider(&providerAliyun.AliyunApplicantConfig{ applicant, err := providerAliyun.NewChallengeProvider(&providerAliyun.AliyunApplicantConfig{
AccessKeyId: access.AccessKeyId, AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret, AccessKeySecret: access.AccessKeySecret,
PropagationTimeout: options.PropagationTimeout, DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
}) })
return applicant, err return applicant, err
} }
@ -68,11 +69,12 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
} }
applicant, err := providerAWSRoute53.NewChallengeProvider(&providerAWSRoute53.AWSRoute53ApplicantConfig{ applicant, err := providerAWSRoute53.NewChallengeProvider(&providerAWSRoute53.AWSRoute53ApplicantConfig{
AccessKeyId: access.AccessKeyId, AccessKeyId: access.AccessKeyId,
SecretAccessKey: access.SecretAccessKey, SecretAccessKey: access.SecretAccessKey,
Region: maps.GetValueAsString(options.ProviderApplyConfig, "region"), Region: maps.GetValueAsString(options.ProviderApplyConfig, "region"),
HostedZoneId: maps.GetValueAsString(options.ProviderApplyConfig, "hostedZoneId"), HostedZoneId: maps.GetValueAsString(options.ProviderApplyConfig, "hostedZoneId"),
PropagationTimeout: options.PropagationTimeout, DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
}) })
return applicant, err return applicant, err
} }
@ -85,11 +87,12 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
} }
applicant, err := providerAzureDNS.NewChallengeProvider(&providerAzureDNS.AzureDNSApplicantConfig{ applicant, err := providerAzureDNS.NewChallengeProvider(&providerAzureDNS.AzureDNSApplicantConfig{
TenantId: access.TenantId, TenantId: access.TenantId,
ClientId: access.ClientId, ClientId: access.ClientId,
ClientSecret: access.ClientSecret, ClientSecret: access.ClientSecret,
CloudName: access.CloudName, CloudName: access.CloudName,
PropagationTimeout: options.PropagationTimeout, DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
}) })
return applicant, err return applicant, err
} }
@ -102,8 +105,9 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
} }
applicant, err := providerCloudflare.NewChallengeProvider(&providerCloudflare.CloudflareApplicantConfig{ applicant, err := providerCloudflare.NewChallengeProvider(&providerCloudflare.CloudflareApplicantConfig{
DnsApiToken: access.DnsApiToken, DnsApiToken: access.DnsApiToken,
PropagationTimeout: options.PropagationTimeout, DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
}) })
return applicant, err return applicant, err
} }
@ -116,9 +120,10 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
} }
applicant, err := providerGoDaddy.NewChallengeProvider(&providerGoDaddy.GoDaddyApplicantConfig{ applicant, err := providerGoDaddy.NewChallengeProvider(&providerGoDaddy.GoDaddyApplicantConfig{
ApiKey: access.ApiKey, ApiKey: access.ApiKey,
ApiSecret: access.ApiSecret, ApiSecret: access.ApiSecret,
PropagationTimeout: options.PropagationTimeout, DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
}) })
return applicant, err return applicant, err
} }
@ -131,10 +136,11 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
} }
applicant, err := providerHuaweiCloud.NewChallengeProvider(&providerHuaweiCloud.HuaweiCloudApplicantConfig{ applicant, err := providerHuaweiCloud.NewChallengeProvider(&providerHuaweiCloud.HuaweiCloudApplicantConfig{
AccessKeyId: access.AccessKeyId, AccessKeyId: access.AccessKeyId,
SecretAccessKey: access.SecretAccessKey, SecretAccessKey: access.SecretAccessKey,
Region: maps.GetValueAsString(options.ProviderApplyConfig, "region"), Region: maps.GetValueAsString(options.ProviderApplyConfig, "region"),
PropagationTimeout: options.PropagationTimeout, DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
}) })
return applicant, err return applicant, err
} }
@ -147,9 +153,10 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
} }
applicant, err := providerNameDotCom.NewChallengeProvider(&providerNameDotCom.NameDotComApplicantConfig{ applicant, err := providerNameDotCom.NewChallengeProvider(&providerNameDotCom.NameDotComApplicantConfig{
Username: access.Username, Username: access.Username,
ApiToken: access.ApiToken, ApiToken: access.ApiToken,
PropagationTimeout: options.PropagationTimeout, DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
}) })
return applicant, err return applicant, err
} }
@ -162,8 +169,9 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
} }
applicant, err := providerNameSilo.NewChallengeProvider(&providerNameSilo.NameSiloApplicantConfig{ applicant, err := providerNameSilo.NewChallengeProvider(&providerNameSilo.NameSiloApplicantConfig{
ApiKey: access.ApiKey, ApiKey: access.ApiKey,
PropagationTimeout: options.PropagationTimeout, DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
}) })
return applicant, err return applicant, err
} }
@ -176,8 +184,9 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
} }
applicant, err := providerNS1.NewChallengeProvider(&providerNS1.NS1ApplicantConfig{ applicant, err := providerNS1.NewChallengeProvider(&providerNS1.NS1ApplicantConfig{
ApiKey: access.ApiKey, ApiKey: access.ApiKey,
PropagationTimeout: options.PropagationTimeout, DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
}) })
return applicant, err return applicant, err
} }
@ -190,9 +199,10 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
} }
applicant, err := providerPowerDNS.NewChallengeProvider(&providerPowerDNS.PowerDNSApplicantConfig{ applicant, err := providerPowerDNS.NewChallengeProvider(&providerPowerDNS.PowerDNSApplicantConfig{
ApiUrl: access.ApiUrl, ApiUrl: access.ApiUrl,
ApiKey: access.ApiKey, ApiKey: access.ApiKey,
PropagationTimeout: options.PropagationTimeout, DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
}) })
return applicant, err return applicant, err
} }
@ -205,9 +215,10 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
} }
applicant, err := providerTencentCloud.NewChallengeProvider(&providerTencentCloud.TencentCloudApplicantConfig{ applicant, err := providerTencentCloud.NewChallengeProvider(&providerTencentCloud.TencentCloudApplicantConfig{
SecretId: access.SecretId, SecretId: access.SecretId,
SecretKey: access.SecretKey, SecretKey: access.SecretKey,
PropagationTimeout: options.PropagationTimeout, DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
}) })
return applicant, err return applicant, err
} }
@ -220,9 +231,10 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
} }
applicant, err := providerVolcEngine.NewChallengeProvider(&providerVolcEngine.VolcEngineApplicantConfig{ applicant, err := providerVolcEngine.NewChallengeProvider(&providerVolcEngine.VolcEngineApplicantConfig{
AccessKeyId: access.AccessKeyId, AccessKeyId: access.AccessKeyId,
SecretAccessKey: access.SecretAccessKey, SecretAccessKey: access.SecretAccessKey,
PropagationTimeout: options.PropagationTimeout, DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
}) })
return applicant, err return applicant, err
} }

View File

@ -58,28 +58,31 @@ type WorkflowNode struct {
} }
type WorkflowNodeConfigForApply struct { type WorkflowNodeConfigForApply struct {
Domains string `json:"domains"` Domains string `json:"domains"` // 域名列表,以半角逗号分隔
ContactEmail string `json:"contactEmail"` ContactEmail string `json:"contactEmail"` // 联系邮箱
Provider string `json:"provider"` Provider string `json:"provider"` // DNS 提供商
ProviderAccessId string `json:"providerAccessId"` ProviderAccessId string `json:"providerAccessId"` // DNS 提供商授权记录 ID
ProviderConfig map[string]any `json:"providerConfig"` ProviderConfig map[string]any `json:"providerConfig"` // DNS 提供商额外配置
KeyAlgorithm string `json:"keyAlgorithm"` KeyAlgorithm string `json:"keyAlgorithm"` // 密钥算法
Nameservers string `json:"nameservers"` Nameservers string `json:"nameservers"` // DNS 服务器列表,以半角逗号分隔
PropagationTimeout int32 `json:"propagationTimeout"` DnsPropagationTimeout int32 `json:"dnsPropagationTimeout"` // DNS 传播超时时间(默认取决于提供商)
DisableFollowCNAME bool `json:"disableFollowCNAME"` DnsTTL int32 `json:"dnsTTL"` // DNS TTL默认取决于提供商
DisableFollowCNAME bool `json:"disableFollowCNAME"` // 是否禁用 CNAME 跟随
SkipBeforeExpiryDays int32 `json:"skipBeforeExpiryDays"` // TODO: 证书到期前多少天前跳过续期默认值30
} }
type WorkflowNodeConfigForDeploy struct { type WorkflowNodeConfigForDeploy struct {
Certificate string `json:"certificate"` Certificate string `json:"certificate"` // 前序节点输出的证书,形如“${NodeId}#certificate”
Provider string `json:"provider"` Provider string `json:"provider"` // 主机提供商
ProviderAccessId string `json:"providerAccessId"` ProviderAccessId string `json:"providerAccessId"` // 主机提供商授权记录 ID
ProviderConfig map[string]any `json:"providerConfig"` ProviderConfig map[string]any `json:"providerConfig"` // 主机提供商额外配置
SkipOnLastSucceeded bool `json:"skipOnLastSucceeded"` // TODO: 上次部署成功时是否跳过
} }
type WorkflowNodeConfigForNotify struct { type WorkflowNodeConfigForNotify struct {
Channel string `json:"channel"` Channel string `json:"channel"` // 通知渠道
Subject string `json:"subject"` Subject string `json:"subject"` // 通知主题
Message string `json:"message"` Message string `json:"message"` // 通知内容
} }
func (n *WorkflowNode) getConfigValueAsString(key string) string { func (n *WorkflowNode) getConfigValueAsString(key string) string {
@ -105,25 +108,33 @@ func (n *WorkflowNode) getConfigValueAsMap(key string) map[string]any {
} }
func (n *WorkflowNode) GetConfigForApply() WorkflowNodeConfigForApply { func (n *WorkflowNode) GetConfigForApply() WorkflowNodeConfigForApply {
skipBeforeExpiryDays := n.getConfigValueAsInt32("skipBeforeExpiryDays")
if skipBeforeExpiryDays == 0 {
skipBeforeExpiryDays = 30
}
return WorkflowNodeConfigForApply{ return WorkflowNodeConfigForApply{
Domains: n.getConfigValueAsString("domains"), Domains: n.getConfigValueAsString("domains"),
ContactEmail: n.getConfigValueAsString("contactEmail"), ContactEmail: n.getConfigValueAsString("contactEmail"),
Provider: n.getConfigValueAsString("provider"), Provider: n.getConfigValueAsString("provider"),
ProviderAccessId: n.getConfigValueAsString("providerAccessId"), ProviderAccessId: n.getConfigValueAsString("providerAccessId"),
ProviderConfig: n.getConfigValueAsMap("providerConfig"), ProviderConfig: n.getConfigValueAsMap("providerConfig"),
KeyAlgorithm: n.getConfigValueAsString("keyAlgorithm"), KeyAlgorithm: n.getConfigValueAsString("keyAlgorithm"),
Nameservers: n.getConfigValueAsString("nameservers"), Nameservers: n.getConfigValueAsString("nameservers"),
PropagationTimeout: n.getConfigValueAsInt32("propagationTimeout"), DnsPropagationTimeout: n.getConfigValueAsInt32("dnsPropagationTimeout"),
DisableFollowCNAME: n.getConfigValueAsBool("disableFollowCNAME"), DnsTTL: n.getConfigValueAsInt32("dnsTTL"),
DisableFollowCNAME: n.getConfigValueAsBool("disableFollowCNAME"),
SkipBeforeExpiryDays: skipBeforeExpiryDays,
} }
} }
func (n *WorkflowNode) GetConfigForDeploy() WorkflowNodeConfigForDeploy { func (n *WorkflowNode) GetConfigForDeploy() WorkflowNodeConfigForDeploy {
return WorkflowNodeConfigForDeploy{ return WorkflowNodeConfigForDeploy{
Certificate: n.getConfigValueAsString("certificate"), Certificate: n.getConfigValueAsString("certificate"),
Provider: n.getConfigValueAsString("provider"), Provider: n.getConfigValueAsString("provider"),
ProviderAccessId: n.getConfigValueAsString("providerAccessId"), ProviderAccessId: n.getConfigValueAsString("providerAccessId"),
ProviderConfig: n.getConfigValueAsMap("providerConfig"), ProviderConfig: n.getConfigValueAsMap("providerConfig"),
SkipOnLastSucceeded: n.getConfigValueAsBool("skipOnLastSucceeded"),
} }
} }

View File

@ -10,11 +10,11 @@ import (
) )
type ACMEHttpReqApplicantConfig struct { type ACMEHttpReqApplicantConfig struct {
Endpoint string `json:"endpoint"` Endpoint string `json:"endpoint"`
Mode string `json:"mode"` Mode string `json:"mode"`
Username string `json:"username"` Username string `json:"username"`
Password string `json:"password"` Password string `json:"password"`
PropagationTimeout int32 `json:"propagationTimeout,omitempty"` DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
} }
func NewChallengeProvider(config *ACMEHttpReqApplicantConfig) (challenge.Provider, error) { func NewChallengeProvider(config *ACMEHttpReqApplicantConfig) (challenge.Provider, error) {
@ -28,8 +28,8 @@ func NewChallengeProvider(config *ACMEHttpReqApplicantConfig) (challenge.Provide
providerConfig.Mode = config.Mode providerConfig.Mode = config.Mode
providerConfig.Username = config.Username providerConfig.Username = config.Username
providerConfig.Password = config.Password providerConfig.Password = config.Password
if config.PropagationTimeout != 0 { if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.PropagationTimeout) * time.Second providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
} }
provider, err := httpreq.NewDNSProviderConfig(providerConfig) provider, err := httpreq.NewDNSProviderConfig(providerConfig)

View File

@ -9,9 +9,10 @@ import (
) )
type AliyunApplicantConfig struct { type AliyunApplicantConfig struct {
AccessKeyId string `json:"accessKeyId"` AccessKeyId string `json:"accessKeyId"`
AccessKeySecret string `json:"accessKeySecret"` AccessKeySecret string `json:"accessKeySecret"`
PropagationTimeout int32 `json:"propagationTimeout,omitempty"` DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
} }
func NewChallengeProvider(config *AliyunApplicantConfig) (challenge.Provider, error) { func NewChallengeProvider(config *AliyunApplicantConfig) (challenge.Provider, error) {
@ -22,8 +23,11 @@ func NewChallengeProvider(config *AliyunApplicantConfig) (challenge.Provider, er
providerConfig := alidns.NewDefaultConfig() providerConfig := alidns.NewDefaultConfig()
providerConfig.APIKey = config.AccessKeyId providerConfig.APIKey = config.AccessKeyId
providerConfig.SecretKey = config.AccessKeySecret providerConfig.SecretKey = config.AccessKeySecret
if config.PropagationTimeout != 0 { if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.PropagationTimeout) * time.Second providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
} }
provider, err := alidns.NewDNSProviderConfig(providerConfig) provider, err := alidns.NewDNSProviderConfig(providerConfig)

View File

@ -9,11 +9,12 @@ import (
) )
type AWSRoute53ApplicantConfig struct { type AWSRoute53ApplicantConfig struct {
AccessKeyId string `json:"accessKeyId"` AccessKeyId string `json:"accessKeyId"`
SecretAccessKey string `json:"secretAccessKey"` SecretAccessKey string `json:"secretAccessKey"`
Region string `json:"region"` Region string `json:"region"`
HostedZoneId string `json:"hostedZoneId"` HostedZoneId string `json:"hostedZoneId"`
PropagationTimeout int32 `json:"propagationTimeout,omitempty"` DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
} }
func NewChallengeProvider(config *AWSRoute53ApplicantConfig) (challenge.Provider, error) { func NewChallengeProvider(config *AWSRoute53ApplicantConfig) (challenge.Provider, error) {
@ -26,8 +27,11 @@ func NewChallengeProvider(config *AWSRoute53ApplicantConfig) (challenge.Provider
providerConfig.SecretAccessKey = config.SecretAccessKey providerConfig.SecretAccessKey = config.SecretAccessKey
providerConfig.Region = config.Region providerConfig.Region = config.Region
providerConfig.HostedZoneID = config.HostedZoneId providerConfig.HostedZoneID = config.HostedZoneId
if config.PropagationTimeout != 0 { if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.PropagationTimeout) * time.Second providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
} }
provider, err := route53.NewDNSProviderConfig(providerConfig) provider, err := route53.NewDNSProviderConfig(providerConfig)

View File

@ -12,11 +12,12 @@ import (
) )
type AzureDNSApplicantConfig struct { type AzureDNSApplicantConfig struct {
TenantId string `json:"tenantId"` TenantId string `json:"tenantId"`
ClientId string `json:"clientId"` ClientId string `json:"clientId"`
ClientSecret string `json:"clientSecret"` ClientSecret string `json:"clientSecret"`
CloudName string `json:"cloudName,omitempty"` CloudName string `json:"cloudName,omitempty"`
PropagationTimeout int32 `json:"propagationTimeout,omitempty"` DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
} }
func NewChallengeProvider(config *AzureDNSApplicantConfig) (challenge.Provider, error) { func NewChallengeProvider(config *AzureDNSApplicantConfig) (challenge.Provider, error) {
@ -40,8 +41,11 @@ func NewChallengeProvider(config *AzureDNSApplicantConfig) (challenge.Provider,
return nil, fmt.Errorf("azuredns: unknown environment %s", config.CloudName) return nil, fmt.Errorf("azuredns: unknown environment %s", config.CloudName)
} }
} }
if config.PropagationTimeout != 0 { if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.PropagationTimeout) * time.Second providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
} }
provider, err := azuredns.NewDNSProviderConfig(providerConfig) provider, err := azuredns.NewDNSProviderConfig(providerConfig)

View File

@ -9,8 +9,9 @@ import (
) )
type CloudflareApplicantConfig struct { type CloudflareApplicantConfig struct {
DnsApiToken string `json:"dnsApiToken"` DnsApiToken string `json:"dnsApiToken"`
PropagationTimeout int32 `json:"propagationTimeout,omitempty"` DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
} }
func NewChallengeProvider(config *CloudflareApplicantConfig) (challenge.Provider, error) { func NewChallengeProvider(config *CloudflareApplicantConfig) (challenge.Provider, error) {
@ -20,8 +21,11 @@ func NewChallengeProvider(config *CloudflareApplicantConfig) (challenge.Provider
providerConfig := cloudflare.NewDefaultConfig() providerConfig := cloudflare.NewDefaultConfig()
providerConfig.AuthToken = config.DnsApiToken providerConfig.AuthToken = config.DnsApiToken
if config.PropagationTimeout != 0 { if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.PropagationTimeout) * time.Second providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
} }
provider, err := cloudflare.NewDNSProviderConfig(providerConfig) provider, err := cloudflare.NewDNSProviderConfig(providerConfig)

View File

@ -9,9 +9,10 @@ import (
) )
type GoDaddyApplicantConfig struct { type GoDaddyApplicantConfig struct {
ApiKey string `json:"apiKey"` ApiKey string `json:"apiKey"`
ApiSecret string `json:"apiSecret"` ApiSecret string `json:"apiSecret"`
PropagationTimeout int32 `json:"propagationTimeout,omitempty"` DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
} }
func NewChallengeProvider(config *GoDaddyApplicantConfig) (challenge.Provider, error) { func NewChallengeProvider(config *GoDaddyApplicantConfig) (challenge.Provider, error) {
@ -22,8 +23,11 @@ func NewChallengeProvider(config *GoDaddyApplicantConfig) (challenge.Provider, e
providerConfig := godaddy.NewDefaultConfig() providerConfig := godaddy.NewDefaultConfig()
providerConfig.APIKey = config.ApiKey providerConfig.APIKey = config.ApiKey
providerConfig.APISecret = config.ApiSecret providerConfig.APISecret = config.ApiSecret
if config.PropagationTimeout != 0 { if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.PropagationTimeout) * time.Second providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
} }
provider, err := godaddy.NewDNSProviderConfig(providerConfig) provider, err := godaddy.NewDNSProviderConfig(providerConfig)

View File

@ -9,10 +9,11 @@ import (
) )
type HuaweiCloudApplicantConfig struct { type HuaweiCloudApplicantConfig struct {
AccessKeyId string `json:"accessKeyId"` AccessKeyId string `json:"accessKeyId"`
SecretAccessKey string `json:"secretAccessKey"` SecretAccessKey string `json:"secretAccessKey"`
Region string `json:"region"` Region string `json:"region"`
PropagationTimeout int32 `json:"propagationTimeout,omitempty"` DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
} }
func NewChallengeProvider(config *HuaweiCloudApplicantConfig) (challenge.Provider, error) { func NewChallengeProvider(config *HuaweiCloudApplicantConfig) (challenge.Provider, error) {
@ -30,8 +31,11 @@ func NewChallengeProvider(config *HuaweiCloudApplicantConfig) (challenge.Provide
providerConfig.AccessKeyID = config.AccessKeyId providerConfig.AccessKeyID = config.AccessKeyId
providerConfig.SecretAccessKey = config.SecretAccessKey providerConfig.SecretAccessKey = config.SecretAccessKey
providerConfig.Region = region providerConfig.Region = region
if config.PropagationTimeout != 0 { if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.PropagationTimeout) * time.Second providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = config.DnsTTL
} }
provider, err := hwc.NewDNSProviderConfig(providerConfig) provider, err := hwc.NewDNSProviderConfig(providerConfig)

View File

@ -9,9 +9,10 @@ import (
) )
type NameDotComApplicantConfig struct { type NameDotComApplicantConfig struct {
Username string `json:"username"` Username string `json:"username"`
ApiToken string `json:"apiToken"` ApiToken string `json:"apiToken"`
PropagationTimeout int32 `json:"propagationTimeout,omitempty"` DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
} }
func NewChallengeProvider(config *NameDotComApplicantConfig) (challenge.Provider, error) { func NewChallengeProvider(config *NameDotComApplicantConfig) (challenge.Provider, error) {
@ -22,8 +23,11 @@ func NewChallengeProvider(config *NameDotComApplicantConfig) (challenge.Provider
providerConfig := namedotcom.NewDefaultConfig() providerConfig := namedotcom.NewDefaultConfig()
providerConfig.Username = config.Username providerConfig.Username = config.Username
providerConfig.APIToken = config.ApiToken providerConfig.APIToken = config.ApiToken
if config.PropagationTimeout != 0 { if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.PropagationTimeout) * time.Second providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
} }
provider, err := namedotcom.NewDNSProviderConfig(providerConfig) provider, err := namedotcom.NewDNSProviderConfig(providerConfig)

View File

@ -9,8 +9,9 @@ import (
) )
type NameSiloApplicantConfig struct { type NameSiloApplicantConfig struct {
ApiKey string `json:"apiKey"` ApiKey string `json:"apiKey"`
PropagationTimeout int32 `json:"propagationTimeout,omitempty"` DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
} }
func NewChallengeProvider(config *NameSiloApplicantConfig) (challenge.Provider, error) { func NewChallengeProvider(config *NameSiloApplicantConfig) (challenge.Provider, error) {
@ -20,8 +21,11 @@ func NewChallengeProvider(config *NameSiloApplicantConfig) (challenge.Provider,
providerConfig := namesilo.NewDefaultConfig() providerConfig := namesilo.NewDefaultConfig()
providerConfig.APIKey = config.ApiKey providerConfig.APIKey = config.ApiKey
if config.PropagationTimeout != 0 { if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.PropagationTimeout) * time.Second providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
} }
provider, err := namesilo.NewDNSProviderConfig(providerConfig) provider, err := namesilo.NewDNSProviderConfig(providerConfig)

View File

@ -9,8 +9,9 @@ import (
) )
type NS1ApplicantConfig struct { type NS1ApplicantConfig struct {
ApiKey string `json:"apiKey"` ApiKey string `json:"apiKey"`
PropagationTimeout int32 `json:"propagationTimeout,omitempty"` DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
} }
func NewChallengeProvider(config *NS1ApplicantConfig) (challenge.Provider, error) { func NewChallengeProvider(config *NS1ApplicantConfig) (challenge.Provider, error) {
@ -20,8 +21,11 @@ func NewChallengeProvider(config *NS1ApplicantConfig) (challenge.Provider, error
providerConfig := ns1.NewDefaultConfig() providerConfig := ns1.NewDefaultConfig()
providerConfig.APIKey = config.ApiKey providerConfig.APIKey = config.ApiKey
if config.PropagationTimeout != 0 { if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.PropagationTimeout) * time.Second providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
} }
provider, err := ns1.NewDNSProviderConfig(providerConfig) provider, err := ns1.NewDNSProviderConfig(providerConfig)

View File

@ -10,9 +10,10 @@ import (
) )
type PowerDNSApplicantConfig struct { type PowerDNSApplicantConfig struct {
ApiUrl string `json:"apiUrl"` ApiUrl string `json:"apiUrl"`
ApiKey string `json:"apiKey"` ApiKey string `json:"apiKey"`
PropagationTimeout int32 `json:"propagationTimeout,omitempty"` DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
} }
func NewChallengeProvider(config *PowerDNSApplicantConfig) (challenge.Provider, error) { func NewChallengeProvider(config *PowerDNSApplicantConfig) (challenge.Provider, error) {
@ -24,8 +25,11 @@ func NewChallengeProvider(config *PowerDNSApplicantConfig) (challenge.Provider,
providerConfig := pdns.NewDefaultConfig() providerConfig := pdns.NewDefaultConfig()
providerConfig.Host = host providerConfig.Host = host
providerConfig.APIKey = config.ApiKey providerConfig.APIKey = config.ApiKey
if config.PropagationTimeout != 0 { if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.PropagationTimeout) * time.Second providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
} }
provider, err := pdns.NewDNSProviderConfig(providerConfig) provider, err := pdns.NewDNSProviderConfig(providerConfig)

View File

@ -9,9 +9,10 @@ import (
) )
type TencentCloudApplicantConfig struct { type TencentCloudApplicantConfig struct {
SecretId string `json:"secretId"` SecretId string `json:"secretId"`
SecretKey string `json:"secretKey"` SecretKey string `json:"secretKey"`
PropagationTimeout int32 `json:"propagationTimeout,omitempty"` DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
} }
func NewChallengeProvider(config *TencentCloudApplicantConfig) (challenge.Provider, error) { func NewChallengeProvider(config *TencentCloudApplicantConfig) (challenge.Provider, error) {
@ -22,8 +23,11 @@ func NewChallengeProvider(config *TencentCloudApplicantConfig) (challenge.Provid
providerConfig := tencentcloud.NewDefaultConfig() providerConfig := tencentcloud.NewDefaultConfig()
providerConfig.SecretID = config.SecretId providerConfig.SecretID = config.SecretId
providerConfig.SecretKey = config.SecretKey providerConfig.SecretKey = config.SecretKey
if config.PropagationTimeout != 0 { if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.PropagationTimeout) * time.Second providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
} }
provider, err := tencentcloud.NewDNSProviderConfig(providerConfig) provider, err := tencentcloud.NewDNSProviderConfig(providerConfig)

View File

@ -9,9 +9,10 @@ import (
) )
type VolcEngineApplicantConfig struct { type VolcEngineApplicantConfig struct {
AccessKeyId string `json:"accessKeyId"` AccessKeyId string `json:"accessKeyId"`
SecretAccessKey string `json:"secretAccessKey"` SecretAccessKey string `json:"secretAccessKey"`
PropagationTimeout int32 `json:"propagationTimeout,omitempty"` DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
} }
func NewChallengeProvider(config *VolcEngineApplicantConfig) (challenge.Provider, error) { func NewChallengeProvider(config *VolcEngineApplicantConfig) (challenge.Provider, error) {
@ -22,8 +23,11 @@ func NewChallengeProvider(config *VolcEngineApplicantConfig) (challenge.Provider
providerConfig := volcengine.NewDefaultConfig() providerConfig := volcengine.NewDefaultConfig()
providerConfig.AccessKey = config.AccessKeyId providerConfig.AccessKey = config.AccessKeyId
providerConfig.SecretKey = config.SecretAccessKey providerConfig.SecretKey = config.SecretAccessKey
if config.PropagationTimeout != 0 { if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.PropagationTimeout) * time.Second providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
} }
provider, err := volcengine.NewDNSProviderConfig(providerConfig) provider, err := volcengine.NewDNSProviderConfig(providerConfig)

View File

@ -42,7 +42,6 @@ const MULTIPLE_INPUT_DELIMITER = ";";
const initFormModel = (): ApplyNodeConfigFormFieldValues => { const initFormModel = (): ApplyNodeConfigFormFieldValues => {
return { return {
keyAlgorithm: "RSA2048", keyAlgorithm: "RSA2048",
propagationTimeout: 60,
disableFollowCNAME: true, disableFollowCNAME: true,
}; };
}; };
@ -77,10 +76,16 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
.split(MULTIPLE_INPUT_DELIMITER) .split(MULTIPLE_INPUT_DELIMITER)
.every((e) => validIPv4Address(e) || validIPv6Address(e) || validDomainName(e)); .every((e) => validIPv4Address(e) || validIPv6Address(e) || validDomainName(e));
}, t("common.errmsg.host_invalid")), }, t("common.errmsg.host_invalid")),
propagationTimeout: z dnsPropagationTimeout: z
.union([ .union([
z.number().int().gte(1, t("workflow_node.apply.form.propagation_timeout.placeholder")), z.number().int().gte(1, t("workflow_node.apply.form.dns_propagation_timeout.placeholder")),
z.string().refine((v) => !v || /^[1-9]\d*$/.test(v), t("workflow_node.apply.form.propagation_timeout.placeholder")), z.string().refine((v) => !v || /^[1-9]\d*$/.test(v), t("workflow_node.apply.form.dns_propagation_timeout.placeholder")),
])
.nullish(),
dnsTTL: z
.union([
z.number().int().gte(1, t("workflow_node.apply.form.dns_ttl.placeholder")),
z.string().refine((v) => !v || /^[1-9]\d*$/.test(v), t("workflow_node.apply.form.dns_ttl.placeholder")),
]) ])
.nullish(), .nullish(),
disableFollowCNAME: z.boolean().nullish(), disableFollowCNAME: z.boolean().nullish(),
@ -313,18 +318,34 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
</Form.Item> </Form.Item>
<Form.Item <Form.Item
name="propagationTimeout" name="dnsPropagationTimeout"
label={t("workflow_node.apply.form.propagation_timeout.label")} label={t("workflow_node.apply.form.dns_propagation_timeout.label")}
rules={[formRule]} rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.apply.form.propagation_timeout.tooltip") }}></span>} tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.apply.form.dns_propagation_timeout.tooltip") }}></span>}
> >
<Input <Input
type="number" type="number"
allowClear allowClear
min={0} min={0}
max={3600} max={3600}
placeholder={t("workflow_node.apply.form.propagation_timeout.placeholder")} placeholder={t("workflow_node.apply.form.dns_propagation_timeout.placeholder")}
addonAfter={t("workflow_node.apply.form.propagation_timeout.suffix")} addonAfter={t("workflow_node.apply.form.dns_propagation_timeout.suffix")}
/>
</Form.Item>
<Form.Item
name="dnsTTL"
label={t("workflow_node.apply.form.dns_ttl.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.apply.form.dns_ttl.tooltip") }}></span>}
>
<Input
type="number"
allowClear
min={0}
max={86400}
placeholder={t("workflow_node.apply.form.dns_ttl.placeholder")}
addonAfter={t("workflow_node.apply.form.dns_ttl.suffix")}
/> />
</Form.Item> </Form.Item>

View File

@ -108,7 +108,8 @@ export type WorkflowNodeConfigForApply = {
providerConfig?: Record<string, unknown>; providerConfig?: Record<string, unknown>;
keyAlgorithm: string; keyAlgorithm: string;
nameservers?: string; nameservers?: string;
propagationTimeout?: number; dnsPropagationTimeout?: number;
dnsTTL?: number;
disableFollowCNAME?: boolean; disableFollowCNAME?: boolean;
}; };

View File

@ -54,10 +54,14 @@
"workflow_node.apply.form.nameservers.tooltip": "It determines whether to custom DNS recursive nameservers during ACME DNS-01 authentication. If you don't understand this option, just keep it by default.<br><a href=\"https://go-acme.github.io/lego/usage/cli/options/index.html#dns-resolvers-and-challenge-verification\" target=\"_blank\">Learn more</a>.", "workflow_node.apply.form.nameservers.tooltip": "It determines whether to custom DNS recursive nameservers during ACME DNS-01 authentication. If you don't understand this option, just keep it by default.<br><a href=\"https://go-acme.github.io/lego/usage/cli/options/index.html#dns-resolvers-and-challenge-verification\" target=\"_blank\">Learn more</a>.",
"workflow_node.apply.form.nameservers.multiple_input_modal.title": "Change DNS rcursive nameservers", "workflow_node.apply.form.nameservers.multiple_input_modal.title": "Change DNS rcursive nameservers",
"workflow_node.apply.form.nameservers.multiple_input_modal.placeholder": "Please enter DNS recursive nameserver", "workflow_node.apply.form.nameservers.multiple_input_modal.placeholder": "Please enter DNS recursive nameserver",
"workflow_node.apply.form.propagation_timeout.label": "DNS propagation timeout (Optional)", "workflow_node.apply.form.dns_propagation_timeout.label": "DNS propagation timeout (Optional)",
"workflow_node.apply.form.propagation_timeout.placeholder": "Please enter DNS propagation timeout", "workflow_node.apply.form.dns_propagation_timeout.placeholder": "Please enter DNS propagation timeout",
"workflow_node.apply.form.propagation_timeout.suffix": "seconds", "workflow_node.apply.form.dns_propagation_timeout.suffix": "seconds",
"workflow_node.apply.form.propagation_timeout.tooltip": "It determines the maximum waiting time for DNS propagation checks during ACME DNS-01 authentication. If you don't understand this option, just keep it by default.", "workflow_node.apply.form.dns_propagation_timeout.tooltip": "It determines the maximum waiting time for DNS propagation checks during ACME DNS-01 authentication. If you don't understand this option, just keep it by default.<br><br>Leave blank to use the default value provided by the provider.",
"workflow_node.apply.form.dns_ttl.label": "DNS TTL (Optional)",
"workflow_node.apply.form.dns_ttl.placeholder": "Please enter DNS TTL",
"workflow_node.apply.form.dns_ttl.suffix": "seconds",
"workflow_node.apply.form.dns_ttl.tooltip": "It determines the time to live for DNS record during ACME DNS-01 authentication. If you don't understand this option, just keep it by default.<br><br>Leave blank to use the default value provided by the provider.",
"workflow_node.apply.form.disable_follow_cname.label": "Disable CNAME following", "workflow_node.apply.form.disable_follow_cname.label": "Disable CNAME following",
"workflow_node.apply.form.disable_follow_cname.tooltip": "It determines whether to disable CNAME following during ACME DNS-01 authentication. If you don't understand this option, just keep it by default.<br><a href=\"https://letsencrypt.org/2019/10/09/onboarding-your-customers-with-lets-encrypt-and-acme/#the-advantages-of-a-cname\" target=\"_blank\">Learn more</a>.", "workflow_node.apply.form.disable_follow_cname.tooltip": "It determines whether to disable CNAME following during ACME DNS-01 authentication. If you don't understand this option, just keep it by default.<br><a href=\"https://letsencrypt.org/2019/10/09/onboarding-your-customers-with-lets-encrypt-and-acme/#the-advantages-of-a-cname\" target=\"_blank\">Learn more</a>.",

View File

@ -54,10 +54,14 @@
"workflow_node.apply.form.nameservers.tooltip": "在 ACME DNS-01 认证时使用自定义的 DNS 递归服务器。如果你不了解该选项的用途,保持默认即可。<br><a href=\"https://go-acme.github.io/lego/usage/cli/options/index.html#dns-resolvers-and-challenge-verification\" target=\"_blank\">点此了解更多</a>。", "workflow_node.apply.form.nameservers.tooltip": "在 ACME DNS-01 认证时使用自定义的 DNS 递归服务器。如果你不了解该选项的用途,保持默认即可。<br><a href=\"https://go-acme.github.io/lego/usage/cli/options/index.html#dns-resolvers-and-challenge-verification\" target=\"_blank\">点此了解更多</a>。",
"workflow_node.apply.form.nameservers.multiple_input_modal.title": "修改 DNS 递归服务器", "workflow_node.apply.form.nameservers.multiple_input_modal.title": "修改 DNS 递归服务器",
"workflow_node.apply.form.nameservers.multiple_input_modal.placeholder": "请输入 DNS 递归服务器", "workflow_node.apply.form.nameservers.multiple_input_modal.placeholder": "请输入 DNS 递归服务器",
"workflow_node.apply.form.propagation_timeout.label": "DNS 传播检查超时时间(可选)", "workflow_node.apply.form.dns_propagation_timeout.label": "DNS 传播检查超时时间(可选)",
"workflow_node.apply.form.propagation_timeout.placeholder": "请输入 DNS 传播检查超时时间", "workflow_node.apply.form.dns_propagation_timeout.placeholder": "请输入 DNS 传播检查超时时间",
"workflow_node.apply.form.propagation_timeout.suffix": "秒", "workflow_node.apply.form.dns_propagation_timeout.suffix": "秒",
"workflow_node.apply.form.propagation_timeout.tooltip": "在 ACME DNS-01 认证时等待 DNS 传播检查的最长时间。如果你不了解此选项的用途,保持默认即可。", "workflow_node.apply.form.dns_propagation_timeout.tooltip": "在 ACME DNS-01 认证时等待 DNS 传播检查的最长时间。如果你不了解此选项的用途,保持默认即可。<br><br>为空时,将使用提供商提供的默认值。",
"workflow_node.apply.form.dns_ttl.label": "DNS 解析 TTL可选",
"workflow_node.apply.form.dns_ttl.placeholder": "请输入 DNS 解析 TTL",
"workflow_node.apply.form.dns_ttl.suffix": "秒",
"workflow_node.apply.form.dns_ttl.tooltip": "在 ACME DNS-01 认证时 DNS 解析记录的 TTL。如果你不了解此选项的用途保持默认即可。<br><br>为空时,将使用提供商提供的默认值。",
"workflow_node.apply.form.disable_follow_cname.label": "禁止 CNAME 跟随", "workflow_node.apply.form.disable_follow_cname.label": "禁止 CNAME 跟随",
"workflow_node.apply.form.disable_follow_cname.tooltip": "在 ACME DNS-01 认证时是否禁止 CNAME 跟随。如果你不了解该选项的用途,保持默认即可。<br><a href=\"https://letsencrypt.org/2019/10/09/onboarding-your-customers-with-lets-encrypt-and-acme/#the-advantages-of-a-cname\" target=\"_blank\">点此了解更多</a>。", "workflow_node.apply.form.disable_follow_cname.tooltip": "在 ACME DNS-01 认证时是否禁止 CNAME 跟随。如果你不了解该选项的用途,保持默认即可。<br><a href=\"https://letsencrypt.org/2019/10/09/onboarding-your-customers-with-lets-encrypt-and-acme/#the-advantages-of-a-cname\" target=\"_blank\">点此了解更多</a>。",