From 9eba19978d1aef6ab69e600a3cd5d915d050e41a Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Mon, 15 Jun 2026 18:05:55 +0800 Subject: [PATCH] feat(provider): new deployment provider: cmcccloud cdn --- go.mod | 13 +- .../certmgmt/deployers/sp_cmcccloud_cdn.go | 27 ++ internal/domain/provider.go | 1 + .../dns01/cmcccloud/internal/lego.go | 2 +- .../providers/cmcccloud-cdn/cmcccloud_cdn.go | 287 ++++++++++++++++++ .../cmcccloud-cdn/cmcccloud_cdn_test.go | 54 ++++ .../providers/cmcccloud-cdn/consts.go | 10 + pkg/sdk3rd-forked | 2 +- .../ecloud/ecloudsdkclouddns/client.go | 12 +- .../ecloud/ecloudsdkcmcdn/client.go | 179 +++++++++++ .../BizDeployNodeConfigFieldsProvider.tsx | 2 + ...oyNodeConfigFieldsProviderCMCCCloudCDN.tsx | 120 ++++++++ ui/src/domain/provider.ts | 4 +- ui/src/i18n/resources/en/nls.provider.json | 1 + .../i18n/resources/en/nls.workflow.nodes.json | 4 + ui/src/i18n/resources/zh/nls.provider.json | 1 + .../i18n/resources/zh/nls.workflow.nodes.json | 4 + 17 files changed, 711 insertions(+), 12 deletions(-) create mode 100644 internal/certmgmt/deployers/sp_cmcccloud_cdn.go create mode 100644 pkg/core/deployer/providers/cmcccloud-cdn/cmcccloud_cdn.go create mode 100644 pkg/core/deployer/providers/cmcccloud-cdn/cmcccloud_cdn_test.go create mode 100644 pkg/core/deployer/providers/cmcccloud-cdn/consts.go create mode 100644 pkg/sdk3rd-trimmed/gitlab.ecloud.com/ecloud/ecloudsdkcmcdn/client.go create mode 100644 ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProviderCMCCCloudCDN.tsx diff --git a/go.mod b/go.mod index 510ad839..788ce144 100644 --- a/go.mod +++ b/go.mod @@ -85,8 +85,10 @@ require ( github.com/xhit/go-str2duration/v2 v2.1.0 github.com/yunify/qingcloud-sdk-go v2.0.0-alpha.38+incompatible github.com/zenlayer/zenlayercloud-sdk-go v0.2.39 + gitlab.ecloud.com/ecloud/ecloudsdkcloudcore v1.0.0 gitlab.ecloud.com/ecloud/ecloudsdkclouddns v1.0.1 - gitlab.ecloud.com/ecloud/ecloudsdkcore v1.0.0 + gitlab.ecloud.com/ecloud/ecloudsdkcmcdn v1.0.0 + gitlab.ecloud.com/ecloud/ecloudsdkcore v1.0.2 golang.org/x/crypto v0.52.0 golang.org/x/oauth2 v0.36.0 golang.org/x/sync v0.20.0 @@ -288,5 +290,10 @@ require ( modernc.org/sqlite v1.52.0 // indirect ) -replace gitlab.ecloud.com/ecloud/ecloudsdkcore v1.0.0 => ./pkg/sdk3rd-forked/gitlab.ecloud.com/ecloud/ecloudsdkcore@v1.0.0 -replace gitlab.ecloud.com/ecloud/ecloudsdkclouddns v1.0.1 => ./pkg/sdk3rd-forked/gitlab.ecloud.com/ecloud/ecloudsdkclouddns@v1.0.1 +replace gitlab.ecloud.com/ecloud/ecloudsdkcloudcore v1.0.0 => ./pkg/sdk3rd-forked/gitlab.ecloud.com/ecloud/ecloudsdkcloudcore@v1.0.0+compact + +replace gitlab.ecloud.com/ecloud/ecloudsdkclouddns v1.0.1 => ./pkg/sdk3rd-forked/gitlab.ecloud.com/ecloud/ecloudsdkclouddns@v1.0.1+compact + +replace gitlab.ecloud.com/ecloud/ecloudsdkcmcdn v1.0.0 => ./pkg/sdk3rd-forked/gitlab.ecloud.com/ecloud/ecloudsdkcmcdn@v1.0.0 + +replace gitlab.ecloud.com/ecloud/ecloudsdkcore v1.0.2 => ./pkg/sdk3rd-forked/gitlab.ecloud.com/ecloud/ecloudsdkcore@v1.0.2 diff --git a/internal/certmgmt/deployers/sp_cmcccloud_cdn.go b/internal/certmgmt/deployers/sp_cmcccloud_cdn.go new file mode 100644 index 00000000..d09bd25d --- /dev/null +++ b/internal/certmgmt/deployers/sp_cmcccloud_cdn.go @@ -0,0 +1,27 @@ +package deployers + +import ( + "fmt" + + "github.com/certimate-go/certimate/internal/domain" + "github.com/certimate-go/certimate/pkg/core" + dplyimpl "github.com/certimate-go/certimate/pkg/core/deployer/providers/cmcccloud-cdn" + xmaps "github.com/certimate-go/certimate/pkg/utils/maps" +) + +func init() { + Registries.MustRegister(domain.DeploymentProviderTypeCMCCCloudCDN, func(options *ProviderFactoryOptions) (core.Deployer, error) { + credentials := domain.AccessConfigForCMCCCloud{} + if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + provider, err := dplyimpl.NewDeployer(&dplyimpl.DeployerConfig{ + AccessKeyId: credentials.AccessKeyId, + AccessKeySecret: credentials.AccessKeySecret, + DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"), + Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"), + }) + return provider, err + }) +} diff --git a/internal/domain/provider.go b/internal/domain/provider.go index 8d5f57b7..e20ec820 100644 --- a/internal/domain/provider.go +++ b/internal/domain/provider.go @@ -357,6 +357,7 @@ const ( DeploymentProviderTypeCacheFly = DeploymentProviderType(AccessProviderTypeCacheFly) DeploymentProviderTypeCdnfly = DeploymentProviderType(AccessProviderTypeCdnfly) DeploymentProviderTypeCloudflareSSL = DeploymentProviderType(AccessProviderTypeCloudflare + "-ssl") + DeploymentProviderTypeCMCCCloudCDN = DeploymentProviderType(AccessProviderTypeCMCCCloud + "-cdn") DeploymentProviderTypeCPanel = DeploymentProviderType(AccessProviderTypeCPanel) DeploymentProviderTypeCTCCCloudAO = DeploymentProviderType(AccessProviderTypeCTCCCloud + "-ao") DeploymentProviderTypeCTCCCloudCDN = DeploymentProviderType(AccessProviderTypeCTCCCloud + "-cdn") diff --git a/pkg/core/certifier/challengers/dns01/cmcccloud/internal/lego.go b/pkg/core/certifier/challengers/dns01/cmcccloud/internal/lego.go index 254be5fb..868e7875 100644 --- a/pkg/core/certifier/challengers/dns01/cmcccloud/internal/lego.go +++ b/pkg/core/certifier/challengers/dns01/cmcccloud/internal/lego.go @@ -10,8 +10,8 @@ import ( "github.com/go-acme/lego/v5/challenge/dns01" "github.com/go-acme/lego/v5/platform/env" "github.com/samber/lo" + "gitlab.ecloud.com/ecloud/ecloudsdkcloudcore/config" "gitlab.ecloud.com/ecloud/ecloudsdkclouddns/model" - "gitlab.ecloud.com/ecloud/ecloudsdkcore/config" "github.com/certimate-go/certimate/pkg/sdk3rd-trimmed/gitlab.ecloud.com/ecloud/ecloudsdkclouddns" ) diff --git a/pkg/core/deployer/providers/cmcccloud-cdn/cmcccloud_cdn.go b/pkg/core/deployer/providers/cmcccloud-cdn/cmcccloud_cdn.go new file mode 100644 index 00000000..769920df --- /dev/null +++ b/pkg/core/deployer/providers/cmcccloud-cdn/cmcccloud_cdn.go @@ -0,0 +1,287 @@ +package cmcccloudcdn + +import ( + "context" + "errors" + "fmt" + "log/slog" + "time" + + "github.com/samber/lo" + "gitlab.ecloud.com/ecloud/ecloudsdkcmcdn/model" + "gitlab.ecloud.com/ecloud/ecloudsdkcore/config" + + "github.com/certimate-go/certimate/pkg/sdk3rd-trimmed/gitlab.ecloud.com/ecloud/ecloudsdkcmcdn" + + "github.com/certimate-go/certimate/pkg/core" + xcert "github.com/certimate-go/certimate/pkg/utils/cert" + xcerthostname "github.com/certimate-go/certimate/pkg/utils/cert/hostname" +) + +type ( + Provider = core.Deployer + DeployResult = core.DeployerDeployResult +) + +type DeployerConfig struct { + // 移动云 AccessKeyId。 + AccessKeyId string `json:"accessKeyId"` + // 移动云 AccessKeySecret。 + AccessKeySecret string `json:"accessKeySecret"` + // 域名匹配模式。 + // 零值时默认值 [DOMAIN_MATCH_PATTERN_EXACT]。 + DomainMatchPattern string `json:"domainMatchPattern,omitempty"` + // 加速域名(不支持泛域名)。 + Domain string `json:"domain"` +} + +type Deployer struct { + config *DeployerConfig + logger *slog.Logger + sdkClient *ecloudsdkcmcdn.Client +} + +var _ Provider = (*Deployer)(nil) + +func NewDeployer(config *DeployerConfig) (*Deployer, error) { + if config == nil { + return nil, fmt.Errorf("the configuration of the deployer provider is nil") + } + + client, err := createSDKClient(config.AccessKeyId, config.AccessKeySecret) + if err != nil { + return nil, fmt.Errorf("could not create client: %w", err) + } + + return &Deployer{ + config: config, + logger: slog.Default(), + sdkClient: client, + }, nil +} + +func (d *Deployer) SetLogger(logger *slog.Logger) { + if logger == nil { + d.logger = slog.New(slog.DiscardHandler) + } else { + d.logger = logger + } +} + +func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*DeployResult, error) { + // 获取待部署的域名列表 + var domainIds []int32 + switch d.config.DomainMatchPattern { + case "", DOMAIN_MATCH_PATTERN_EXACT: + { + if d.config.Domain == "" { + return nil, fmt.Errorf("config `domain` is required") + } + + domainCandidates, err := d.getAllDomains(ctx) + if err != nil { + return nil, err + } + + domains := lo.Filter(domainCandidates, func(domainItem *model.DescribeUserDomainsResponseList, _ int) bool { + return d.config.Domain == lo.FromPtr(domainItem.DomainName) + }) + if len(domains) == 0 { + return nil, fmt.Errorf("could not find domain") + } + + domainIds = lo.Map(domains, func(domainItem *model.DescribeUserDomainsResponseList, _ int) int32 { + return lo.FromPtr(domainItem.DomainId) + }) + } + + case DOMAIN_MATCH_PATTERN_WILDCARD: + { + if d.config.Domain == "" { + return nil, fmt.Errorf("config `domain` is required") + } + + domainCandidates, err := d.getAllDomains(ctx) + if err != nil { + return nil, err + } + + domains := lo.Filter(domainCandidates, func(domainItem *model.DescribeUserDomainsResponseList, _ int) bool { + return xcerthostname.IsMatch(d.config.Domain, lo.FromPtr(domainItem.DomainName)) + }) + if len(domains) == 0 { + return nil, fmt.Errorf("could not find any domains matched by wildcard") + } + + domainIds = lo.Map(domains, func(domainItem *model.DescribeUserDomainsResponseList, _ int) int32 { + return lo.FromPtr(domainItem.DomainId) + }) + } + + case DOMAIN_MATCH_PATTERN_CERTSAN: + { + domainCandidates, err := d.getAllDomains(ctx) + if err != nil { + return nil, err + } + + domains := lo.Filter(domainCandidates, func(domainItem *model.DescribeUserDomainsResponseList, _ int) bool { + return xcerthostname.IsMatchByCertificatePEM(certPEM, lo.FromPtr(domainItem.DomainName)) + }) + if len(domains) == 0 { + return nil, fmt.Errorf("could not find any domains matched by certificate") + } + + domainIds = lo.Map(domains, func(domainItem *model.DescribeUserDomainsResponseList, _ int) int32 { + return lo.FromPtr(domainItem.DomainId) + }) + } + + default: + return nil, fmt.Errorf("unsupported domain match pattern: '%s'", d.config.DomainMatchPattern) + } + + // 遍历更新域名证书 + if len(domainIds) == 0 { + d.logger.Info("no cdn domains to deploy") + } else { + d.logger.Info("found cdn domains to deploy", slog.Any("domainIds", domainIds)) + var errs []error + + for _, domainId := range domainIds { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + if err := d.updateDomainCertificate(ctx, domainId, certPEM, privkeyPEM); err != nil { + errs = append(errs, err) + } + } + } + + if len(errs) > 0 { + return nil, errors.Join(errs...) + } + } + + return &DeployResult{}, nil +} + +func (d *Deployer) getAllDomains(ctx context.Context) ([]*model.DescribeUserDomainsResponseList, error) { + domains := make([]*model.DescribeUserDomainsResponseList, 0) + + // 查询域名列表 + // REF: https://ecloud.10086.cn/op-help-center/doc/article/71517 + describeUserDomainsPage := 1 + describeUserDomainsPageSize := 10 + for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + + describeUserDomainsReq := &model.DescribeUserDomainsRequest{ + &model.DescribeUserDomainsQuery{ + Page: lo.ToPtr(fmt.Sprintf("%d", describeUserDomainsPage)), + PageSize: lo.ToPtr(fmt.Sprintf("%d", describeUserDomainsPageSize)), + }, + } + describeUserDomainsResp, err := d.sdkClient.DescribeUserDomains(describeUserDomainsReq) + d.logger.Debug("sdk request 'ecdn.DescribeUserDomains'", slog.Any("request", describeUserDomainsReq), slog.Any("response", describeUserDomainsResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'ecdn.DescribeUserDomains': %w", err) + } + + if describeUserDomainsResp.Body == nil || describeUserDomainsResp.Body.List == nil { + break + } + + ignoredStatuses := []string{"ADD_AUDITING", "ADD_OPENING", "PAUSE_AUDITING", "PAUSE_HANDLING", "OFFLINE_AUDITING", "OFFLINE_HANDLING", "OFFLINE", "AUDIT_FAIL", "ADD_FAIL", "OFFLINE_FAIL"} + for _, domainItem := range *describeUserDomainsResp.Body.List { + if lo.FromPtr(domainItem.Deleted) { + continue + } + if lo.Contains(ignoredStatuses, string(lo.FromPtr(domainItem.DomainStatus))) { + continue + } + + domains = append(domains, &domainItem) + } + + if len(*describeUserDomainsResp.Body.List) < describeUserDomainsPageSize { + break + } + + describeUserDomainsPage++ + } + + return domains, nil +} + +func (d *Deployer) updateDomainCertificate(ctx context.Context, cloudDomainId int32, certPEM, privkeyPEM string) error { + // 查询域名详情 + // REF: https://ecloud.10086.cn/op-help-center/doc/article/71520 + describeCdnDomainDetailReq := &model.DescribeCdnDomainDetailRequest{ + &model.DescribeCdnDomainDetailPath{ + DomainId: lo.ToPtr(fmt.Sprintf("%d", cloudDomainId)), + }, + } + describeCdnDomainDetailResp, err := d.sdkClient.DescribeCdnDomainDetail(describeCdnDomainDetailReq) + d.logger.Debug("sdk request 'ecdn.DescribeCdnDomainDetail'", slog.Any("request", describeCdnDomainDetailReq), slog.Any("response", describeCdnDomainDetailResp)) + if err != nil { + return fmt.Errorf("failed to execute sdk request 'ecdn.DescribeCdnDomainDetail': %w", err) + } + + // 查询证书详情,避免重复上传 + // REF: https://ecloud.10086.cn/op-help-center/doc/article/71524 + if lo.FromPtr(describeCdnDomainDetailResp.Body.CrtUniqueId) != "" { + describeCdnCertificateDetailReq := &model.DescribeCdnCertificateDetailRequest{ + &model.DescribeCdnCertificateDetailPath{ + UniqueId: describeCdnDomainDetailResp.Body.CrtUniqueId, + }, + } + describeCdnCertificateDetailResp, err := d.sdkClient.DescribeCdnCertificateDetail(describeCdnCertificateDetailReq) + d.logger.Debug("sdk request 'ecdn.DescribeCdnCertificateDetail'", slog.Any("request", describeCdnCertificateDetailReq), slog.Any("response", describeCdnCertificateDetailResp)) + if err != nil { + return fmt.Errorf("failed to execute sdk request 'ecdn.DescribeCdnCertificateDetail': %w", err) + } else { + if xcert.EqualCertificatesFromPEM(certPEM, lo.FromPtr(describeCdnCertificateDetailResp.Body.Certificate)) { + d.logger.Info("ssl certificate already deployed") + } + } + } + + // 添加域名证书 + addDomainServerCertificateReq := &model.AddDomainServerCertificateRequest{ + &model.AddDomainServerCertificateBody{ + DomainId: lo.ToPtr(cloudDomainId), + CrtName: lo.ToPtr(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())), + Certificate: lo.ToPtr(certPEM), + PrivateKey: lo.ToPtr(privkeyPEM), + }, + } + addDomainServerCertificateResp, err := d.sdkClient.AddDomainServerCertificate(addDomainServerCertificateReq) + d.logger.Debug("sdk request 'ecdn.AddDomainServerCertificate'", slog.Any("request", addDomainServerCertificateReq), slog.Any("response", addDomainServerCertificateResp)) + if err != nil { + return fmt.Errorf("failed to execute sdk request 'ecdn.AddDomainServerCertificate': %w", err) + } + + return nil +} + +func createSDKClient(accessKeyId, accessKeySecret string) (*ecloudsdkcmcdn.Client, error) { + ak := accessKeyId + sk := accessKeySecret + + // 资源池一览 https://ecloud.10086.cn/op-help-center/doc/article/54462 + poolId := "CIDC-CORE-00" + + client := ecloudsdkcmcdn.NewClient(&config.Config{ + AccessKey: &ak, + SecretKey: &sk, + PoolId: &poolId, + }) + + return client, nil +} diff --git a/pkg/core/deployer/providers/cmcccloud-cdn/cmcccloud_cdn_test.go b/pkg/core/deployer/providers/cmcccloud-cdn/cmcccloud_cdn_test.go new file mode 100644 index 00000000..4f9b40a4 --- /dev/null +++ b/pkg/core/deployer/providers/cmcccloud-cdn/cmcccloud_cdn_test.go @@ -0,0 +1,54 @@ +package cmcccloudcdn_test + +import ( + "testing" + + "github.com/certimate-go/certimate/pkg/core/deployer/internal/tester" + impl "github.com/certimate-go/certimate/pkg/core/deployer/providers/cmcccloud-cdn" +) + +var ( + fp = tester.Args("CMCCCLOUDCDN_") + fTestCertPath string + fTestKeyPath string + fAccessKeyId string + fAccessKeySecret string + fDomain string +) + +func init() { + fp.DefineString(&fTestCertPath, "TESTCERTPATH") + fp.DefineString(&fTestKeyPath, "TESTKEYPATH") + fp.DefineString(&fAccessKeyId, "ACCESSKEYID") + fp.DefineString(&fAccessKeySecret, "ACCESSKEYSECRET") + fp.DefineString(&fDomain, "DOMAIN") +} + +/* +Shell command to run this test: + + go test -v ./cmcccloud_cdn_test.go -args \ + --CMCCCLOUDCDN_TESTCERTPATH="/path/to/your-test-cert.pem" \ + --CMCCCLOUDCDN_TESTKEYPATH="/path/to/your-test-key.pem" \ + --CMCCCLOUDCDN_ACCESSKEYID="your-access-key-id" \ + --CMCCCLOUDCDN_ACCESSKEYSECRET="your-access-key-secret" \ + --CMCCCLOUDCDN_DOMAIN="example.com" +*/ +func TestProvider(t *testing.T) { + fp.Parse() + + t.Run("Deploy", func(t *testing.T) { + provider, err := impl.NewDeployer(&impl.DeployerConfig{ + AccessKeyId: fAccessKeyId, + AccessKeySecret: fAccessKeySecret, + DomainMatchPattern: impl.DOMAIN_MATCH_PATTERN_EXACT, + Domain: fDomain, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + tester.TestDeploy(t, provider, tester.TestDeployArgs{CertPath: fTestCertPath, KeyPath: fTestKeyPath}) + }) +} diff --git a/pkg/core/deployer/providers/cmcccloud-cdn/consts.go b/pkg/core/deployer/providers/cmcccloud-cdn/consts.go new file mode 100644 index 00000000..1aabc02c --- /dev/null +++ b/pkg/core/deployer/providers/cmcccloud-cdn/consts.go @@ -0,0 +1,10 @@ +package cmcccloudcdn + +const ( + // 匹配模式:精确匹配。 + DOMAIN_MATCH_PATTERN_EXACT = "exact" + // 匹配模式:通配符匹配。 + DOMAIN_MATCH_PATTERN_WILDCARD = "wildcard" + // 匹配模式:证书 SAN 匹配。 + DOMAIN_MATCH_PATTERN_CERTSAN = "certsan" +) diff --git a/pkg/sdk3rd-forked b/pkg/sdk3rd-forked index 0c5c8fe9..ad292586 160000 --- a/pkg/sdk3rd-forked +++ b/pkg/sdk3rd-forked @@ -1 +1 @@ -Subproject commit 0c5c8fe9448323c68fd25d5dfe0e2471e0209400 +Subproject commit ad2925860f15794f131bff0aef60d738100d1a93 diff --git a/pkg/sdk3rd-trimmed/gitlab.ecloud.com/ecloud/ecloudsdkclouddns/client.go b/pkg/sdk3rd-trimmed/gitlab.ecloud.com/ecloud/ecloudsdkclouddns/client.go index b28a5263..365baf12 100644 --- a/pkg/sdk3rd-trimmed/gitlab.ecloud.com/ecloud/ecloudsdkclouddns/client.go +++ b/pkg/sdk3rd-trimmed/gitlab.ecloud.com/ecloud/ecloudsdkclouddns/client.go @@ -1,22 +1,22 @@ package ecloudsdkclouddns import ( + "gitlab.ecloud.com/ecloud/ecloudsdkcloudcore" + "gitlab.ecloud.com/ecloud/ecloudsdkcloudcore/config" "gitlab.ecloud.com/ecloud/ecloudsdkclouddns/model" - "gitlab.ecloud.com/ecloud/ecloudsdkcore" - "gitlab.ecloud.com/ecloud/ecloudsdkcore/config" ) type Client struct { - APIClient *ecloudsdkcore.APIClient + APIClient *ecloudsdkcloudcore.APIClient config *config.Config - httpRequest *ecloudsdkcore.HttpRequest + httpRequest *ecloudsdkcloudcore.HttpRequest } func NewClient(config *config.Config) *Client { client := &Client{} client.config = config - apiClient := ecloudsdkcore.NewAPIClient() - httpRequest := ecloudsdkcore.NewDefaultHttpRequest() + apiClient := ecloudsdkcloudcore.NewAPIClient() + httpRequest := ecloudsdkcloudcore.NewDefaultHttpRequest() httpRequest.Product = product httpRequest.Version = version httpRequest.SdkVersion = sdkVersion diff --git a/pkg/sdk3rd-trimmed/gitlab.ecloud.com/ecloud/ecloudsdkcmcdn/client.go b/pkg/sdk3rd-trimmed/gitlab.ecloud.com/ecloud/ecloudsdkcmcdn/client.go new file mode 100644 index 00000000..084d7d3a --- /dev/null +++ b/pkg/sdk3rd-trimmed/gitlab.ecloud.com/ecloud/ecloudsdkcmcdn/client.go @@ -0,0 +1,179 @@ +package ecloudsdkcmcdn + +import ( + "gitlab.ecloud.com/ecloud/ecloudsdkcmcdn/model" + "gitlab.ecloud.com/ecloud/ecloudsdkcore" + "gitlab.ecloud.com/ecloud/ecloudsdkcore/config" + "gitlab.ecloud.com/ecloud/ecloudsdkcore/param" + "gitlab.ecloud.com/ecloud/ecloudsdkcore/request" + "gitlab.ecloud.com/ecloud/ecloudsdkcore/utils" +) + +type Client struct { + apiClient *ecloudsdkcore.APIClient + config *config.Config + httpRequest *request.HttpRequest + allRegions map[string]string +} + +func NewClient(config *config.Config) *Client { + httpRequest := request.DefaultHttpRequest() + httpRequest.Product = product + httpRequest.Version = version + httpRequest.SdkVersion = sdkVersion + ecloudsdkcore.InitConfig(config) + apiClient := ecloudsdkcore.DefaultApiClient(config, httpRequest) + client := &Client{ + apiClient: apiClient, + config: config, + httpRequest: httpRequest, + } + client.allRegions = client.initRegions() + client.setEndpoint(config, httpRequest) + return client +} + +const ( + product string = "CM-CDN" + version = "v1" + sdkVersion = "1.0.0" +) + +func (c *Client) initRegions() map[string]string { + m := map[string]string{ + "CIDC-RP-04": "https://console-yunnan-1.cmecloud.cn:8443", + "CIDC-RP-16": "https://console-qinghai-1.cmecloud.cn:8443", + "CIDC-RP-25": "https://console-wuxi-1.cmecloud.cn:8443", + "CIDC-RP-26": "https://console-dongguan-1.cmecloud.cn:8443", + "CIDC-RP-27": "https://console-yaan-1.cmecloud.cn:8443", + "CIDC-RP-28": "https://console-zhengzhou-1.cmecloud.cn:8443", + "CIDC-RP-29": "https://console-beijing-2.cmecloud.cn:8443", + "CIDC-RP-30": "https://console-zhuzhou-1.cmecloud.cn:8443", + "CIDC-RP-31": "https://console-jinan-1.cmecloud.cn:8443", + "CIDC-RP-32": "https://console-xian-1.cmecloud.cn:8443", + "CIDC-RP-33": "https://console-shanghai-1.cmecloud.cn:8443", + "CIDC-RP-34": "https://console-chongqing-1.cmecloud.cn:8443", + "CIDC-RP-35": "https://console-ningbo-1.cmecloud.cn:8443", + "CIDC-RP-36": "https://console-tianjin-1.cmecloud.cn:8443", + "CIDC-RP-37": "https://console-jilin-1.cmecloud.cn:8443", + "CIDC-RP-38": "https://console-hubei-1.cmecloud.cn:8443", + "CIDC-RP-39": "https://console-jiangxi-1.cmecloud.cn:8443", + "CIDC-RP-40": "https://console-gansu-1.cmecloud.cn:8443", + "CIDC-RP-41": "https://console-shanxi-1.cmecloud.cn:8443", + "CIDC-RP-42": "https://console-liaoning-1.cmecloud.cn:8443", + "CIDC-RP-43": "https://console-yunnan-2.cmecloud.cn:8443", + "CIDC-RP-44": "https://console-hebei-1.cmecloud.cn:8443", + "CIDC-RP-45": "https://console-fujian-1.cmecloud.cn:8443", + "CIDC-RP-46": "https://console-guangxi-1.cmecloud.cn:8443", + "CIDC-RP-47": "https://console-anhui-1.cmecloud.cn:8443", + "CIDC-RP-48": "https://console-huhehaote-1.cmecloud.cn:8443", + "CIDC-RP-49": "https://console-guiyang-1.cmecloud.cn:8443", + "CIDC-CORE-00": "https://ecloud.10086.cn", + "CIDC-RP-53": "https://console-hainan-1.cmecloud.cn:8443", + "CIDC-RP-54": "https://console-xinjiang-1.cmecloud.cn:8443", + "CIDC-RP-55": "http://console-heilongjiang-1.cmecloud.cn:18080", + "CIDC-BRP-25": "", + "CIDC-RP-60": "", + "CIDC-RP-61": "", + "CIDC-RP-62": "", + } + return m +} + +func (c *Client) setEndpoint(config *config.Config, httpRequest *request.HttpRequest) { + if utils.IsUnSet(config.PoolId) { + httpRequest.Endpoint = utils.DefaultEndpoint + return + } + endpoint := c.allRegions[*config.PoolId] + if utils.IsUnSet(endpoint) { + httpRequest.Endpoint = utils.DefaultEndpoint + return + } + httpRequest.Endpoint = endpoint +} + +func (c *Client) DescribeCdnCertificateDetail(request *model.DescribeCdnCertificateDetailRequest) (*model.DescribeCdnCertificateDetailResponse, error) { + return c.DescribeCdnCertificateDetailWithConfig(request, nil) +} + +func (c *Client) DescribeCdnCertificateDetailWithConfig(request *model.DescribeCdnCertificateDetailRequest, runtimeConfig *config.RuntimeConfig) (*model.DescribeCdnCertificateDetailResponse, error) { + params := param.NewParamsBuilder(). + Action("describeCdnCertificateDetail"). + Uri("/domainManager/openapi/certificate/describeCdnCertificateDetail/{uniqueId}"). + GatewayUri("/api/openapi-ecdn/domainManager/openapi/certificate/describeCdnCertificateDetail/{uniqueId}"). + Protocol("https"). + Method("GET"). + Request(request). + Build() + returnValue := &model.DescribeCdnCertificateDetailResponse{} + if _, err := c.apiClient.Excute(params, runtimeConfig, returnValue); err != nil { + return nil, err + } else { + return returnValue, nil + } +} + +func (c *Client) DescribeCdnDomainDetail(request *model.DescribeCdnDomainDetailRequest) (*model.DescribeCdnDomainDetailResponse, error) { + return c.DescribeCdnDomainDetailWithConfig(request, nil) +} + +func (c *Client) DescribeCdnDomainDetailWithConfig(request *model.DescribeCdnDomainDetailRequest, runtimeConfig *config.RuntimeConfig) (*model.DescribeCdnDomainDetailResponse, error) { + params := param.NewParamsBuilder(). + Action("describeCdnDomainDetail"). + Uri("/domainManager/openapi/domain/describeCdnDomainDetail/{domainId}"). + GatewayUri("/api/openapi-ecdn/domainManager/openapi/domain/describeCdnDomainDetail/{domainId}"). + Protocol("https"). + Method("GET"). + Request(request). + Build() + returnValue := &model.DescribeCdnDomainDetailResponse{} + if _, err := c.apiClient.Excute(params, runtimeConfig, returnValue); err != nil { + return nil, err + } else { + return returnValue, nil + } +} + +func (c *Client) DescribeUserDomains(request *model.DescribeUserDomainsRequest) (*model.DescribeUserDomainsResponse, error) { + return c.DescribeUserDomainsWithConfig(request, nil) +} + +func (c *Client) DescribeUserDomainsWithConfig(request *model.DescribeUserDomainsRequest, runtimeConfig *config.RuntimeConfig) (*model.DescribeUserDomainsResponse, error) { + params := param.NewParamsBuilder(). + Action("describeUserDomains"). + Uri("/domainManager/openapi/domain/describeUserDomains"). + GatewayUri("/api/openapi-ecdn/domainManager/openapi/domain/describeUserDomains"). + Protocol("https"). + Method("GET"). + Request(request). + Build() + returnValue := &model.DescribeUserDomainsResponse{} + if _, err := c.apiClient.Excute(params, runtimeConfig, returnValue); err != nil { + return nil, err + } else { + return returnValue, nil + } +} + +func (c *Client) AddDomainServerCertificate(request *model.AddDomainServerCertificateRequest) (*model.AddDomainServerCertificateResponse, error) { + return c.AddDomainServerCertificateWithConfig(request, nil) +} + +func (c *Client) AddDomainServerCertificateWithConfig(request *model.AddDomainServerCertificateRequest, runtimeConfig *config.RuntimeConfig) (*model.AddDomainServerCertificateResponse, error) { + params := param.NewParamsBuilder(). + Action("addDomainServerCertificate"). + Uri("/domainManager/openapi/certificate/addDomainServerCertificate"). + GatewayUri("/api/openapi-ecdn/domainManager/openapi/certificate/addDomainServerCertificate"). + Protocol("https"). + ContentType("application/json"). + Method("POST"). + Request(request). + Build() + returnValue := &model.AddDomainServerCertificateResponse{} + if _, err := c.apiClient.Excute(params, runtimeConfig, returnValue); err != nil { + return nil, err + } else { + return returnValue, nil + } +} diff --git a/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProvider.tsx b/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProvider.tsx index 8e1f3f80..a2b50c88 100644 --- a/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProvider.tsx +++ b/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProvider.tsx @@ -51,6 +51,7 @@ import BizDeployNodeConfigFieldsProviderBytePlusMediaLive from "./BizDeployNodeC import BizDeployNodeConfigFieldsProviderBytePlusTOS from "./BizDeployNodeConfigFieldsProviderBytePlusTOS"; import BizDeployNodeConfigFieldsProviderCdnfly from "./BizDeployNodeConfigFieldsProviderCdnfly"; import BizDeployNodeConfigFieldsProviderCloudflareSSL from "./BizDeployNodeConfigFieldsProviderCloudflareSSL"; +import BizDeployNodeConfigFieldsProviderCMCCCloudCDN from "./BizDeployNodeConfigFieldsProviderCMCCCloudCDN"; import BizDeployNodeConfigFieldsProviderCPanel from "./BizDeployNodeConfigFieldsProviderCPanel"; import BizDeployNodeConfigFieldsProviderCTCCCloudAO from "./BizDeployNodeConfigFieldsProviderCTCCCloudAO"; import BizDeployNodeConfigFieldsProviderCTCCCloudCDN from "./BizDeployNodeConfigFieldsProviderCTCCCloudCDN"; @@ -195,6 +196,7 @@ const providerComponentMap: Partial { + const { i18n, t } = useTranslation(); + + const { parentNamePath } = useFormNestedFieldsContext(); + const formSchema = z.object({ + [parentNamePath]: getSchema({ i18n }), + }); + const formRule = createSchemaFieldRule(formSchema); + const formInst = Form.useFormInstance(); + const initialValues = getInitialValues(); + + const fieldDomainMatchPattern = Form.useWatch([parentNamePath, "domainMatchPattern"], { form: formInst, preserve: true }); + + return ( + <> + + ) : ( + void 0 + ) + } + rules={[formRule]} + > + ({ + label: t(`workflow_node.deploy.form.shared_domain_match_pattern.option.${s}.label`), + value: s, + }))} + /> + + + + + + + + + ); +}; + +const getInitialValues = (): Nullish>> => { + return { + domainMatchPattern: DOMAIN_MATCH_PATTERN_EXACT, + domain: "", + }; +}; + +const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType }) => { + const { t } = i18n; + + return z + .object({ + domainMatchPattern: z.string().nonempty().default(DOMAIN_MATCH_PATTERN_EXACT), + domain: z.string().nullish(), + }) + .superRefine((values, ctx) => { + if (values.domainMatchPattern) { + switch (values.domainMatchPattern) { + case DOMAIN_MATCH_PATTERN_EXACT: + { + const scDomain = z.string().refine((v) => isDomain(v), t("common.errmsg.domain_invalid")); + const spDomain = scDomain.safeParse(values.domain); + if (!spDomain.success) { + ctx.addIssue({ + code: "custom", + message: z.treeifyError(spDomain.error).errors.join(), + path: ["domain"], + }); + } + } + break; + + case DOMAIN_MATCH_PATTERN_WILDCARD: + { + const scDomain = z.string().refine((v) => isDomain(v, { allowWildcard: true }), t("common.errmsg.domain_invalid")); + const spDomain = scDomain.safeParse(values.domain); + if (!spDomain.success) { + ctx.addIssue({ + code: "custom", + message: z.treeifyError(spDomain.error).errors.join(), + path: ["domain"], + }); + } + } + break; + } + } + }); +}; + +const _default = Object.assign(BizDeployNodeConfigFieldsProviderCMCCCloudCDN, { + getInitialValues, + getSchema, +}); + +export default _default; diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts index 2cf23ad7..2ce0d6c0 100644 --- a/ui/src/domain/provider.ts +++ b/ui/src/domain/provider.ts @@ -181,6 +181,7 @@ export const accessProvidersMap: Maphttps://developers.cloudflare.com/ssl/edge-certificates/custom-certificates/uploading/" }, + "cmcccloud_cdn_domain": { + "label": "CMCC ECloud CDN domain", + "placeholder": "Please enter CMCC ECloud CDN domain name" + }, "cpanel_deploy_target": { "option": { "website": { diff --git a/ui/src/i18n/resources/zh/nls.provider.json b/ui/src/i18n/resources/zh/nls.provider.json index 79ce7000..ab0f5213 100644 --- a/ui/src/i18n/resources/zh/nls.provider.json +++ b/ui/src/i18n/resources/zh/nls.provider.json @@ -108,6 +108,7 @@ "cloudflare_ssl": "Cloudflare - 边缘证书", "cloudns": "ClouDNS", "cmcccloud": "移动云", + "cmcccloud_cdn": "移动云 - 内容分发网络 CDN", "cmcccloud_dns": "移动云 - 云解析 DNS", "conohavps": "ConoHa VPS", "constellix": "Constellix", diff --git a/ui/src/i18n/resources/zh/nls.workflow.nodes.json b/ui/src/i18n/resources/zh/nls.workflow.nodes.json index d59aa145..861055b1 100644 --- a/ui/src/i18n/resources/zh/nls.workflow.nodes.json +++ b/ui/src/i18n/resources/zh/nls.workflow.nodes.json @@ -1427,6 +1427,10 @@ "help": "提示:不填写时,将上传新证书;否则,将替换原证书。", "tooltip": "这是什么?请参阅 https://developers.cloudflare.com/ssl/edge-certificates/custom-certificates/uploading/" }, + "cmcccloud_cdn_domain": { + "label": "移动云 CDN 加速域名", + "placeholder": "请输入移动云 CDN 加速域名" + }, "cpanel_deploy_target": { "option": { "website": {