feat: extract some configs from access to apply logic

This commit is contained in:
Fu Diwei 2025-01-11 16:31:28 +08:00
parent a0c08e841d
commit 598d0705fb
21 changed files with 647 additions and 427 deletions

1
go.mod
View File

@ -55,6 +55,7 @@ require (
github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.23.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible // indirect github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect github.com/golang/protobuf v1.5.4 // indirect
github.com/google/gnostic-models v0.6.8 // indirect github.com/google/gnostic-models v0.6.8 // indirect

2
go.sum
View File

@ -379,6 +379,8 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible h1:2cauKuaELYAEARXRkq2LrJ0yDDv1rW7+wrTEdVL3uaU= github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible h1:2cauKuaELYAEARXRkq2LrJ0yDDv1rW7+wrTEdVL3uaU=
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM= github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw=
github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM= github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM=
github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=

View File

@ -18,16 +18,6 @@ import (
"github.com/usual2970/certimate/internal/repository" "github.com/usual2970/certimate/internal/repository"
) )
type applyConfig struct {
Domains string
ContactEmail string
AccessConfig string
KeyAlgorithm string
Nameservers string
PropagationTimeout int32
DisableFollowCNAME bool
}
type ApplyCertResult struct { type ApplyCertResult struct {
Certificate string Certificate string
PrivateKey string PrivateKey string
@ -41,6 +31,18 @@ type Applicant interface {
Apply() (*ApplyCertResult, error) Apply() (*ApplyCertResult, error)
} }
type applicantOptions struct {
Domains []string
ContactEmail string
Provider domain.ApplyDNSProviderType
ProviderAccessConfig map[string]any
ProviderApplyConfig map[string]any
KeyAlgorithm string
Nameservers []string
PropagationTimeout int32
DisableFollowCNAME bool
}
func NewWithApplyNode(node *domain.WorkflowNode) (Applicant, error) { func NewWithApplyNode(node *domain.WorkflowNode) (Applicant, error) {
if node.Type != domain.WorkflowNodeTypeApply { if node.Type != domain.WorkflowNodeTypeApply {
return nil, fmt.Errorf("node type is not apply") return nil, fmt.Errorf("node type is not apply")
@ -53,29 +55,35 @@ func NewWithApplyNode(node *domain.WorkflowNode) (Applicant, error) {
return nil, fmt.Errorf("failed to get access #%s record: %w", accessId, err) return nil, fmt.Errorf("failed to get access #%s record: %w", accessId, err)
} }
applyProvider := node.GetConfigString("provider") accessConfig, err := access.UnmarshalConfigToMap()
applyConfig := &applyConfig{ if err != nil {
Domains: node.GetConfigString("domains"), return nil, fmt.Errorf("failed to unmarshal access config: %w", err)
}
options := &applicantOptions{
Domains: strings.Split(node.GetConfigString("domains"), ";"),
ContactEmail: node.GetConfigString("contactEmail"), ContactEmail: node.GetConfigString("contactEmail"),
AccessConfig: access.Config, Provider: domain.ApplyDNSProviderType(node.GetConfigString("provider")),
ProviderAccessConfig: accessConfig,
ProviderApplyConfig: node.GetConfigMap("providerConfig"),
KeyAlgorithm: node.GetConfigString("keyAlgorithm"), KeyAlgorithm: node.GetConfigString("keyAlgorithm"),
Nameservers: node.GetConfigString("nameservers"), Nameservers: strings.Split(node.GetConfigString("nameservers"), ";"),
PropagationTimeout: node.GetConfigInt32("propagationTimeout"), PropagationTimeout: node.GetConfigInt32("propagationTimeout"),
DisableFollowCNAME: node.GetConfigBool("disableFollowCNAME"), DisableFollowCNAME: node.GetConfigBool("disableFollowCNAME"),
} }
challengeProvider, err := createChallengeProvider(domain.ApplyDNSProviderType(applyProvider), access.Config, applyConfig) applicant, err := createApplicant(options)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &proxyApplicant{ return &proxyApplicant{
applicant: challengeProvider, applicant: applicant,
applyConfig: applyConfig, options: options,
}, nil }, nil
} }
func apply(challengeProvider challenge.Provider, applyConfig *applyConfig) (*ApplyCertResult, error) { func apply(challengeProvider challenge.Provider, options *applicantOptions) (*ApplyCertResult, error) {
settingsRepo := repository.NewSettingsRepository() settingsRepo := repository.NewSettingsRepository()
settings, _ := settingsRepo.GetByName(context.Background(), "sslProvider") settings, _ := settingsRepo.GetByName(context.Background(), "sslProvider")
@ -93,20 +101,20 @@ func apply(challengeProvider challenge.Provider, applyConfig *applyConfig) (*App
sslProviderConfig.Provider = defaultSSLProvider sslProviderConfig.Provider = defaultSSLProvider
} }
myUser, err := newAcmeUser(sslProviderConfig.Provider, applyConfig.ContactEmail) myUser, err := newAcmeUser(sslProviderConfig.Provider, options.ContactEmail)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Some unified lego environment variables are configured here. // Some unified lego environment variables are configured here.
// link: https://github.com/go-acme/lego/issues/1867 // link: https://github.com/go-acme/lego/issues/1867
os.Setenv("LEGO_DISABLE_CNAME_SUPPORT", strconv.FormatBool(applyConfig.DisableFollowCNAME)) os.Setenv("LEGO_DISABLE_CNAME_SUPPORT", strconv.FormatBool(options.DisableFollowCNAME))
config := lego.NewConfig(myUser) config := lego.NewConfig(myUser)
// This CA URL is configured for a local dev instance of Boulder running in Docker in a VM. // This CA URL is configured for a local dev instance of Boulder running in Docker in a VM.
config.CADirURL = sslProviderUrls[sslProviderConfig.Provider] config.CADirURL = sslProviderUrls[sslProviderConfig.Provider]
config.Certificate.KeyType = parseKeyAlgorithm(applyConfig.KeyAlgorithm) config.Certificate.KeyType = parseKeyAlgorithm(options.KeyAlgorithm)
// A client facilitates communication with the CA server. // A client facilitates communication with the CA server.
client, err := lego.NewClient(config) client, err := lego.NewClient(config)
@ -115,8 +123,8 @@ func apply(challengeProvider challenge.Provider, applyConfig *applyConfig) (*App
} }
challengeOptions := make([]dns01.ChallengeOption, 0) challengeOptions := make([]dns01.ChallengeOption, 0)
if len(applyConfig.Nameservers) > 0 { if len(options.Nameservers) > 0 {
challengeOptions = append(challengeOptions, dns01.AddRecursiveNameservers(dns01.ParseNameservers(strings.Split(applyConfig.Nameservers, ";")))) challengeOptions = append(challengeOptions, dns01.AddRecursiveNameservers(dns01.ParseNameservers(options.Nameservers)))
challengeOptions = append(challengeOptions, dns01.DisableAuthoritativeNssPropagationRequirement()) challengeOptions = append(challengeOptions, dns01.DisableAuthoritativeNssPropagationRequirement())
} }
@ -132,7 +140,7 @@ func apply(challengeProvider challenge.Provider, applyConfig *applyConfig) (*App
} }
certRequest := certificate.ObtainRequest{ certRequest := certificate.ObtainRequest{
Domains: strings.Split(applyConfig.Domains, ";"), Domains: options.Domains,
Bundle: true, Bundle: true,
} }
certResource, err := client.Certificate.Obtain(certRequest) certResource, err := client.Certificate.Obtain(certRequest)
@ -144,9 +152,9 @@ func apply(challengeProvider challenge.Provider, applyConfig *applyConfig) (*App
PrivateKey: string(certResource.PrivateKey), PrivateKey: string(certResource.PrivateKey),
Certificate: string(certResource.Certificate), Certificate: string(certResource.Certificate),
IssuerCertificate: string(certResource.IssuerCertificate), IssuerCertificate: string(certResource.IssuerCertificate),
CSR: string(certResource.CSR),
ACMECertUrl: certResource.CertURL, ACMECertUrl: certResource.CertURL,
ACMECertStableUrl: certResource.CertStableURL, ACMECertStableUrl: certResource.CertStableURL,
CSR: string(certResource.CSR),
}, nil }, nil
} }
@ -172,9 +180,9 @@ func parseKeyAlgorithm(algo string) certcrypto.KeyType {
// TODO: 暂时使用代理模式以兼容之前版本代码,后续重新实现此处逻辑 // TODO: 暂时使用代理模式以兼容之前版本代码,后续重新实现此处逻辑
type proxyApplicant struct { type proxyApplicant struct {
applicant challenge.Provider applicant challenge.Provider
applyConfig *applyConfig options *applicantOptions
} }
func (d *proxyApplicant) Apply() (*ApplyCertResult, error) { func (d *proxyApplicant) Apply() (*ApplyCertResult, error) {
return apply(d.applicant, d.applyConfig) return apply(d.applicant, d.options)
} }

View File

@ -1,7 +1,6 @@
package applicant package applicant
import ( import (
"encoding/json"
"fmt" "fmt"
"github.com/go-acme/lego/v4/challenge" "github.com/go-acme/lego/v4/challenge"
@ -18,19 +17,20 @@ import (
providerPowerDNS "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/powerdns" providerPowerDNS "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/powerdns"
providerTencentCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/tencentcloud" providerTencentCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/tencentcloud"
providerVolcEngine "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/volcengine" providerVolcEngine "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/volcengine"
"github.com/usual2970/certimate/internal/pkg/utils/maps"
) )
func createChallengeProvider(provider domain.ApplyDNSProviderType, accessConfig string, applyConfig *applyConfig) (challenge.Provider, error) { func createApplicant(options *applicantOptions) (challenge.Provider, error) {
/* /*
注意如果追加新的常量值请保持以 ASCII 排序 注意如果追加新的常量值请保持以 ASCII 排序
NOTICE: If you add new constant, please keep ASCII order. NOTICE: If you add new constant, please keep ASCII order.
*/ */
switch provider { switch options.Provider {
case domain.ApplyDNSProviderTypeACMEHttpReq: case domain.ApplyDNSProviderTypeACMEHttpReq:
{ {
access := &domain.AccessConfigForACMEHttpReq{} access := domain.AccessConfigForACMEHttpReq{}
if err := json.Unmarshal([]byte(accessConfig), access); err != nil { if err := maps.Decode(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to unmarshal access config: %w", err) return nil, fmt.Errorf("failed to decode provider access config: %w", err)
} }
applicant, err := providerACMEHttpReq.NewChallengeProvider(&providerACMEHttpReq.ACMEHttpReqApplicantConfig{ applicant, err := providerACMEHttpReq.NewChallengeProvider(&providerACMEHttpReq.ACMEHttpReqApplicantConfig{
@ -38,162 +38,162 @@ func createChallengeProvider(provider domain.ApplyDNSProviderType, accessConfig
Mode: access.Mode, Mode: access.Mode,
Username: access.Username, Username: access.Username,
Password: access.Password, Password: access.Password,
PropagationTimeout: applyConfig.PropagationTimeout, PropagationTimeout: options.PropagationTimeout,
}) })
return applicant, err return applicant, err
} }
case domain.ApplyDNSProviderTypeAliyun, domain.ApplyDNSProviderTypeAliyunDNS: case domain.ApplyDNSProviderTypeAliyun, domain.ApplyDNSProviderTypeAliyunDNS:
{ {
access := &domain.AccessConfigForAliyun{} access := domain.AccessConfigForAliyun{}
if err := json.Unmarshal([]byte(accessConfig), access); err != nil { if err := maps.Decode(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to unmarshal access config: %w", err) return nil, fmt.Errorf("failed to decode provider access config: %w", err)
} }
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: applyConfig.PropagationTimeout, PropagationTimeout: options.PropagationTimeout,
}) })
return applicant, err return applicant, err
} }
case domain.ApplyDNSProviderTypeAWS, domain.ApplyDNSProviderTypeAWSRoute53: case domain.ApplyDNSProviderTypeAWS, domain.ApplyDNSProviderTypeAWSRoute53:
{ {
access := &domain.AccessConfigForAWS{} access := domain.AccessConfigForAWS{}
if err := json.Unmarshal([]byte(accessConfig), access); err != nil { if err := maps.Decode(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to unmarshal access config: %w", err) return nil, fmt.Errorf("failed to decode provider access config: %w", err)
} }
applicant, err := providerAWS.NewChallengeProvider(&providerAWS.AWSApplicantConfig{ applicant, err := providerAWS.NewChallengeProvider(&providerAWS.AWSApplicantConfig{
AccessKeyId: access.AccessKeyId, AccessKeyId: access.AccessKeyId,
SecretAccessKey: access.SecretAccessKey, SecretAccessKey: access.SecretAccessKey,
Region: access.Region, Region: maps.GetValueAsString(options.ProviderApplyConfig, "region"),
HostedZoneId: access.HostedZoneId, HostedZoneId: maps.GetValueAsString(options.ProviderApplyConfig, "hostedZoneId"),
PropagationTimeout: applyConfig.PropagationTimeout, PropagationTimeout: options.PropagationTimeout,
}) })
return applicant, err return applicant, err
} }
case domain.ApplyDNSProviderTypeCloudflare: case domain.ApplyDNSProviderTypeCloudflare:
{ {
access := &domain.AccessConfigForCloudflare{} access := domain.AccessConfigForCloudflare{}
if err := json.Unmarshal([]byte(accessConfig), access); err != nil { if err := maps.Decode(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to unmarshal access config: %w", err) return nil, fmt.Errorf("failed to decode provider access config: %w", err)
} }
applicant, err := providerCloudflare.NewChallengeProvider(&providerCloudflare.CloudflareApplicantConfig{ applicant, err := providerCloudflare.NewChallengeProvider(&providerCloudflare.CloudflareApplicantConfig{
DnsApiToken: access.DnsApiToken, DnsApiToken: access.DnsApiToken,
PropagationTimeout: applyConfig.PropagationTimeout, PropagationTimeout: options.PropagationTimeout,
}) })
return applicant, err return applicant, err
} }
case domain.ApplyDNSProviderTypeGoDaddy: case domain.ApplyDNSProviderTypeGoDaddy:
{ {
access := &domain.AccessConfigForGoDaddy{} access := domain.AccessConfigForGoDaddy{}
if err := json.Unmarshal([]byte(accessConfig), access); err != nil { if err := maps.Decode(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to unmarshal access config: %w", err) return nil, fmt.Errorf("failed to decode provider access config: %w", err)
} }
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: applyConfig.PropagationTimeout, PropagationTimeout: options.PropagationTimeout,
}) })
return applicant, err return applicant, err
} }
case domain.ApplyDNSProviderTypeHuaweiCloud, domain.ApplyDNSProviderTypeHuaweiCloudDNS: case domain.ApplyDNSProviderTypeHuaweiCloud, domain.ApplyDNSProviderTypeHuaweiCloudDNS:
{ {
access := &domain.AccessConfigForHuaweiCloud{} access := domain.AccessConfigForHuaweiCloud{}
if err := json.Unmarshal([]byte(accessConfig), access); err != nil { if err := maps.Decode(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to unmarshal access config: %w", err) return nil, fmt.Errorf("failed to decode provider access config: %w", err)
} }
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: access.Region, Region: maps.GetValueAsString(options.ProviderApplyConfig, "region"),
PropagationTimeout: applyConfig.PropagationTimeout, PropagationTimeout: options.PropagationTimeout,
}) })
return applicant, err return applicant, err
} }
case domain.ApplyDNSProviderTypeNameDotCom: case domain.ApplyDNSProviderTypeNameDotCom:
{ {
access := &domain.AccessConfigForNameDotCom{} access := domain.AccessConfigForNameDotCom{}
if err := json.Unmarshal([]byte(accessConfig), access); err != nil { if err := maps.Decode(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to unmarshal access config: %w", err) return nil, fmt.Errorf("failed to decode provider access config: %w", err)
} }
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: applyConfig.PropagationTimeout, PropagationTimeout: options.PropagationTimeout,
}) })
return applicant, err return applicant, err
} }
case domain.ApplyDNSProviderTypeNameSilo: case domain.ApplyDNSProviderTypeNameSilo:
{ {
access := &domain.AccessConfigForNameSilo{} access := domain.AccessConfigForNameSilo{}
if err := json.Unmarshal([]byte(accessConfig), access); err != nil { if err := maps.Decode(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to unmarshal access config: %w", err) return nil, fmt.Errorf("failed to decode provider access config: %w", err)
} }
applicant, err := providerNameSilo.NewChallengeProvider(&providerNameSilo.NameSiloApplicantConfig{ applicant, err := providerNameSilo.NewChallengeProvider(&providerNameSilo.NameSiloApplicantConfig{
ApiKey: access.ApiKey, ApiKey: access.ApiKey,
PropagationTimeout: applyConfig.PropagationTimeout, PropagationTimeout: options.PropagationTimeout,
}) })
return applicant, err return applicant, err
} }
case domain.ApplyDNSProviderTypePowerDNS: case domain.ApplyDNSProviderTypePowerDNS:
{ {
access := &domain.AccessConfigForPowerDNS{} access := domain.AccessConfigForPowerDNS{}
if err := json.Unmarshal([]byte(accessConfig), access); err != nil { if err := maps.Decode(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to unmarshal access config: %w", err) return nil, fmt.Errorf("failed to decode provider access config: %w", err)
} }
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: applyConfig.PropagationTimeout, PropagationTimeout: options.PropagationTimeout,
}) })
return applicant, err return applicant, err
} }
case domain.ApplyDNSProviderTypeTencentCloud, domain.ApplyDNSProviderTypeTencentCloudDNS: case domain.ApplyDNSProviderTypeTencentCloud, domain.ApplyDNSProviderTypeTencentCloudDNS:
{ {
access := &domain.AccessConfigForTencentCloud{} access := domain.AccessConfigForTencentCloud{}
if err := json.Unmarshal([]byte(accessConfig), access); err != nil { if err := maps.Decode(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to unmarshal access config: %w", err) return nil, fmt.Errorf("failed to decode provider access config: %w", err)
} }
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: applyConfig.PropagationTimeout, PropagationTimeout: options.PropagationTimeout,
}) })
return applicant, err return applicant, err
} }
case domain.ApplyDNSProviderTypeVolcEngine, domain.ApplyDNSProviderTypeVolcEngineDNS: case domain.ApplyDNSProviderTypeVolcEngine, domain.ApplyDNSProviderTypeVolcEngineDNS:
{ {
access := &domain.AccessConfigForVolcEngine{} access := domain.AccessConfigForVolcEngine{}
if err := json.Unmarshal([]byte(accessConfig), access); err != nil { if err := maps.Decode(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to unmarshal access config: %w", err) return nil, fmt.Errorf("failed to decode provider access config: %w", err)
} }
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: applyConfig.PropagationTimeout, PropagationTimeout: options.PropagationTimeout,
}) })
return applicant, err return applicant, err
} }
} }
return nil, fmt.Errorf("unsupported applicant provider: %s", provider) return nil, fmt.Errorf("unsupported applicant provider: %s", string(options.Provider))
} }

