feat(provider): new deployment provider: cmcccloud cdn

This commit is contained in:
Fu Diwei 2026-06-15 18:05:55 +08:00 committed by RHQYZ
parent c277955e4d
commit 9eba19978d
17 changed files with 711 additions and 12 deletions

13
go.mod
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,10 @@
package cmcccloudcdn
const (
// 匹配模式:精确匹配。
DOMAIN_MATCH_PATTERN_EXACT = "exact"
// 匹配模式:通配符匹配。
DOMAIN_MATCH_PATTERN_WILDCARD = "wildcard"
// 匹配模式:证书 SAN 匹配。
DOMAIN_MATCH_PATTERN_CERTSAN = "certsan"
)

@ -1 +1 @@
Subproject commit 0c5c8fe9448323c68fd25d5dfe0e2471e0209400
Subproject commit ad2925860f15794f131bff0aef60d738100d1a93

View File

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

View File

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

View File

@ -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<Record<DeploymentProviderType, React.Compone
[DEPLOYMENT_PROVIDERS.BYTEPLUS_TOS]: BizDeployNodeConfigFieldsProviderBytePlusTOS,
[DEPLOYMENT_PROVIDERS.CDNFLY]: BizDeployNodeConfigFieldsProviderCdnfly,
[DEPLOYMENT_PROVIDERS.CLOUDFLARE_SSL]: BizDeployNodeConfigFieldsProviderCloudflareSSL,
[DEPLOYMENT_PROVIDERS.CMCCCLOUD_CDN]: BizDeployNodeConfigFieldsProviderCMCCCloudCDN,
[DEPLOYMENT_PROVIDERS.CPANEL]: BizDeployNodeConfigFieldsProviderCPanel,
[DEPLOYMENT_PROVIDERS.CTCCCLOUD_AO]: BizDeployNodeConfigFieldsProviderCTCCCloudAO,
[DEPLOYMENT_PROVIDERS.CTCCCLOUD_CDN]: BizDeployNodeConfigFieldsProviderCTCCCloudCDN,

View File

@ -0,0 +1,120 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input, Radio } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import Show from "@/components/Show";
import { isDomain } from "@/utils/validator";
import { useFormNestedFieldsContext } from "./_context";
const DOMAIN_MATCH_PATTERN_EXACT = "exact" as const;
const DOMAIN_MATCH_PATTERN_WILDCARD = "wildcard" as const;
const DOMAIN_MATCH_PATTERN_CERTSAN = "certsan" as const;
const BizDeployNodeConfigFieldsProviderCMCCCloudCDN = () => {
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 (
<>
<Form.Item
name={[parentNamePath, "domainMatchPattern"]}
initialValue={initialValues.domainMatchPattern}
label={t("workflow_node.deploy.form.shared_domain_match_pattern.label")}
extra={
fieldDomainMatchPattern === DOMAIN_MATCH_PATTERN_EXACT ? (
<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.shared_domain_match_pattern.option.exact.help.wildcard") }}></span>
) : (
void 0
)
}
rules={[formRule]}
>
<Radio.Group
options={[DOMAIN_MATCH_PATTERN_EXACT, DOMAIN_MATCH_PATTERN_WILDCARD, DOMAIN_MATCH_PATTERN_CERTSAN].map((s) => ({
label: t(`workflow_node.deploy.form.shared_domain_match_pattern.option.${s}.label`),
value: s,
}))}
/>
</Form.Item>
<Show when={fieldDomainMatchPattern !== DOMAIN_MATCH_PATTERN_CERTSAN}>
<Form.Item
name={[parentNamePath, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.cmcccloud_cdn_domain.label")}
rules={[formRule]}
>
<Input placeholder={t("workflow_node.deploy.form.cmcccloud_cdn_domain.placeholder")} />
</Form.Item>
</Show>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
domainMatchPattern: DOMAIN_MATCH_PATTERN_EXACT,
domain: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
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;

View File

@ -181,6 +181,7 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv
[ACCESS_PROVIDERS.GOOGLECLOUD, "provider.googlecloud", "/imgs/providers/google.svg", [ACCESS_USAGES.DNS, ACCESS_USAGES.HOSTING]],
[ACCESS_PROVIDERS.LINODE, "provider.linode", "/imgs/providers/linode.svg", [ACCESS_USAGES.DNS, ACCESS_USAGES.HOSTING]],
[ACCESS_PROVIDERS.BAIDUCLOUD, "provider.baiducloud", "/imgs/providers/baiducloud.svg", [ACCESS_USAGES.DNS, ACCESS_USAGES.HOSTING]],
[ACCESS_PROVIDERS.CMCCCLOUD, "provider.cmcccloud", "/imgs/providers/cmcccloud.svg", [ACCESS_USAGES.DNS, ACCESS_USAGES.HOSTING]],
[ACCESS_PROVIDERS.CTCCCLOUD, "provider.ctcccloud", "/imgs/providers/ctcccloud.svg", [ACCESS_USAGES.DNS, ACCESS_USAGES.HOSTING]],
[ACCESS_PROVIDERS.JDCLOUD, "provider.jdcloud", "/imgs/providers/jdcloud.svg", [ACCESS_USAGES.DNS, ACCESS_USAGES.HOSTING]],
[ACCESS_PROVIDERS.QINGCLOUD, "provider.qingcloud", "/imgs/providers/qingcloud.svg", [ACCESS_USAGES.DNS, ACCESS_USAGES.HOSTING]],
@ -252,7 +253,6 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv
[ACCESS_PROVIDERS.RUCENTER, "provider.rucenter", "/imgs/providers/rucenter.svg", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS.SPACESHIP, "provider.spaceship", "/imgs/providers/spaceship.png", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS.VULTR, "provider.vultr", "/imgs/providers/vultr.svg", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS.CMCCCLOUD, "provider.cmcccloud", "/imgs/providers/cmcccloud.svg", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS["35CN"], "provider.35cn", "/imgs/providers/35cn.png", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS["51DNSCOM"], "provider.51dnscom", "/imgs/providers/51dnscom.png", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS.DNSLA, "provider.dnsla", "/imgs/providers/dnsla.svg", [ACCESS_USAGES.DNS]],
@ -643,6 +643,7 @@ export const DEPLOYMENT_PROVIDERS = Object.freeze({
CACHEFLY: `${ACCESS_PROVIDERS.CACHEFLY}`,
CDNFLY: `${ACCESS_PROVIDERS.CDNFLY}`,
CLOUDFLARE_SSL: `${ACCESS_PROVIDERS.CLOUDFLARE}-ssl`,
CMCCCLOUD_CDN: `${ACCESS_PROVIDERS.CMCCCLOUD}-cdn`,
CPANEL: `${ACCESS_PROVIDERS.CPANEL}`,
CTCCCLOUD_AO: `${ACCESS_PROVIDERS.CTCCCLOUD}-ao`,
CTCCCLOUD_CDN: `${ACCESS_PROVIDERS.CTCCCLOUD}-cdn`,
@ -861,6 +862,7 @@ export const deploymentProvidersMap: Map<DeploymentProvider["type"] | string, De
[DEPLOYMENT_PROVIDERS.BAIDUCLOUD_BLB, "provider.baiducloud_blb", DEPLOYMENT_CATEGORIES.LOADBALANCE],
[DEPLOYMENT_PROVIDERS.BAIDUCLOUD_APPBLB, "provider.baiducloud_appblb", DEPLOYMENT_CATEGORIES.LOADBALANCE],
[DEPLOYMENT_PROVIDERS.BAIDUCLOUD_CERT, "provider.baiducloud_cert_upload", DEPLOYMENT_CATEGORIES.SSL],
[DEPLOYMENT_PROVIDERS.CMCCCLOUD_CDN, "provider.cmcccloud_cdn", DEPLOYMENT_CATEGORIES.CDN],
[DEPLOYMENT_PROVIDERS.CTCCCLOUD_CDN, "provider.ctcccloud_cdn", DEPLOYMENT_CATEGORIES.CDN],
[DEPLOYMENT_PROVIDERS.CTCCCLOUD_ICDN, "provider.ctcccloud_icdn", DEPLOYMENT_CATEGORIES.CDN],
[DEPLOYMENT_PROVIDERS.CTCCCLOUD_AO, "provider.ctcccloud_ao", DEPLOYMENT_CATEGORIES.CDN],

View File

@ -108,6 +108,7 @@
"cloudflare_ssl": "Cloudflare - Edge Certificates",
"cloudns": "ClouDNS",
"cmcccloud": "China Mobile ECloud",
"cmcccloud_cdn": "China Mobile ECloud - CDN (Content Delivery Network)",
"cmcccloud_dns": "China Mobile ECloud - DNS",
"conohavps": "ConoHa VPS",
"constellix": "Constellix",

View File

@ -1428,6 +1428,10 @@
"help": "Notes: Leave it blank to import a new certificate; otherwise, to replace the existing one.",
"tooltip": "For more information, see <a href=\"https://developers.cloudflare.com/ssl/edge-certificates/custom-certificates/uploading/\" target=\"_blank\">https://developers.cloudflare.com/ssl/edge-certificates/custom-certificates/uploading/</a>"
},
"cmcccloud_cdn_domain": {
"label": "CMCC ECloud CDN domain",
"placeholder": "Please enter CMCC ECloud CDN domain name"
},
"cpanel_deploy_target": {
"option": {
"website": {

View File

@ -108,6 +108,7 @@
"cloudflare_ssl": "Cloudflare - 边缘证书",
"cloudns": "ClouDNS",
"cmcccloud": "移动云",
"cmcccloud_cdn": "移动云 - 内容分发网络 CDN",
"cmcccloud_dns": "移动云 - 云解析 DNS",
"conohavps": "ConoHa VPS",
"constellix": "Constellix",

View File

@ -1427,6 +1427,10 @@
"help": "提示:不填写时,将上传新证书;否则,将替换原证书。",
"tooltip": "这是什么?请参阅 <a href=\"https://developers.cloudflare.com/ssl/edge-certificates/custom-certificates/uploading/\" target=\"_blank\">https://developers.cloudflare.com/ssl/edge-certificates/custom-certificates/uploading/</a>"
},
"cmcccloud_cdn_domain": {
"label": "移动云 CDN 加速域名",
"placeholder": "请输入移动云 CDN 加速域名"
},
"cpanel_deploy_target": {
"option": {
"website": {