View File

@ -14,6 +14,12 @@ type Deployer interface {
Deploy(ctx context.Context) error Deploy(ctx context.Context) error
} }
type deployerOptions struct {
Provider domain.DeployProviderType
ProviderAccessConfig map[string]any
ProviderDeployConfig map[string]any
}
func NewWithDeployNode(node *domain.WorkflowNode, certdata struct { func NewWithDeployNode(node *domain.WorkflowNode, certdata struct {
Certificate string Certificate string
PrivateKey string PrivateKey string
@ -30,9 +36,16 @@ func NewWithDeployNode(node *domain.WorkflowNode, certdata struct {
return nil, fmt.Errorf("failed to get access #%s record: %w", accessId, err) return nil, fmt.Errorf("failed to get access #%s record: %w", accessId, err)
} }
deployProvider := node.GetConfigString("provider") accessConfig, err := access.UnmarshalConfigToMap()
deployConfig := node.GetConfigMap("providerConfig") if err != nil {
deployer, logger, err := createDeployer(domain.DeployProviderType(deployProvider), access.Config, deployConfig) return nil, fmt.Errorf("failed to unmarshal access config: %w", err)
}
deployer, logger, err := createDeployer(&deployerOptions{
Provider: domain.DeployProviderType(node.GetConfigString("provider")),
ProviderAccessConfig: accessConfig,
ProviderDeployConfig: node.GetConfigMap("providerConfig"),
})
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -1,7 +1,6 @@
package deployer package deployer
import ( import (
"encoding/json"
"fmt" "fmt"
"strconv" "strconv"
@ -34,30 +33,30 @@ import (
"github.com/usual2970/certimate/internal/pkg/utils/maps" "github.com/usual2970/certimate/internal/pkg/utils/maps"
) )
func createDeployer(provider domain.DeployProviderType, accessConfig string, deployConfig map[string]any) (deployer.Deployer, logger.Logger, error) { func createDeployer(options *deployerOptions) (deployer.Deployer, logger.Logger, error) {
logger := logger.NewDefaultLogger() logger := logger.NewDefaultLogger()
/* /*
注意如果追加新的常量值请保持以 ASCII 排序 注意如果追加新的常量值请保持以 ASCII 排序
NOTICE: If you add new constant, please keep ASCII order. NOTICE: If you add new constant, please keep ASCII order.
*/ */
switch provider { switch options.Provider {
case domain.DeployProviderTypeAliyunALB, domain.DeployProviderTypeAliyunCDN, domain.DeployProviderTypeAliyunCLB, domain.DeployProviderTypeAliyunDCDN, domain.DeployProviderTypeAliyunNLB, domain.DeployProviderTypeAliyunOSS: case domain.DeployProviderTypeAliyunALB, domain.DeployProviderTypeAliyunCDN, domain.DeployProviderTypeAliyunCLB, domain.DeployProviderTypeAliyunDCDN, domain.DeployProviderTypeAliyunNLB, domain.DeployProviderTypeAliyunOSS:
{ {
access := &domain.AccessConfigForAliyun{} access := domain.AccessConfigForAliyun{}
if err := json.Unmarshal([]byte(accessConfig), access); err != nil { if err := maps.Decode(options.ProviderAccessConfig, &access); err != nil {
return nil, nil, fmt.Errorf("failed to unmarshal access config: %w", err) return nil, nil, fmt.Errorf("failed to decode provider access config: %w", err)
} }
switch provider { switch options.Provider {
case domain.DeployProviderTypeAliyunALB: case domain.DeployProviderTypeAliyunALB:
deployer, err := providerAliyunALB.NewWithLogger(&providerAliyunALB.AliyunALBDeployerConfig{ deployer, err := providerAliyunALB.NewWithLogger(&providerAliyunALB.AliyunALBDeployerConfig{
AccessKeyId: access.AccessKeyId, AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret, AccessKeySecret: access.AccessKeySecret,
Region: maps.GetValueAsString(deployConfig, "region"), Region: maps.GetValueAsString(options.ProviderDeployConfig, "region"),
ResourceType: providerAliyunALB.DeployResourceType(maps.GetValueAsString(deployConfig, "resourceType")), ResourceType: providerAliyunALB.DeployResourceType(maps.GetValueAsString(options.ProviderDeployConfig, "resourceType")),
LoadbalancerId: maps.GetValueAsString(deployConfig, "loadbalancerId"), LoadbalancerId: maps.GetValueAsString(options.ProviderDeployConfig, "loadbalancerId"),
ListenerId: maps.GetValueAsString(deployConfig, "listenerId"), ListenerId: maps.GetValueAsString(options.ProviderDeployConfig, "listenerId"),
}, logger) }, logger)
return deployer, logger, err return deployer, logger, err
@ -65,7 +64,7 @@ func createDeployer(provider domain.DeployProviderType, accessConfig string, dep
deployer, err := providerAliyunCDN.NewWithLogger(&providerAliyunCDN.AliyunCDNDeployerConfig{ deployer, err := providerAliyunCDN.NewWithLogger(&providerAliyunCDN.AliyunCDNDeployerConfig{
AccessKeyId: access.AccessKeyId, AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret, AccessKeySecret: access.AccessKeySecret,
Domain: maps.GetValueAsString(deployConfig, "domain"), Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"),
}, logger) }, logger)
return deployer, logger, err return deployer, logger, err
@ -73,10 +72,10 @@ func createDeployer(provider domain.DeployProviderType, accessConfig string, dep
deployer, err := providerAliyunCLB.NewWithLogger(&providerAliyunCLB.AliyunCLBDeployerConfig{ deployer, err := providerAliyunCLB.NewWithLogger(&providerAliyunCLB.AliyunCLBDeployerConfig{
AccessKeyId: access.AccessKeyId, AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret, AccessKeySecret: access.AccessKeySecret,
Region: maps.GetValueAsString(deployConfig, "region"), Region: maps.GetValueAsString(options.ProviderDeployConfig, "region"),
ResourceType: providerAliyunCLB.DeployResourceType(maps.GetValueAsString(deployConfig, "resourceType")), ResourceType: providerAliyunCLB.DeployResourceType(maps.GetValueAsString(options.ProviderDeployConfig, "resourceType")),
LoadbalancerId: maps.GetValueAsString(deployConfig, "loadbalancerId"), LoadbalancerId: maps.GetValueAsString(options.ProviderDeployConfig, "loadbalancerId"),
ListenerPort: maps.GetValueAsInt32(deployConfig, "listenerPort"), ListenerPort: maps.GetValueAsInt32(options.ProviderDeployConfig, "listenerPort"),
}, logger) }, logger)
return deployer, logger, err return deployer, logger, err
@ -84,7 +83,7 @@ func createDeployer(provider domain.DeployProviderType, accessConfig string, dep
deployer, err := providerAliyunDCDN.NewWithLogger(&providerAliyunDCDN.AliyunDCDNDeployerConfig{ deployer, err := providerAliyunDCDN.NewWithLogger(&providerAliyunDCDN.AliyunDCDNDeployerConfig{
AccessKeyId: access.AccessKeyId, AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret, AccessKeySecret: access.AccessKeySecret,
Domain: maps.GetValueAsString(deployConfig, "domain"), Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"),
}, logger) }, logger)
return deployer, logger, err return deployer, logger, err
@ -92,10 +91,10 @@ func createDeployer(provider domain.DeployProviderType, accessConfig string, dep
deployer, err := providerAliyunNLB.NewWithLogger(&providerAliyunNLB.AliyunNLBDeployerConfig{ deployer, err := providerAliyunNLB.NewWithLogger(&providerAliyunNLB.AliyunNLBDeployerConfig{
AccessKeyId: access.AccessKeyId, AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret, AccessKeySecret: access.AccessKeySecret,
Region: maps.GetValueAsString(deployConfig, "region"), Region: maps.GetValueAsString(options.ProviderDeployConfig, "region"),
ResourceType: providerAliyunNLB.DeployResourceType(maps.GetValueAsString(deployConfig, "resourceType")), ResourceType: providerAliyunNLB.DeployResourceType(maps.GetValueAsString(options.ProviderDeployConfig, "resourceType")),
LoadbalancerId: maps.GetValueAsString(deployConfig, "loadbalancerId"), LoadbalancerId: maps.GetValueAsString(options.ProviderDeployConfig, "loadbalancerId"),
ListenerId: maps.GetValueAsString(deployConfig, "listenerId"), ListenerId: maps.GetValueAsString(options.ProviderDeployConfig, "listenerId"),
}, logger) }, logger)
return deployer, logger, err return deployer, logger, err
@ -103,9 +102,9 @@ func createDeployer(provider domain.DeployProviderType, accessConfig string, dep
deployer, err := providerAliyunOSS.NewWithLogger(&providerAliyunOSS.AliyunOSSDeployerConfig{ deployer, err := providerAliyunOSS.NewWithLogger(&providerAliyunOSS.AliyunOSSDeployerConfig{
AccessKeyId: access.AccessKeyId, AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret, AccessKeySecret: access.AccessKeySecret,
Region: maps.GetValueAsString(deployConfig, "region"), Region: maps.GetValueAsString(options.ProviderDeployConfig, "region"),
Bucket: maps.GetValueAsString(deployConfig, "bucket"), Bucket: maps.GetValueAsString(options.ProviderDeployConfig, "bucket"),
Domain: maps.GetValueAsString(deployConfig, "domain"), Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"),
}, logger) }, logger)
return deployer, logger, err return deployer, logger, err
@ -116,63 +115,63 @@ func createDeployer(provider domain.DeployProviderType, accessConfig string, dep
case domain.DeployProviderTypeBaiduCloudCDN: case domain.DeployProviderTypeBaiduCloudCDN:
{ {
access := &domain.AccessConfigForBaiduCloud{} access := domain.AccessConfigForBaiduCloud{}
if err := json.Unmarshal([]byte(accessConfig), access); err != nil { if err := maps.Decode(options.ProviderAccessConfig, &access); err != nil {
return nil, nil, fmt.Errorf("failed to unmarshal access config: %w", err) return nil, nil, fmt.Errorf("failed to decode provider access config: %w", err)
} }
deployer, err := providerBaiduCloudCDN.NewWithLogger(&providerBaiduCloudCDN.BaiduCloudCDNDeployerConfig{ deployer, err := providerBaiduCloudCDN.NewWithLogger(&providerBaiduCloudCDN.BaiduCloudCDNDeployerConfig{
AccessKeyId: access.AccessKeyId, AccessKeyId: access.AccessKeyId,
SecretAccessKey: access.SecretAccessKey, SecretAccessKey: access.SecretAccessKey,
Domain: maps.GetValueAsString(deployConfig, "domain"), Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"),
}, logger) }, logger)
return deployer, logger, err return deployer, logger, err
} }
case domain.DeployProviderTypeBytePlusCDN: case domain.DeployProviderTypeBytePlusCDN:
{ {
access := &domain.AccessConfigForBytePlus{} access := domain.AccessConfigForBytePlus{}
if err := json.Unmarshal([]byte(accessConfig), access); err != nil { if err := maps.Decode(options.ProviderAccessConfig, &access); err != nil {
return nil, nil, fmt.Errorf("failed to unmarshal access config: %w", err) return nil, nil, fmt.Errorf("failed to decode provider access config: %w", err)
} }
deployer, err := providerBytePlusCDN.NewWithLogger(&providerBytePlusCDN.BytePlusCDNDeployerConfig{ deployer, err := providerBytePlusCDN.NewWithLogger(&providerBytePlusCDN.BytePlusCDNDeployerConfig{
AccessKey: access.AccessKey, AccessKey: access.AccessKey,
SecretKey: access.SecretKey, SecretKey: access.SecretKey,
Domain: maps.GetValueAsString(deployConfig, "domain"), Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"),
}, logger) }, logger)
return deployer, logger, err return deployer, logger, err
} }
case domain.DeployProviderTypeDogeCloudCDN: case domain.DeployProviderTypeDogeCloudCDN:
{ {
access := &domain.AccessConfigForDogeCloud{} access := domain.AccessConfigForDogeCloud{}
if err := json.Unmarshal([]byte(accessConfig), access); err != nil { if err := maps.Decode(options.ProviderAccessConfig, &access); err != nil {
return nil, nil, fmt.Errorf("failed to unmarshal access config: %w", err) return nil, nil, fmt.Errorf("failed to decode provider access config: %w", err)
} }
deployer, err := providerDogeCDN.NewWithLogger(&providerDogeCDN.DogeCloudCDNDeployerConfig{ deployer, err := providerDogeCDN.NewWithLogger(&providerDogeCDN.DogeCloudCDNDeployerConfig{
AccessKey: access.AccessKey, AccessKey: access.AccessKey,
SecretKey: access.SecretKey, SecretKey: access.SecretKey,
Domain: maps.GetValueAsString(deployConfig, "domain"), Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"),
}, logger) }, logger)
return deployer, logger, err return deployer, logger, err
} }
case domain.DeployProviderTypeHuaweiCloudCDN, domain.DeployProviderTypeHuaweiCloudELB: case domain.DeployProviderTypeHuaweiCloudCDN, domain.DeployProviderTypeHuaweiCloudELB:
{ {
access := &domain.AccessConfigForHuaweiCloud{} access := domain.AccessConfigForHuaweiCloud{}
if err := json.Unmarshal([]byte(accessConfig), access); err != nil { if err := maps.Decode(options.ProviderAccessConfig, &access); err != nil {
return nil, nil, fmt.Errorf("failed to unmarshal access config: %w", err) return nil, nil, fmt.Errorf("failed to decode provider access config: %w", err)
} }
switch provider { switch options.Provider {
case domain.DeployProviderTypeHuaweiCloudCDN: case domain.DeployProviderTypeHuaweiCloudCDN:
deployer, err := providerHuaweiCloudCDN.NewWithLogger(&providerHuaweiCloudCDN.HuaweiCloudCDNDeployerConfig{ deployer, err := providerHuaweiCloudCDN.NewWithLogger(&providerHuaweiCloudCDN.HuaweiCloudCDNDeployerConfig{
AccessKeyId: access.AccessKeyId, AccessKeyId: access.AccessKeyId,
SecretAccessKey: access.SecretAccessKey, SecretAccessKey: access.SecretAccessKey,
Region: maps.GetValueAsString(deployConfig, "region"), Region: maps.GetValueAsString(options.ProviderDeployConfig, "region"),
Domain: maps.GetValueAsString(deployConfig, "domain"), Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"),
}, logger) }, logger)
return deployer, logger, err return deployer, logger, err
@ -180,11 +179,11 @@ func createDeployer(provider domain.DeployProviderType, accessConfig string, dep
deployer, err := providerHuaweiCloudELB.NewWithLogger(&providerHuaweiCloudELB.HuaweiCloudELBDeployerConfig{ deployer, err := providerHuaweiCloudELB.NewWithLogger(&providerHuaweiCloudELB.HuaweiCloudELBDeployerConfig{
AccessKeyId: access.AccessKeyId, AccessKeyId: access.AccessKeyId,
SecretAccessKey: access.SecretAccessKey, SecretAccessKey: access.SecretAccessKey,
Region: maps.GetValueAsString(deployConfig, "region"), Region: maps.GetValueAsString(options.ProviderDeployConfig, "region"),
ResourceType: providerHuaweiCloudELB.DeployResourceType(maps.GetValueAsString(deployConfig, "resourceType")), ResourceType: providerHuaweiCloudELB.DeployResourceType(maps.GetValueAsString(options.ProviderDeployConfig, "resourceType")),
CertificateId: maps.GetValueAsString(deployConfig, "certificateId"), CertificateId: maps.GetValueAsString(options.ProviderDeployConfig, "certificateId"),
LoadbalancerId: maps.GetValueAsString(deployConfig, "loadbalancerId"), LoadbalancerId: maps.GetValueAsString(options.ProviderDeployConfig, "loadbalancerId"),
ListenerId: maps.GetValueAsString(deployConfig, "listenerId"), ListenerId: maps.GetValueAsString(options.ProviderDeployConfig, "listenerId"),
}, logger) }, logger)
return deployer, logger, err return deployer, logger, err
@ -196,58 +195,58 @@ func createDeployer(provider domain.DeployProviderType, accessConfig string, dep
case domain.DeployProviderTypeLocal: case domain.DeployProviderTypeLocal:
{ {
deployer, err := providerLocal.NewWithLogger(&providerLocal.LocalDeployerConfig{ deployer, err := providerLocal.NewWithLogger(&providerLocal.LocalDeployerConfig{
ShellEnv: providerLocal.ShellEnvType(maps.GetValueAsString(deployConfig, "shellEnv")), ShellEnv: providerLocal.ShellEnvType(maps.GetValueAsString(options.ProviderDeployConfig, "shellEnv")),
PreCommand: maps.GetValueAsString(deployConfig, "preCommand"), PreCommand: maps.GetValueAsString(options.ProviderDeployConfig, "preCommand"),
PostCommand: maps.GetValueAsString(deployConfig, "postCommand"), PostCommand: maps.GetValueAsString(options.ProviderDeployConfig, "postCommand"),
OutputFormat: providerLocal.OutputFormatType(maps.GetValueOrDefaultAsString(deployConfig, "format", string(providerLocal.OUTPUT_FORMAT_PEM))), OutputFormat: providerLocal.OutputFormatType(maps.GetValueOrDefaultAsString(options.ProviderDeployConfig, "format", string(providerLocal.OUTPUT_FORMAT_PEM))),
OutputCertPath: maps.GetValueAsString(deployConfig, "certPath"), OutputCertPath: maps.GetValueAsString(options.ProviderDeployConfig, "certPath"),
OutputKeyPath: maps.GetValueAsString(deployConfig, "keyPath"), OutputKeyPath: maps.GetValueAsString(options.ProviderDeployConfig, "keyPath"),
PfxPassword: maps.GetValueAsString(deployConfig, "pfxPassword"), PfxPassword: maps.GetValueAsString(options.ProviderDeployConfig, "pfxPassword"),
JksAlias: maps.GetValueAsString(deployConfig, "jksAlias"), JksAlias: maps.GetValueAsString(options.ProviderDeployConfig, "jksAlias"),
JksKeypass: maps.GetValueAsString(deployConfig, "jksKeypass"), JksKeypass: maps.GetValueAsString(options.ProviderDeployConfig, "jksKeypass"),
JksStorepass: maps.GetValueAsString(deployConfig, "jksStorepass"), JksStorepass: maps.GetValueAsString(options.ProviderDeployConfig, "jksStorepass"),
}, logger) }, logger)
return deployer, logger, err return deployer, logger, err
} }
case domain.DeployProviderTypeKubernetesSecret: case domain.DeployProviderTypeKubernetesSecret:
{ {
access := &domain.AccessConfigForKubernetes{} access := domain.AccessConfigForKubernetes{}
if err := json.Unmarshal([]byte(accessConfig), access); err != nil { if err := maps.Decode(options.ProviderAccessConfig, &access); err != nil {
return nil, nil, fmt.Errorf("failed to unmarshal access config: %w", err) return nil, nil, fmt.Errorf("failed to decode provider access config: %w", err)
} }
deployer, err := providerK8sSecret.NewWithLogger(&providerK8sSecret.K8sSecretDeployerConfig{ deployer, err := providerK8sSecret.NewWithLogger(&providerK8sSecret.K8sSecretDeployerConfig{
KubeConfig: access.KubeConfig, KubeConfig: access.KubeConfig,
Namespace: maps.GetValueOrDefaultAsString(deployConfig, "namespace", "default"), Namespace: maps.GetValueOrDefaultAsString(options.ProviderDeployConfig, "namespace", "default"),
SecretName: maps.GetValueAsString(deployConfig, "secretName"), SecretName: maps.GetValueAsString(options.ProviderDeployConfig, "secretName"),
SecretType: maps.GetValueOrDefaultAsString(deployConfig, "secretType", "kubernetes.io/tls"), SecretType: maps.GetValueOrDefaultAsString(options.ProviderDeployConfig, "secretType", "kubernetes.io/tls"),
SecretDataKeyForCrt: maps.GetValueOrDefaultAsString(deployConfig, "secretDataKeyForCrt", "tls.crt"), SecretDataKeyForCrt: maps.GetValueOrDefaultAsString(options.ProviderDeployConfig, "secretDataKeyForCrt", "tls.crt"),
SecretDataKeyForKey: maps.GetValueOrDefaultAsString(deployConfig, "secretDataKeyForKey", "tls.key"), SecretDataKeyForKey: maps.GetValueOrDefaultAsString(options.ProviderDeployConfig, "secretDataKeyForKey", "tls.key"),
}, logger) }, logger)
return deployer, logger, err return deployer, logger, err
} }
case domain.DeployProviderTypeQiniuCDN: case domain.DeployProviderTypeQiniuCDN:
{ {
access := &domain.AccessConfigForQiniu{} access := domain.AccessConfigForQiniu{}
if err := json.Unmarshal([]byte(accessConfig), access); err != nil { if err := maps.Decode(options.ProviderAccessConfig, &access); err != nil {
return nil, nil, fmt.Errorf("failed to unmarshal access config: %w", err) return nil, nil, fmt.Errorf("failed to decode provider access config: %w", err)
} }
deployer, err := providerQiniuCDN.NewWithLogger(&providerQiniuCDN.QiniuCDNDeployerConfig{ deployer, err := providerQiniuCDN.NewWithLogger(&providerQiniuCDN.QiniuCDNDeployerConfig{
AccessKey: access.AccessKey, AccessKey: access.AccessKey,
SecretKey: access.SecretKey, SecretKey: access.SecretKey,
Domain: maps.GetValueAsString(deployConfig, "domain"), Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"),
}, logger) }, logger)
return deployer, logger, err return deployer, logger, err
} }
case domain.DeployProviderTypeSSH: case domain.DeployProviderTypeSSH:
{ {
access := &domain.AccessConfigForSSH{} access := domain.AccessConfigForSSH{}
if err := json.Unmarshal([]byte(accessConfig), access); err != nil { if err := maps.Decode(options.ProviderAccessConfig, &access); err != nil {
return nil, nil, fmt.Errorf("failed to unmarshal access config: %w", err) return nil, nil, fmt.Errorf("failed to decode provider access config: %w", err)
} }
sshPort, _ := strconv.ParseInt(access.Port, 10, 32) sshPort, _ := strconv.ParseInt(access.Port, 10, 32)
@ -258,32 +257,32 @@ func createDeployer(provider domain.DeployProviderType, accessConfig string, dep
SshPassword: access.Password, SshPassword: access.Password,
SshKey: access.Key, SshKey: access.Key,
SshKeyPassphrase: access.KeyPassphrase, SshKeyPassphrase: access.KeyPassphrase,
PreCommand: maps.GetValueAsString(deployConfig, "preCommand"), PreCommand: maps.GetValueAsString(options.ProviderDeployConfig, "preCommand"),
PostCommand: maps.GetValueAsString(deployConfig, "postCommand"), PostCommand: maps.GetValueAsString(options.ProviderDeployConfig, "postCommand"),
OutputFormat: providerSSH.OutputFormatType(maps.GetValueOrDefaultAsString(deployConfig, "format", string(providerSSH.OUTPUT_FORMAT_PEM))), OutputFormat: providerSSH.OutputFormatType(maps.GetValueOrDefaultAsString(options.ProviderDeployConfig, "format", string(providerSSH.OUTPUT_FORMAT_PEM))),
OutputCertPath: maps.GetValueAsString(deployConfig, "certPath"), OutputCertPath: maps.GetValueAsString(options.ProviderDeployConfig, "certPath"),
OutputKeyPath: maps.GetValueAsString(deployConfig, "keyPath"), OutputKeyPath: maps.GetValueAsString(options.ProviderDeployConfig, "keyPath"),
PfxPassword: maps.GetValueAsString(deployConfig, "pfxPassword"), PfxPassword: maps.GetValueAsString(options.ProviderDeployConfig, "pfxPassword"),
JksAlias: maps.GetValueAsString(deployConfig, "jksAlias"), JksAlias: maps.GetValueAsString(options.ProviderDeployConfig, "jksAlias"),
JksKeypass: maps.GetValueAsString(deployConfig, "jksKeypass"), JksKeypass: maps.GetValueAsString(options.ProviderDeployConfig, "jksKeypass"),
JksStorepass: maps.GetValueAsString(deployConfig, "jksStorepass"), JksStorepass: maps.GetValueAsString(options.ProviderDeployConfig, "jksStorepass"),
}, logger) }, logger)
return deployer, logger, err return deployer, logger, err
} }
case domain.DeployProviderTypeTencentCloudCDN, domain.DeployProviderTypeTencentCloudCLB, domain.DeployProviderTypeTencentCloudCOS, domain.DeployProviderTypeTencentCloudECDN, domain.DeployProviderTypeTencentCloudEO: case domain.DeployProviderTypeTencentCloudCDN, domain.DeployProviderTypeTencentCloudCLB, domain.DeployProviderTypeTencentCloudCOS, domain.DeployProviderTypeTencentCloudECDN, domain.DeployProviderTypeTencentCloudEO:
{ {
access := &domain.AccessConfigForTencentCloud{} access := domain.AccessConfigForTencentCloud{}
if err := json.Unmarshal([]byte(accessConfig), access); err != nil { if err := maps.Decode(options.ProviderAccessConfig, &access); err != nil {
return nil, nil, fmt.Errorf("failed to unmarshal access config: %w", err) return nil, nil, fmt.Errorf("failed to decode provider access config: %w", err)
} }
switch provider { switch options.Provider {
case domain.DeployProviderTypeTencentCloudCDN: case domain.DeployProviderTypeTencentCloudCDN:
deployer, err := providerTencentCloudCDN.NewWithLogger(&providerTencentCloudCDN.TencentCloudCDNDeployerConfig{ deployer, err := providerTencentCloudCDN.NewWithLogger(&providerTencentCloudCDN.TencentCloudCDNDeployerConfig{
SecretId: access.SecretId, SecretId: access.SecretId,
SecretKey: access.SecretKey, SecretKey: access.SecretKey,
Domain: maps.GetValueAsString(deployConfig, "domain"), Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"),
}, logger) }, logger)
return deployer, logger, err return deployer, logger, err
@ -291,11 +290,11 @@ func createDeployer(provider domain.DeployProviderType, accessConfig string, dep
deployer, err := providerTencentCloudCLB.NewWithLogger(&providerTencentCloudCLB.TencentCloudCLBDeployerConfig{ deployer, err := providerTencentCloudCLB.NewWithLogger(&providerTencentCloudCLB.TencentCloudCLBDeployerConfig{
SecretId: access.SecretId, SecretId: access.SecretId,
SecretKey: access.SecretKey, SecretKey: access.SecretKey,
Region: maps.GetValueAsString(deployConfig, "region"), Region: maps.GetValueAsString(options.ProviderDeployConfig, "region"),
ResourceType: providerTencentCloudCLB.DeployResourceType(maps.GetValueAsString(deployConfig, "resourceType")), ResourceType: providerTencentCloudCLB.DeployResourceType(maps.GetValueAsString(options.ProviderDeployConfig, "resourceType")),
LoadbalancerId: maps.GetValueAsString(deployConfig, "loadbalancerId"), LoadbalancerId: maps.GetValueAsString(options.ProviderDeployConfig, "loadbalancerId"),
ListenerId: maps.GetValueAsString(deployConfig, "listenerId"), ListenerId: maps.GetValueAsString(options.ProviderDeployConfig, "listenerId"),
Domain: maps.GetValueAsString(deployConfig, "domain"), Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"),
}, logger) }, logger)
return deployer, logger, err return deployer, logger, err
@ -303,9 +302,9 @@ func createDeployer(provider domain.DeployProviderType, accessConfig string, dep
deployer, err := providerTencentCloudCOD.NewWithLogger(&providerTencentCloudCOD.TencentCloudCOSDeployerConfig{ deployer, err := providerTencentCloudCOD.NewWithLogger(&providerTencentCloudCOD.TencentCloudCOSDeployerConfig{
SecretId: access.SecretId, SecretId: access.SecretId,
SecretKey: access.SecretKey, SecretKey: access.SecretKey,
Region: maps.GetValueAsString(deployConfig, "region"), Region: maps.GetValueAsString(options.ProviderDeployConfig, "region"),
Bucket: maps.GetValueAsString(deployConfig, "bucket"), Bucket: maps.GetValueAsString(options.ProviderDeployConfig, "bucket"),
Domain: maps.GetValueAsString(deployConfig, "domain"), Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"),
}, logger) }, logger)
return deployer, logger, err return deployer, logger, err
@ -313,7 +312,7 @@ func createDeployer(provider domain.DeployProviderType, accessConfig string, dep
deployer, err := providerTencentCloudECDN.NewWithLogger(&providerTencentCloudECDN.TencentCloudECDNDeployerConfig{ deployer, err := providerTencentCloudECDN.NewWithLogger(&providerTencentCloudECDN.TencentCloudECDNDeployerConfig{
SecretId: access.SecretId, SecretId: access.SecretId,
SecretKey: access.SecretKey, SecretKey: access.SecretKey,
Domain: maps.GetValueAsString(deployConfig, "domain"), Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"),
}, logger) }, logger)
return deployer, logger, err return deployer, logger, err
@ -321,8 +320,8 @@ func createDeployer(provider domain.DeployProviderType, accessConfig string, dep
deployer, err := providerTencentCloudEO.NewWithLogger(&providerTencentCloudEO.TencentCloudEODeployerConfig{ deployer, err := providerTencentCloudEO.NewWithLogger(&providerTencentCloudEO.TencentCloudEODeployerConfig{
SecretId: access.SecretId, SecretId: access.SecretId,
SecretKey: access.SecretKey, SecretKey: access.SecretKey,
ZoneId: maps.GetValueAsString(deployConfig, "zoneId"), ZoneId: maps.GetValueAsString(options.ProviderDeployConfig, "zoneId"),
Domain: maps.GetValueAsString(deployConfig, "domain"), Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"),
}, logger) }, logger)
return deployer, logger, err return deployer, logger, err
@ -333,17 +332,17 @@ func createDeployer(provider domain.DeployProviderType, accessConfig string, dep
case domain.DeployProviderTypeVolcEngineCDN, domain.DeployProviderTypeVolcEngineLive: case domain.DeployProviderTypeVolcEngineCDN, domain.DeployProviderTypeVolcEngineLive:
{ {
access := &domain.AccessConfigForVolcEngine{} access := domain.AccessConfigForVolcEngine{}
if err := json.Unmarshal([]byte(accessConfig), access); err != nil { if err := maps.Decode(options.ProviderAccessConfig, &access); err != nil {
return nil, nil, fmt.Errorf("failed to unmarshal access config: %w", err) return nil, nil, fmt.Errorf("failed to decode provider access config: %w", err)
} }
switch provider { switch options.Provider {
case domain.DeployProviderTypeVolcEngineCDN: case domain.DeployProviderTypeVolcEngineCDN:
deployer, err := providerVolcEngineCDN.NewWithLogger(&providerVolcEngineCDN.VolcEngineCDNDeployerConfig{ deployer, err := providerVolcEngineCDN.NewWithLogger(&providerVolcEngineCDN.VolcEngineCDNDeployerConfig{
AccessKey: access.AccessKeyId, AccessKey: access.AccessKeyId,
SecretKey: access.SecretAccessKey, SecretKey: access.SecretAccessKey,
Domain: maps.GetValueAsString(deployConfig, "domain"), Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"),
}, logger) }, logger)
return deployer, logger, err return deployer, logger, err
@ -351,7 +350,7 @@ func createDeployer(provider domain.DeployProviderType, accessConfig string, dep
deployer, err := providerVolcEngineLive.NewWithLogger(&providerVolcEngineLive.VolcEngineLiveDeployerConfig{ deployer, err := providerVolcEngineLive.NewWithLogger(&providerVolcEngineLive.VolcEngineLiveDeployerConfig{
AccessKey: access.AccessKeyId, AccessKey: access.AccessKeyId,
SecretKey: access.SecretAccessKey, SecretKey: access.SecretAccessKey,
Domain: maps.GetValueAsString(deployConfig, "domain"), Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"),
}, logger) }, logger)
return deployer, logger, err return deployer, logger, err
@ -362,18 +361,18 @@ func createDeployer(provider domain.DeployProviderType, accessConfig string, dep
case domain.DeployProviderTypeWebhook: case domain.DeployProviderTypeWebhook:
{ {
access := &domain.AccessConfigForWebhook{} access := domain.AccessConfigForWebhook{}
if err := json.Unmarshal([]byte(accessConfig), access); err != nil { if err := maps.Decode(options.ProviderAccessConfig, &access); err != nil {
return nil, nil, fmt.Errorf("failed to unmarshal access config: %w", err) return nil, nil, fmt.Errorf("failed to decode provider access config: %w", err)
} }
deployer, err := providerWebhook.NewWithLogger(&providerWebhook.WebhookDeployerConfig{ deployer, err := providerWebhook.NewWithLogger(&providerWebhook.WebhookDeployerConfig{
WebhookUrl: access.Url, WebhookUrl: access.Url,
WebhookData: maps.GetValueAsString(deployConfig, "webhookData"), WebhookData: maps.GetValueAsString(options.ProviderDeployConfig, "webhookData"),
}, logger) }, logger)
return deployer, logger, err return deployer, logger, err
} }
} }
return nil, nil, fmt.Errorf("unsupported deployer provider: %s", provider) return nil, nil, fmt.Errorf("unsupported deployer provider: %s", string(options.Provider))
} }

View File

@ -1,6 +1,9 @@
package domain package domain
import "time" import (
"encoding/json"
"time"
)
type Access struct { type Access struct {
Meta Meta
@ -11,6 +14,15 @@ type Access struct {
DeletedAt time.Time `json:"deleted" db:"deleted"` DeletedAt time.Time `json:"deleted" db:"deleted"`
} }
func (a *Access) UnmarshalConfigToMap() (map[string]any, error) {
config := make(map[string]any)
if err := json.Unmarshal([]byte(a.Config), &config); err != nil {
return nil, err
}
return config, nil
}
type AccessConfigForACMEHttpReq struct { type AccessConfigForACMEHttpReq struct {
Endpoint string `json:"endpoint"` Endpoint string `json:"endpoint"`
Mode string `json:"mode"` Mode string `json:"mode"`
@ -26,8 +38,6 @@ type AccessConfigForAliyun struct {
type AccessConfigForAWS struct { type AccessConfigForAWS struct {
AccessKeyId string `json:"accessKeyId"` AccessKeyId string `json:"accessKeyId"`
SecretAccessKey string `json:"secretAccessKey"` SecretAccessKey string `json:"secretAccessKey"`
Region string `json:"region"`
HostedZoneId string `json:"hostedZoneId"`
} }
type AccessConfigForBaiduCloud struct { type AccessConfigForBaiduCloud struct {
@ -57,7 +67,6 @@ type AccessConfigForGoDaddy struct {
type AccessConfigForHuaweiCloud struct { type AccessConfigForHuaweiCloud struct {
AccessKeyId string `json:"accessKeyId"` AccessKeyId string `json:"accessKeyId"`
SecretAccessKey string `json:"secretAccessKey"` SecretAccessKey string `json:"secretAccessKey"`
Region string `json:"region"`
} }
type AccessConfigForLocal struct{} type AccessConfigForLocal struct{}

View File

@ -1,6 +1,10 @@
package maps package maps
import "strconv" import (
"strconv"
mapstructure "github.com/go-viper/mapstructure/v2"
)
// 以字符串形式从字典中获取指定键的值。 // 以字符串形式从字典中获取指定键的值。
// //
@ -178,3 +182,27 @@ func GetValueOrDefaultAsBool(dict map[string]any, key string, defaultValue bool)
return defaultValue return defaultValue
} }
// 将字典解码为指定类型的结构体。
//
// 入参:
// - dict: 字典。
// - output: 结构体指针。
//
// 出参:
// - 错误信息。如果解码失败,则返回错误信息。
func Decode(dict map[string]any, output any) error {
config := &mapstructure.DecoderConfig{
Metadata: nil,
Result: output,
WeaklyTypedInput: true,
TagName: "json",
}
decoder, err := mapstructure.NewDecoder(config)
if err != nil {
return err
}
return decoder.Decode(dict)
}

View File

@ -130,20 +130,22 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className,
}; };
const handleFormChange = (_: unknown, values: AccessFormFieldValues) => { const handleFormChange = (_: unknown, values: AccessFormFieldValues) => {
if (values.provider !== fieldProvider) {
formInst.setFieldValue("provider", values.provider);
}
onValuesChange?.(values); onValuesChange?.(values);
}; };
useImperativeHandle(ref, () => { useImperativeHandle(ref, () => {
return { return {
getFieldsValue: () => { getFieldsValue: () => {
return formInst.getFieldsValue(true); const values = formInst.getFieldsValue(true);
values.config = nestedFormInst.getFieldsValue();
return values;
}, },
resetFields: (fields) => { resetFields: (fields) => {
return formInst.resetFields(fields); formInst.resetFields(fields);
if (!!fields && fields.includes("config")) {
nestedFormInst.resetFields(fields);
}
}, },
validateFields: (nameList, config) => { validateFields: (nameList, config) => {
const t1 = formInst.validateFields(nameList, config); const t1 = formInst.validateFields(nameList, config);

View File

@ -19,8 +19,6 @@ const initFormModel = (): AccessFormAWSConfigFieldValues => {
return { return {
accessKeyId: "", accessKeyId: "",
secretAccessKey: "", secretAccessKey: "",
region: "us-east-1",
hostedZoneId: "",
}; };
}; };
@ -38,18 +36,6 @@ const AccessFormAWSConfig = ({ form: formInst, formName, disabled, initialValues
.min(1, t("access.form.aws_secret_access_key.placeholder")) .min(1, t("access.form.aws_secret_access_key.placeholder"))
.max(64, t("common.errmsg.string_max", { max: 64 })) .max(64, t("common.errmsg.string_max", { max: 64 }))
.trim(), .trim(),
// TODO: 该字段仅用于申请证书,后续迁移到工作流表单中
region: z
.string()
.max(64, t("common.errmsg.string_max", { max: 64 }))
.trim()
.nullish(),
// TODO: 该字段仅用于申请证书,后续迁移到工作流表单中
hostedZoneId: z
.string()
.max(64, t("common.errmsg.string_max", { max: 64 }))
.trim()
.nullish(),
}); });
const formRule = createSchemaFieldRule(formSchema); const formRule = createSchemaFieldRule(formSchema);
@ -83,24 +69,6 @@ const AccessFormAWSConfig = ({ form: formInst, formName, disabled, initialValues
> >
<Input.Password autoComplete="new-password" placeholder={t("access.form.aws_secret_access_key.placeholder")} /> <Input.Password autoComplete="new-password" placeholder={t("access.form.aws_secret_access_key.placeholder")} />
</Form.Item> </Form.Item>
<Form.Item
name="region"
label={t("access.form.aws_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.aws_region.tooltip") }}></span>}
>
<Input placeholder={t("access.form.aws_region.placeholder")} />
</Form.Item>
<Form.Item
name="hostedZoneId"
label={t("access.form.aws_hosted_zone_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.aws_hosted_zone_id.tooltip") }}></span>}
>
<Input placeholder={t("access.form.aws_hosted_zone_id.placeholder")} />
</Form.Item>
</Form> </Form>
); );
}; };

View File

@ -19,7 +19,6 @@ const initFormModel = (): AccessFormHuaweiCloudConfigFieldValues => {
return { return {
accessKeyId: "", accessKeyId: "",
secretAccessKey: "", secretAccessKey: "",
region: "cn-north-1",
}; };
}; };
@ -37,12 +36,6 @@ const AccessFormHuaweiCloudConfig = ({ form: formInst, formName, disabled, initi
.min(1, t("access.form.huaweicloud_secret_access_key.placeholder")) .min(1, t("access.form.huaweicloud_secret_access_key.placeholder"))
.max(64, t("common.errmsg.string_max", { max: 64 })) .max(64, t("common.errmsg.string_max", { max: 64 }))
.trim(), .trim(),
// TODO: 该字段仅用于申请证书,后续迁移到工作流表单中
region: z
.string()
.max(64, t("common.errmsg.string_max", { max: 64 }))
.trim()
.nullish(),
}); });
const formRule = createSchemaFieldRule(formSchema); const formRule = createSchemaFieldRule(formSchema);
@ -76,15 +69,6 @@ const AccessFormHuaweiCloudConfig = ({ form: formInst, formName, disabled, initi
> >
<Input.Password autoComplete="new-password" placeholder={t("access.form.huaweicloud_secret_access_key.placeholder")} /> <Input.Password autoComplete="new-password" placeholder={t("access.form.huaweicloud_secret_access_key.placeholder")} />
</Form.Item> </Form.Item>
<Form.Item
name="region"
label={t("access.form.huaweicloud_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.huaweicloud_region.tooltip") }}></span>}
>
<Input placeholder={t("access.form.huaweicloud_region.placeholder")} />
</Form.Item>
</Form> </Form>
); );
}; };

View File

@ -1,4 +1,4 @@
import { forwardRef, memo, useEffect, useImperativeHandle, useState } from "react"; import { forwardRef, memo, useEffect, useImperativeHandle, useMemo, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { FormOutlined as FormOutlinedIcon, PlusOutlined as PlusOutlinedIcon, QuestionCircleOutlined as QuestionCircleOutlinedIcon } from "@ant-design/icons"; import { FormOutlined as FormOutlinedIcon, PlusOutlined as PlusOutlinedIcon, QuestionCircleOutlined as QuestionCircleOutlinedIcon } from "@ant-design/icons";
import { useControllableValue } from "ahooks"; import { useControllableValue } from "ahooks";
@ -11,13 +11,16 @@ import MultipleInput from "@/components/MultipleInput";
import AccessEditModal from "@/components/access/AccessEditModal"; import AccessEditModal from "@/components/access/AccessEditModal";
import AccessSelect from "@/components/access/AccessSelect"; import AccessSelect from "@/components/access/AccessSelect";
import ApplyDNSProviderSelect from "@/components/provider/ApplyDNSProviderSelect"; import ApplyDNSProviderSelect from "@/components/provider/ApplyDNSProviderSelect";
import { ACCESS_USAGES, accessProvidersMap, applyDNSProvidersMap } from "@/domain/provider"; import { ACCESS_PROVIDERS, ACCESS_USAGES, APPLY_DNS_PROVIDERS, accessProvidersMap, applyDNSProvidersMap } from "@/domain/provider";
import { type WorkflowNodeConfigForApply } from "@/domain/workflow"; import { type WorkflowNodeConfigForApply } from "@/domain/workflow";
import { useAntdForm, useZustandShallowSelector } from "@/hooks"; import { useAntdForm, useAntdFormName, useZustandShallowSelector } from "@/hooks";
import { useAccessesStore } from "@/stores/access"; import { useAccessesStore } from "@/stores/access";
import { useContactEmailsStore } from "@/stores/contact"; import { useContactEmailsStore } from "@/stores/contact";
import { validDomainName, validIPv4Address, validIPv6Address } from "@/utils/validators"; import { validDomainName, validIPv4Address, validIPv6Address } from "@/utils/validators";
import ApplyNodeConfigFormAWSRoute53Config from "./ApplyNodeConfigFormAWSRoute53Config";
import ApplyNodeConfigFormHuaweiCloudDNSConfig from "./ApplyNodeConfigFormHuaweiCloudDNSConfig";
type ApplyNodeConfigFormFieldValues = Partial<WorkflowNodeConfigForApply>; type ApplyNodeConfigFormFieldValues = Partial<WorkflowNodeConfigForApply>;
export type ApplyNodeConfigFormProps = { export type ApplyNodeConfigFormProps = {
@ -61,6 +64,7 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
providerAccessId: z providerAccessId: z
.string({ message: t("workflow_node.apply.form.provider_access.placeholder") }) .string({ message: t("workflow_node.apply.form.provider_access.placeholder") })
.min(1, t("workflow_node.apply.form.provider_access.placeholder")), .min(1, t("workflow_node.apply.form.provider_access.placeholder")),
providerConfig: z.any(),
keyAlgorithm: z keyAlgorithm: z
.string({ message: t("workflow_node.apply.form.key_algorithm.placeholder") }) .string({ message: t("workflow_node.apply.form.key_algorithm.placeholder") })
.nonempty(t("workflow_node.apply.form.key_algorithm.placeholder")), .nonempty(t("workflow_node.apply.form.key_algorithm.placeholder")),
@ -92,6 +96,30 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
const fieldDomains = Form.useWatch<string>("domains", formInst); const fieldDomains = Form.useWatch<string>("domains", formInst);
const fieldNameservers = Form.useWatch<string>("nameservers", formInst); const fieldNameservers = Form.useWatch<string>("nameservers", formInst);
const [nestedFormInst] = Form.useForm();
const nestedFormName = useAntdFormName({ form: nestedFormInst, name: "workflowNodeApplyConfigFormProviderConfigForm" });
const nestedFormEl = useMemo(() => {
const nestedFormProps = {
form: nestedFormInst,
formName: nestedFormName,
disabled: disabled,
initialValues: initialValues?.providerConfig,
};
/*
ASCII
NOTICE: If you add new child component, please keep ASCII order.
*/
switch (fieldProvider) {
case ACCESS_PROVIDERS.AWS:
case APPLY_DNS_PROVIDERS.AWS_ROUTE53:
return <ApplyNodeConfigFormAWSRoute53Config {...nestedFormProps} />;
case ACCESS_PROVIDERS.HUAWEICLOUD:
case APPLY_DNS_PROVIDERS.HUAWEICLOUD_DNS:
return <ApplyNodeConfigFormHuaweiCloudDNSConfig {...nestedFormProps} />;
}
}, [disabled, initialValues?.providerConfig, fieldProvider, nestedFormInst, nestedFormName]);
const handleProviderSelect = (value: string) => { const handleProviderSelect = (value: string) => {
if (fieldProvider === value) return; if (fieldProvider === value) return;
@ -115,6 +143,13 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
onValuesChange?.(formInst.getFieldsValue(true)); onValuesChange?.(formInst.getFieldsValue(true));
}; };
const handleFormProviderChange = (name: string) => {
if (name === nestedFormName) {
formInst.setFieldValue("providerConfig", nestedFormInst.getFieldsValue());
onValuesChange?.(formInst.getFieldsValue(true));
}
};
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => { const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
onValuesChange?.(values as ApplyNodeConfigFormFieldValues); onValuesChange?.(values as ApplyNodeConfigFormFieldValues);
}; };
@ -122,18 +157,27 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
useImperativeHandle(ref, () => { useImperativeHandle(ref, () => {
return { return {
getFieldsValue: () => { getFieldsValue: () => {
return formInst.getFieldsValue(true); const values = formInst.getFieldsValue(true);
values.providerConfig = nestedFormInst.getFieldsValue();
return values;
}, },
resetFields: (fields) => { resetFields: (fields) => {
return formInst.resetFields(fields); formInst.resetFields(fields);
if (!!fields && fields.includes("providerConfig")) {
nestedFormInst.resetFields(fields);
}
}, },
validateFields: (nameList, config) => { validateFields: (nameList, config) => {
return formInst.validateFields(nameList, config); const t1 = formInst.validateFields(nameList, config);
const t2 = nestedFormInst.validateFields(undefined, config);
return Promise.all([t1, t2]).then(() => t1);
}, },
} as ApplyNodeConfigFormInstance; } as ApplyNodeConfigFormInstance;
}); });
return ( return (
<Form.Provider onFormChange={handleFormProviderChange}>
<Form className={className} style={style} {...formProps} disabled={disabled} layout="vertical" scrollToFirstError onValuesChange={handleFormChange}> <Form className={className} style={style} {...formProps} disabled={disabled} layout="vertical" scrollToFirstError onValuesChange={handleFormChange}>
<Form.Item <Form.Item
label={t("workflow_node.apply.form.domains.label")} label={t("workflow_node.apply.form.domains.label")}
@ -217,6 +261,9 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
/> />
</Form.Item> </Form.Item>
</Form.Item> </Form.Item>
</Form>
{nestedFormEl}
<Divider className="my-1"> <Divider className="my-1">
<Typography.Text className="text-xs font-normal" type="secondary"> <Typography.Text className="text-xs font-normal" type="secondary">
@ -224,6 +271,7 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
</Typography.Text> </Typography.Text>
</Divider> </Divider>
<Form className={className} style={style} {...formProps} disabled={disabled} layout="vertical" scrollToFirstError onValuesChange={handleFormChange}>
<Form.Item name="keyAlgorithm" label={t("workflow_node.apply.form.key_algorithm.label")} rules={[formRule]}> <Form.Item name="keyAlgorithm" label={t("workflow_node.apply.form.key_algorithm.label")} rules={[formRule]}>
<Select <Select
options={["RSA2048", "RSA3072", "RSA4096", "RSA8192", "EC256", "EC384"].map((e) => ({ options={["RSA2048", "RSA3072", "RSA4096", "RSA8192", "EC256", "EC384"].map((e) => ({
@ -289,6 +337,7 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
<Switch /> <Switch />
</Form.Item> </Form.Item>
</Form> </Form>
</Form.Provider>
); );
} }
); );

View File

@ -0,0 +1,81 @@
import { useTranslation } from "react-i18next";
import { Form, type FormInstance, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
type ApplyNodeConfigFormAWSRoute53ConfigFieldValues = Nullish<{
region: string;
hostedZoneId: string;
}>;
export type ApplyNodeConfigFormAWSRoute53ConfigProps = {
form: FormInstance;
formName: string;
disabled?: boolean;
initialValues?: ApplyNodeConfigFormAWSRoute53ConfigFieldValues;
onValuesChange?: (values: ApplyNodeConfigFormAWSRoute53ConfigFieldValues) => void;
};
const initFormModel = (): ApplyNodeConfigFormAWSRoute53ConfigFieldValues => {
return {
region: "us-east-1",
hostedZoneId: "",
};
};
const ApplyNodeConfigFormAWSRoute53Config = ({
form: formInst,
formName,
disabled,
initialValues,
onValuesChange,
}: ApplyNodeConfigFormAWSRoute53ConfigProps) => {
const { t } = useTranslation();
const formSchema = z.object({
region: z
.string({ message: t("workflow_node.apply.form.aws_route53_region.placeholder") })
.nonempty(t("workflow_node.apply.form.aws_route53_region.placeholder"))
.trim(),
hostedZoneId: z
.string({ message: t("workflow_node.apply.form.aws_route53_hosted_zone_id.placeholder") })
.nonempty(t("workflow_node.apply.form.aws_route53_hosted_zone_id.placeholder"))
.trim(),
});
const formRule = createSchemaFieldRule(formSchema);
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
onValuesChange?.(values);
};
return (
<Form
form={formInst}
disabled={disabled}
initialValues={initialValues ?? initFormModel()}
layout="vertical"
name={formName}
onValuesChange={handleFormChange}
>
<Form.Item
name="region"
label={t("workflow_node.apply.form.aws_route53_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.apply.form.aws_route53_region.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.apply.form.aws_route53_region.placeholder")} />
</Form.Item>
<Form.Item
name="hostedZoneId"
label={t("workflow_node.apply.form.aws_route53_hosted_zone_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.apply.form.aws_route53_hosted_zone_id.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.apply.form.aws_route53_hosted_zone_id.placeholder")} />
</Form.Item>
</Form>
);
};
export default ApplyNodeConfigFormAWSRoute53Config;

View File

@ -0,0 +1,66 @@
import { useTranslation } from "react-i18next";
import { Form, type FormInstance, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
type ApplyNodeConfigFormHuaweiCloudDNSConfigFieldValues = Nullish<{
region: string;
}>;
export type ApplyNodeConfigFormHuaweiCloudDNSConfigProps = {
form: FormInstance;
formName: string;
disabled?: boolean;
initialValues?: ApplyNodeConfigFormHuaweiCloudDNSConfigFieldValues;
onValuesChange?: (values: ApplyNodeConfigFormHuaweiCloudDNSConfigFieldValues) => void;
};
const initFormModel = (): ApplyNodeConfigFormHuaweiCloudDNSConfigFieldValues => {
return {
region: "cn-north-1",
};
};
const ApplyNodeConfigFormHuaweiCloudDNSConfig = ({
form: formInst,
formName,
disabled,
initialValues,
onValuesChange,
}: ApplyNodeConfigFormHuaweiCloudDNSConfigProps) => {
const { t } = useTranslation();
const formSchema = z.object({
region: z
.string({ message: t("workflow_node.apply.form.huaweicloud_dns_region.placeholder") })
.nonempty(t("workflow_node.apply.form.huaweicloud_dns_region.placeholder"))
.trim(),
});
const formRule = createSchemaFieldRule(formSchema);
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
onValuesChange?.(values);
};
return (
<Form
form={formInst}
disabled={disabled}
initialValues={initialValues ?? initFormModel()}
layout="vertical"
name={formName}
onValuesChange={handleFormChange}
>
<Form.Item
name="region"
label={t("workflow_node.apply.form.huaweicloud_dns_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.apply.form.huaweicloud_dns_region.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.apply.form.huaweicloud_dns_region.placeholder")} />
</Form.Item>
</Form>
);
};
export default ApplyNodeConfigFormHuaweiCloudDNSConfig;

View File

@ -80,7 +80,8 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
provider: z.string({ message: t("workflow_node.deploy.form.provider.placeholder") }).nonempty(t("workflow_node.deploy.form.provider.placeholder")), provider: z.string({ message: t("workflow_node.deploy.form.provider.placeholder") }).nonempty(t("workflow_node.deploy.form.provider.placeholder")),
providerAccessId: z providerAccessId: z
.string({ message: t("workflow_node.deploy.form.provider_access.placeholder") }) .string({ message: t("workflow_node.deploy.form.provider_access.placeholder") })
.nonempty(t("workflow_node.deploy.form.provider_access.placeholder")), .nonempty(t("workflow_node.deploy.form.provider_access.placeholder"))
.refine(() => !!formInst.getFieldValue("provider"), t("workflow_node.deploy.form.provider.placeholder")),
providerConfig: z.any(), providerConfig: z.any(),
}); });
const formRule = createSchemaFieldRule(formSchema); const formRule = createSchemaFieldRule(formSchema);
@ -200,10 +201,16 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
useImperativeHandle(ref, () => { useImperativeHandle(ref, () => {
return { return {
getFieldsValue: () => { getFieldsValue: () => {
return formInst.getFieldsValue(true); const values = formInst.getFieldsValue(true);
values.providerConfig = nestedFormInst.getFieldsValue();
return values;
}, },
resetFields: (fields) => { resetFields: (fields) => {
return formInst.resetFields(fields); formInst.resetFields(fields);
if (!!fields && fields.includes("providerConfig")) {
nestedFormInst.resetFields(fields);
}
}, },
validateFields: (nameList, config) => { validateFields: (nameList, config) => {
const t1 = formInst.validateFields(nameList, config); const t1 = formInst.validateFields(nameList, config);
@ -297,16 +304,18 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
placeholder={t("workflow_node.deploy.form.certificate.placeholder")} placeholder={t("workflow_node.deploy.form.certificate.placeholder")}
/> />
</Form.Item> </Form.Item>
</Show>
</Form>
<Show when={!!fieldProvider}>
<Divider className="my-1"> <Divider className="my-1">
<Typography.Text className="text-xs font-normal" type="secondary"> <Typography.Text className="text-xs font-normal" type="secondary">
{t("workflow_node.deploy.form.params_config.label")} {t("workflow_node.deploy.form.params_config.label")}
</Typography.Text> </Typography.Text>
</Divider> </Divider>
</Show>
</Form>
<Show when={!!fieldProvider}>{nestedFormEl}</Show> {nestedFormEl}
</Show>
</Form.Provider> </Form.Provider>
); );
} }

View File

@ -105,6 +105,7 @@ export type WorkflowNodeConfigForApply = {
contactEmail: string; contactEmail: string;
provider: string; provider: string;
providerAccessId: string; providerAccessId: string;
providerConfig?: Record<string, unknown>;
keyAlgorithm: string; keyAlgorithm: string;
nameservers?: string; nameservers?: string;
propagationTimeout?: number; propagationTimeout?: number;

View File

@ -45,12 +45,6 @@
"access.form.aws_secret_access_key.label": "AWS SecretAccessKey", "access.form.aws_secret_access_key.label": "AWS SecretAccessKey",
"access.form.aws_secret_access_key.placeholder": "Please enter AWS SecretAccessKey", "access.form.aws_secret_access_key.placeholder": "Please enter AWS SecretAccessKey",
"access.form.aws_secret_access_key.tooltip": "For more information, see <a href=\"https://docs.aws.amazon.com/en_us/IAM/latest/UserGuide/id_credentials_access-keys.html\" target=\"_blank\">https://docs.aws.amazon.com/en_us/IAM/latest/UserGuide/id_credentials_access-keys.html</a>", "access.form.aws_secret_access_key.tooltip": "For more information, see <a href=\"https://docs.aws.amazon.com/en_us/IAM/latest/UserGuide/id_credentials_access-keys.html\" target=\"_blank\">https://docs.aws.amazon.com/en_us/IAM/latest/UserGuide/id_credentials_access-keys.html</a>",
"access.form.aws_region.label": "AWS Region",
"access.form.aws_region.placeholder": "Please enter AWS region (e.g. us-east-1)",
"access.form.aws_region.tooltip": "For more information, see <a href=\"https://docs.aws.amazon.com/en_us/general/latest/gr/rande.html#regional-endpoints\" target=\"_blank\">https://docs.aws.amazon.com/en_us/general/latest/gr/rande.html#regional-endpoints</a>",
"access.form.aws_hosted_zone_id.label": "AWS hosted zone ID",
"access.form.aws_hosted_zone_id.placeholder": "Please enter AWS hosted zone ID",
"access.form.aws_hosted_zone_id.tooltip": "For more information, see <a href=\"https://docs.aws.amazon.com/en_us/Route53/latest/DeveloperGuide/hosted-zones-working-with.html\" target=\"_blank\">https://docs.aws.amazon.com/en_us/Route53/latest/DeveloperGuide/hosted-zones-working-with.html</a>",
"access.form.baiducloud_access_key_id.label": "Baidu Cloud AccessKeyID", "access.form.baiducloud_access_key_id.label": "Baidu Cloud AccessKeyID",
"access.form.baiducloud_access_key_id.placeholder": "Please enter Baidu Cloud AccessKeyID", "access.form.baiducloud_access_key_id.placeholder": "Please enter Baidu Cloud AccessKeyID",
"access.form.baiducloud_access_key_id.tooltip": "For more information, see <a href=\"https://intl.cloud.baidu.com/doc/Reference/s/jjwvz2e3p-en\" target=\"_blank\">https://intl.cloud.baidu.com/doc/Reference/s/jjwvz2e3p-en</a>", "access.form.baiducloud_access_key_id.tooltip": "For more information, see <a href=\"https://intl.cloud.baidu.com/doc/Reference/s/jjwvz2e3p-en\" target=\"_blank\">https://intl.cloud.baidu.com/doc/Reference/s/jjwvz2e3p-en</a>",
@ -84,9 +78,6 @@
"access.form.huaweicloud_secret_access_key.label": "Huawei Cloud SecretAccessKey", "access.form.huaweicloud_secret_access_key.label": "Huawei Cloud SecretAccessKey",
"access.form.huaweicloud_secret_access_key.placeholder": "Please enter Huawei Cloud SecretAccessKey", "access.form.huaweicloud_secret_access_key.placeholder": "Please enter Huawei Cloud SecretAccessKey",
"access.form.huaweicloud_secret_access_key.tooltip": "For more information, see <a href=\"https://support.huaweicloud.com/intl/en-us/usermanual-ca/ca_01_0003.html\" target=\"_blank\">https://support.huaweicloud.com/intl/en-us/usermanual-ca/ca_01_0003.html</a>", "access.form.huaweicloud_secret_access_key.tooltip": "For more information, see <a href=\"https://support.huaweicloud.com/intl/en-us/usermanual-ca/ca_01_0003.html\" target=\"_blank\">https://support.huaweicloud.com/intl/en-us/usermanual-ca/ca_01_0003.html</a>",
"access.form.huaweicloud_region.label": "Huawei Cloud region",
"access.form.huaweicloud_region.placeholder": "Please enter Huawei Cloud region (e.g. cn-north-1)",
"access.form.huaweicloud_region.tooltip": "For more information, see <a href=\"https://console-intl.huaweicloud.com/apiexplorer/#/endpoint?locale=en-us\" target=\"_blank\">https://console-intl.huaweicloud.com/apiexplorer/#/endpoint</a>",
"access.form.k8s_kubeconfig.label": "KubeConfig", "access.form.k8s_kubeconfig.label": "KubeConfig",
"access.form.k8s_kubeconfig.placeholder": "Please enter KubeConfig file", "access.form.k8s_kubeconfig.placeholder": "Please enter KubeConfig file",
"access.form.k8s_kubeconfig.upload": "Choose File ...", "access.form.k8s_kubeconfig.upload": "Choose File ...",

View File

@ -37,6 +37,15 @@
"workflow_node.apply.form.provider_access.placeholder": "Please select an authorization of DNS provider", "workflow_node.apply.form.provider_access.placeholder": "Please select an authorization of DNS provider",
"workflow_node.apply.form.provider_access.tooltip": "Used to manage DNS records during ACME DNS-01 authentication.", "workflow_node.apply.form.provider_access.tooltip": "Used to manage DNS records during ACME DNS-01 authentication.",
"workflow_node.apply.form.provider_access.button": "Create", "workflow_node.apply.form.provider_access.button": "Create",
"workflow_node.apply.form.aws_route53_region.label": "AWS Route53 Region",
"workflow_node.apply.form.aws_route53_region.placeholder": "Please enter AWS Route53 region (e.g. us-east-1)",
"workflow_node.apply.form.aws_route53_region.tooltip": "For more information, see <a href=\"https://docs.aws.amazon.com/en_us/general/latest/gr/rande.html#regional-endpoints\" target=\"_blank\">https://docs.aws.amazon.com/en_us/general/latest/gr/rande.html#regional-endpoints</a>",
"workflow_node.apply.form.aws_route53_hosted_zone_id.label": "AWS Route53 hosted zone ID",
"workflow_node.apply.form.aws_route53_hosted_zone_id.placeholder": "Please enter AWS Route53 hosted zone ID",
"workflow_node.apply.form.aws_route53_hosted_zone_id.tooltip": "For more information, see <a href=\"https://docs.aws.amazon.com/en_us/Route53/latest/DeveloperGuide/hosted-zones-working-with.html\" target=\"_blank\">https://docs.aws.amazon.com/en_us/Route53/latest/DeveloperGuide/hosted-zones-working-with.html</a>",
"workflow_node.apply.form.huaweicloud_dns_region.label": "Huawei Cloud DNS region",
"workflow_node.apply.form.huaweicloud_dns_region.placeholder": "Please enter Huawei Cloud DNS region (e.g. cn-north-1)",
"workflow_node.apply.form.huaweicloud_dns_region.tooltip": "For more information, see <a href=\"https://console-intl.huaweicloud.com/apiexplorer/#/endpoint?locale=en-us\" target=\"_blank\">https://console-intl.huaweicloud.com/apiexplorer/#/endpoint</a>",
"workflow_node.apply.form.advanced_config.label": "Advanced settings", "workflow_node.apply.form.advanced_config.label": "Advanced settings",
"workflow_node.apply.form.key_algorithm.label": "Certificate key algorithm", "workflow_node.apply.form.key_algorithm.label": "Certificate key algorithm",
"workflow_node.apply.form.key_algorithm.placeholder": "Please select certificate key algorithm", "workflow_node.apply.form.key_algorithm.placeholder": "Please select certificate key algorithm",

View File

@ -45,12 +45,6 @@
"access.form.aws_secret_access_key.label": "AWS SecretAccessKey", "access.form.aws_secret_access_key.label": "AWS SecretAccessKey",
"access.form.aws_secret_access_key.placeholder": "请输入 AWS SecretAccessKey", "access.form.aws_secret_access_key.placeholder": "请输入 AWS SecretAccessKey",
"access.form.aws_secret_access_key.tooltip": "这是什么?请参阅 <a href=\"https://docs.aws.amazon.com/zh_cn/IAM/latest/UserGuide/id_credentials_access-keys.html\" target=\"_blank\">https://docs.aws.amazon.com/zh_cn/IAM/latest/UserGuide/id_credentials_access-keys.html</a>", "access.form.aws_secret_access_key.tooltip": "这是什么?请参阅 <a href=\"https://docs.aws.amazon.com/zh_cn/IAM/latest/UserGuide/id_credentials_access-keys.html\" target=\"_blank\">https://docs.aws.amazon.com/zh_cn/IAM/latest/UserGuide/id_credentials_access-keys.html</a>",
"access.form.aws_region.label": "AWS 区域",
"access.form.aws_region.placeholder": "请输入 AWS 区域例如us-east-1",
"access.form.aws_region.tooltip": "这是什么?请参阅 <a href=\"https://docs.aws.amazon.com/zh_cn/general/latest/gr/rande.html#regional-endpoints\" target=\"_blank\">https://docs.aws.amazon.com/zh_cn/general/latest/gr/rande.html#regional-endpoints</a>",
"access.form.aws_hosted_zone_id.label": "AWS 托管区域 ID",
"access.form.aws_hosted_zone_id.placeholder": "请输入 AWS 托管区域 ID",
"access.form.aws_hosted_zone_id.tooltip": "这是什么?请参阅 <a href=\"https://docs.aws.amazon.com/zh_cn/Route53/latest/DeveloperGuide/hosted-zones-working-with.html\" target=\"_blank\">https://docs.aws.amazon.com/zh_cn/Route53/latest/DeveloperGuide/hosted-zones-working-with.html</a>",
"access.form.baiducloud_access_key_id.label": "百度智能云 AccessKeyID", "access.form.baiducloud_access_key_id.label": "百度智能云 AccessKeyID",
"access.form.baiducloud_access_key_id.placeholder": "请输入百度智能云 AccessKeyID", "access.form.baiducloud_access_key_id.placeholder": "请输入百度智能云 AccessKeyID",
"access.form.baiducloud_access_key_id.tooltip": "这是什么?请参阅 <a href=\"https://cloud.baidu.com/doc/Reference/s/jjwvz2e3p\" target=\"_blank\">https://cloud.baidu.com/doc/Reference/s/jjwvz2e3p</a>", "access.form.baiducloud_access_key_id.tooltip": "这是什么?请参阅 <a href=\"https://cloud.baidu.com/doc/Reference/s/jjwvz2e3p\" target=\"_blank\">https://cloud.baidu.com/doc/Reference/s/jjwvz2e3p</a>",
@ -84,9 +78,6 @@
"access.form.huaweicloud_secret_access_key.label": "华为云 SecretAccessKey", "access.form.huaweicloud_secret_access_key.label": "华为云 SecretAccessKey",
"access.form.huaweicloud_secret_access_key.placeholder": "请输入华为云 SecretAccessKey", "access.form.huaweicloud_secret_access_key.placeholder": "请输入华为云 SecretAccessKey",
"access.form.huaweicloud_secret_access_key.tooltip": "这是什么?请参阅 <a href=\"https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html\" target=\"_blank\">https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html</a>", "access.form.huaweicloud_secret_access_key.tooltip": "这是什么?请参阅 <a href=\"https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html\" target=\"_blank\">https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html</a>",
"access.form.huaweicloud_region.label": "华为云区域",
"access.form.huaweicloud_region.placeholder": "请输入华为云区域例如cn-north-1",
"access.form.huaweicloud_region.tooltip": "这是什么?请参阅 <a href=\"https://console.huaweicloud.com/apiexplorer/#/endpoint\" target=\"_blank\">https://console.huaweicloud.com/apiexplorer/#/endpoint</a>",
"access.form.k8s_kubeconfig.label": "KubeConfig", "access.form.k8s_kubeconfig.label": "KubeConfig",
"access.form.k8s_kubeconfig.placeholder": "请选择 KubeConfig 文件", "access.form.k8s_kubeconfig.placeholder": "请选择 KubeConfig 文件",
"access.form.k8s_kubeconfig.upload": "选择文件", "access.form.k8s_kubeconfig.upload": "选择文件",

View File

@ -9,7 +9,7 @@
"workflow.action.delete.confirm": "确定要删除此工作流吗?", "workflow.action.delete.confirm": "确定要删除此工作流吗?",
"workflow.action.enable": "启用", "workflow.action.enable": "启用",
"workflow.action.enable.failed.uncompleted": "请先完成流程编排并发布更改", "workflow.action.enable.failed.uncompleted": "请先完成流程编排并发布更改",
"workflow.action.disable": "用", "workflow.action.disable": "用",
"workflow.props.name": "名称", "workflow.props.name": "名称",
"workflow.props.description": "描述", "workflow.props.description": "描述",

View File

@ -37,6 +37,15 @@
"workflow_node.apply.form.provider_access.placeholder": "请选择 DNS 提供商授权", "workflow_node.apply.form.provider_access.placeholder": "请选择 DNS 提供商授权",
"workflow_node.apply.form.provider_access.tooltip": "用于 ACME DNS-01 认证时操作域名解析记录,注意与部署阶段所需的主机提供商相区分。", "workflow_node.apply.form.provider_access.tooltip": "用于 ACME DNS-01 认证时操作域名解析记录,注意与部署阶段所需的主机提供商相区分。",
"workflow_node.apply.form.provider_access.button": "新建", "workflow_node.apply.form.provider_access.button": "新建",
"workflow_node.apply.form.aws_route53_region.label": "AWS Route53 区域",
"workflow_node.apply.form.aws_route53_region.placeholder": "请输入 AWS Route53 区域例如us-east-1",
"workflow_node.apply.form.aws_route53_region.tooltip": "这是什么?请参阅 <a href=\"https://docs.aws.amazon.com/zh_cn/general/latest/gr/rande.html#regional-endpoints\" tworkflow_node.applyank\">https://docs.aws.amazon.com/zh_cn/general/latest/gr/rande.html#regional-endpoints</a>",
"workflow_node.apply.form.aws_route53_hosted_zone_id.label": "AWS Route53 托管区域 ID",
"workflow_node.apply.form.aws_route53_hosted_zone_id.placeholder": "请输入 AWS Route53 托管区域 ID",
"workflow_node.apply.form.aws_route53_hosted_zone_id.tooltip": "这是什么?请参阅 <a href=\"https://docs.aws.amazon.com/zh_cn/Route53/latest/DeveloperGuide/hosted-zones-working-with.html\" target=\"_blank\">https://docs.aws.amazon.com/zh_cn/Route53/latest/DeveloperGuide/hosted-zones-working-with.html</a>",
"workflow_node.apply.form.huaweicloud_dns_region.label": "华为云 DNS 服务区域",
"workflow_node.apply.form.huaweicloud_dns_region.placeholder": "请输入华为云 DNS 服务区域例如cn-north-1",
"workflow_node.apply.form.huaweicloud_dns_region.tooltip": "这是什么?请参阅 <a href=\"https://console.huaweicloud.com/apiexplorer/#/endpoint\" target=\"_blank\">https://console.huaweicloud.com/apiexplorer/#/endpoint</a>",
"workflow_node.apply.form.advanced_config.label": "高级设置", "workflow_node.apply.form.advanced_config.label": "高级设置",
"workflow_node.apply.form.key_algorithm.label": "数字证书算法", "workflow_node.apply.form.key_algorithm.label": "数字证书算法",
"workflow_node.apply.form.key_algorithm.placeholder": "请选择数字证书算法", "workflow_node.apply.form.key_algorithm.placeholder": "请选择数字证书算法",