From 0d44373de6f577d0846e1f5b134eaabfa5bb1a09 Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Fri, 13 Jun 2025 12:01:30 +0800 Subject: [PATCH] feat: new deployment provider: ctcccloud cdn --- internal/deployer/providers.go | 22 +++ internal/domain/provider.go | 6 + .../providers/ctcccloud-cdn/ctcccloud_cdn.go | 111 ++++++++++++ .../ctcccloud-cdn/ctcccloud_cdn_test.go | 75 ++++++++ .../providers/ctcccloud-cdn/ctcccloud_cdn.go | 171 ++++++++++++++++++ .../ctcccloud-cdn/ctcccloud_cdn_test.go | 72 ++++++++ .../wangsu-certificate/wangsu_certificate.go | 5 +- .../wangsu_certificate_test.go | 4 +- .../pkg/sdk3rd/ctyun/cdn/api_create_cert.go | 41 +++++ .../sdk3rd/ctyun/cdn/api_query_cert_detail.go | 51 ++++++ .../sdk3rd/ctyun/cdn/api_query_cert_list.go | 55 ++++++ .../ctyun/cdn/api_query_domain_detail.go | 64 +++++++ .../pkg/sdk3rd/ctyun/cdn/api_update_domain.go | 37 ++++ internal/pkg/sdk3rd/ctyun/cdn/client.go | 40 ++++ internal/pkg/sdk3rd/ctyun/cdn/types.go | 30 +++ .../pkg/sdk3rd/ctyun/dns/api_add_record.go | 8 +- .../pkg/sdk3rd/ctyun/dns/api_delete_record.go | 8 +- .../sdk3rd/ctyun/dns/api_query_record_list.go | 19 +- .../pkg/sdk3rd/ctyun/dns/api_update_record.go | 8 +- internal/pkg/sdk3rd/ctyun/dns/types.go | 11 ++ internal/pkg/sdk3rd/ctyun/openapi/client.go | 24 +-- .../pkg/utils/slice/{slice.go => iter.go} | 0 internal/pkg/utils/type/assert.go | 4 +- internal/pkg/utils/type/cast.go | 4 +- .../workflow/node/DeployNodeConfigForm.tsx | 3 + ...DeployNodeConfigFormCTCCCloudCDNConfig.tsx | 65 +++++++ ui/src/domain/provider.ts | 4 +- ui/src/i18n/locales/en/nls.provider.json | 14 +- .../i18n/locales/en/nls.workflow.nodes.json | 11 +- ui/src/i18n/locales/zh/nls.provider.json | 6 + .../i18n/locales/zh/nls.workflow.nodes.json | 9 +- 31 files changed, 925 insertions(+), 57 deletions(-) create mode 100644 internal/pkg/core/deployer/providers/ctcccloud-cdn/ctcccloud_cdn.go create mode 100644 internal/pkg/core/deployer/providers/ctcccloud-cdn/ctcccloud_cdn_test.go create mode 100644 internal/pkg/core/uploader/providers/ctcccloud-cdn/ctcccloud_cdn.go create mode 100644 internal/pkg/core/uploader/providers/ctcccloud-cdn/ctcccloud_cdn_test.go create mode 100644 internal/pkg/sdk3rd/ctyun/cdn/api_create_cert.go create mode 100644 internal/pkg/sdk3rd/ctyun/cdn/api_query_cert_detail.go create mode 100644 internal/pkg/sdk3rd/ctyun/cdn/api_query_cert_list.go create mode 100644 internal/pkg/sdk3rd/ctyun/cdn/api_query_domain_detail.go create mode 100644 internal/pkg/sdk3rd/ctyun/cdn/api_update_domain.go create mode 100644 internal/pkg/sdk3rd/ctyun/cdn/client.go create mode 100644 internal/pkg/sdk3rd/ctyun/cdn/types.go rename internal/pkg/utils/slice/{slice.go => iter.go} (100%) create mode 100644 ui/src/components/workflow/node/DeployNodeConfigFormCTCCCloudCDNConfig.tsx diff --git a/internal/deployer/providers.go b/internal/deployer/providers.go index 56234b95..f3d93a2c 100644 --- a/internal/deployer/providers.go +++ b/internal/deployer/providers.go @@ -43,6 +43,7 @@ import ( pBytePlusCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/byteplus-cdn" pCacheFly "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/cachefly" pCdnfly "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/cdnfly" + pCTCCCloudCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ctcccloud-cdn" pDogeCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/dogecloud-cdn" pEdgioApplications "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/edgio-applications" pFlexCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/flexcdn" @@ -621,6 +622,27 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer return deployer, err } + case domain.DeploymentProviderTypeCTCCCloudCDN: + { + access := domain.AccessConfigForCTCCCloud{} + if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + switch options.Provider { + case domain.DeploymentProviderTypeCTCCCloudCDN: + deployer, err := pCTCCCloudCDN.NewDeployer(&pCTCCCloudCDN.DeployerConfig{ + AccessKeyId: access.AccessKeyId, + SecretAccessKey: access.SecretAccessKey, + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), + }) + return deployer, err + + default: + break + } + } + case domain.DeploymentProviderTypeDogeCloudCDN: { access := domain.AccessConfigForDogeCloud{} diff --git a/internal/domain/provider.go b/internal/domain/provider.go index b10cda8f..f399c21b 100644 --- a/internal/domain/provider.go +++ b/internal/domain/provider.go @@ -215,6 +215,12 @@ const ( DeploymentProviderTypeBytePlusCDN = DeploymentProviderType(AccessProviderTypeBytePlus + "-cdn") DeploymentProviderTypeCacheFly = DeploymentProviderType(AccessProviderTypeCacheFly) DeploymentProviderTypeCdnfly = DeploymentProviderType(AccessProviderTypeCdnfly) + DeploymentProviderTypeCTCCCloudAO = DeploymentProviderType(AccessProviderTypeCTCCCloud + "-ao") // (预留) + DeploymentProviderTypeCTCCCloudCDN = DeploymentProviderType(AccessProviderTypeCTCCCloud + "-cdn") + DeploymentProviderTypeCTCCCloudCMS = DeploymentProviderType(AccessProviderTypeCTCCCloud + "-cms") // (预留) + DeploymentProviderTypeCTCCCloudELB = DeploymentProviderType(AccessProviderTypeCTCCCloud + "-elb") // (预留) + DeploymentProviderTypeCTCCCloudICDN = DeploymentProviderType(AccessProviderTypeCTCCCloud + "-icdn") // (预留) + DeploymentProviderTypeDogeCloudLVDN = DeploymentProviderType(AccessProviderTypeDogeCloud + "-ldvn") // (预留) DeploymentProviderTypeDogeCloudCDN = DeploymentProviderType(AccessProviderTypeDogeCloud + "-cdn") DeploymentProviderTypeEdgioApplications = DeploymentProviderType(AccessProviderTypeEdgio + "-applications") DeploymentProviderTypeFlexCDN = DeploymentProviderType(AccessProviderTypeFlexCDN) diff --git a/internal/pkg/core/deployer/providers/ctcccloud-cdn/ctcccloud_cdn.go b/internal/pkg/core/deployer/providers/ctcccloud-cdn/ctcccloud_cdn.go new file mode 100644 index 00000000..8cc9edaf --- /dev/null +++ b/internal/pkg/core/deployer/providers/ctcccloud-cdn/ctcccloud_cdn.go @@ -0,0 +1,111 @@ +package ctcccloudcdn + +import ( + "context" + "errors" + "fmt" + "log/slog" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + "github.com/usual2970/certimate/internal/pkg/core/uploader" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/ctcccloud-cdn" + ctyuncdn "github.com/usual2970/certimate/internal/pkg/sdk3rd/ctyun/cdn" + typeutil "github.com/usual2970/certimate/internal/pkg/utils/type" +) + +type DeployerConfig struct { + // 天翼云 AccessKeyId。 + AccessKeyId string `json:"accessKeyId"` + // 天翼云 SecretAccessKey。 + SecretAccessKey string `json:"secretAccessKey"` + // 加速域名(支持泛域名)。 + Domain string `json:"domain"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger *slog.Logger + sdkClient *ctyuncdn.Client + sslUploader uploader.Uploader +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.AccessKeyId, config.SecretAccessKey) + if err != nil { + return nil, fmt.Errorf("failed to create sdk client: %w", err) + } + + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ + AccessKeyId: config.AccessKeyId, + SecretAccessKey: config.SecretAccessKey, + }) + if err != nil { + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) + } + + return &DeployerProvider{ + config: config, + logger: slog.Default(), + sdkClient: client, + sslUploader: uploader, + }, nil +} + +func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { + if logger == nil { + d.logger = slog.New(slog.DiscardHandler) + } else { + d.logger = logger + } + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { + if d.config.Domain == "" { + return nil, errors.New("config `domain` is required") + } + + // 上传证书到 CDN + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) + if err != nil { + return nil, fmt.Errorf("failed to upload certificate file: %w", err) + } else { + d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) + } + + // 查询域名配置信息 + // REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=108&api=11304&data=161&isNormal=1&vid=154 + queryDomainDetailReq := &ctyuncdn.QueryDomainDetailRequest{ + Domain: typeutil.ToPtr(d.config.Domain), + } + queryDomainDetailResp, err := d.sdkClient.QueryDomainDetail(queryDomainDetailReq) + d.logger.Debug("sdk request 'cdn.QueryDomainDetail'", slog.Any("request", queryDomainDetailReq), slog.Any("response", queryDomainDetailResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'cdn.QueryDomainDetail': %w", err) + } + + // 修改域名配置 + // REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=108&api=11308&data=161&isNormal=1&vid=154 + updateDomainReq := &ctyuncdn.UpdateDomainRequest{ + Domain: typeutil.ToPtr(d.config.Domain), + HttpsStatus: typeutil.ToPtr("on"), + CertName: typeutil.ToPtr(upres.CertName), + } + updateDomainResp, err := d.sdkClient.UpdateDomain(updateDomainReq) + d.logger.Debug("sdk request 'cdn.UpdateDomain'", slog.Any("request", updateDomainReq), slog.Any("response", updateDomainResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'cdn.UpdateDomain': %w", err) + } + + return &deployer.DeployResult{}, nil +} + +func createSdkClient(accessKeyId, secretAccessKey string) (*ctyuncdn.Client, error) { + return ctyuncdn.NewClient(accessKeyId, secretAccessKey) +} diff --git a/internal/pkg/core/deployer/providers/ctcccloud-cdn/ctcccloud_cdn_test.go b/internal/pkg/core/deployer/providers/ctcccloud-cdn/ctcccloud_cdn_test.go new file mode 100644 index 00000000..7a754305 --- /dev/null +++ b/internal/pkg/core/deployer/providers/ctcccloud-cdn/ctcccloud_cdn_test.go @@ -0,0 +1,75 @@ +package ctcccloudcdn_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ctcccloud-cdn" +) + +var ( + fInputCertPath string + fInputKeyPath string + fAccessKeyId string + fSecretAccessKey string + fDomain string +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_CTCCCLOUDCDN_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "") + flag.StringVar(&fSecretAccessKey, argsPrefix+"SECRETACCESSKEY", "", "") + flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "") +} + +/* +Shell command to run this test: + + go test -v ./ctcccloud_cdn_test.go -args \ + --CERTIMATE_DEPLOYER_CTCCCLOUDCDN_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_CTCCCLOUDCDN_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_CTCCCLOUDCDN_ACCESSKEYID="your-access-key-id" \ + --CERTIMATE_DEPLOYER_CTCCCLOUDCDN_SECRETACCESSKEY="your-secret-access-key" \ + --CERTIMATE_DEPLOYER_CTCCCLOUDCDN_DOMAIN="example.com" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId), + fmt.Sprintf("SECRETACCESSKEY: %v", fSecretAccessKey), + fmt.Sprintf("DOMAIN: %v", fDomain), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + AccessKeyId: fAccessKeyId, + SecretAccessKey: fSecretAccessKey, + Domain: fDomain, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/core/uploader/providers/ctcccloud-cdn/ctcccloud_cdn.go b/internal/pkg/core/uploader/providers/ctcccloud-cdn/ctcccloud_cdn.go new file mode 100644 index 00000000..6281ede8 --- /dev/null +++ b/internal/pkg/core/uploader/providers/ctcccloud-cdn/ctcccloud_cdn.go @@ -0,0 +1,171 @@ +package ctcccloudcdn + +import ( + "context" + "fmt" + "log/slog" + "slices" + "strings" + "time" + + "github.com/usual2970/certimate/internal/pkg/core/uploader" + ctyuncdn "github.com/usual2970/certimate/internal/pkg/sdk3rd/ctyun/cdn" + certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" + typeutil "github.com/usual2970/certimate/internal/pkg/utils/type" +) + +type UploaderConfig struct { + // 天翼云 AccessKeyId。 + AccessKeyId string `json:"accessKeyId"` + // 天翼云 SecretAccessKey。 + SecretAccessKey string `json:"secretAccessKey"` +} + +type UploaderProvider struct { + config *UploaderConfig + logger *slog.Logger + sdkClient *ctyuncdn.Client +} + +var _ uploader.Uploader = (*UploaderProvider)(nil) + +func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.AccessKeyId, config.SecretAccessKey) + if err != nil { + return nil, fmt.Errorf("failed to create sdk client: %w", err) + } + + return &UploaderProvider{ + config: config, + logger: slog.Default(), + sdkClient: client, + }, nil +} + +func (u *UploaderProvider) WithLogger(logger *slog.Logger) uploader.Uploader { + if logger == nil { + u.logger = slog.New(slog.DiscardHandler) + } else { + u.logger = logger + } + return u +} + +func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPEM string) (*uploader.UploadResult, error) { + // 解析证书内容 + certX509, err := certutil.ParseCertificateFromPEM(certPEM) + if err != nil { + return nil, err + } + + // 查询证书列表,避免重复上传 + // REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=108&api=10901&data=161&isNormal=1&vid=154 + queryCertListPage := int32(1) + queryCertListPerPage := int32(1000) + for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + + queryCertListReq := &ctyuncdn.QueryCertListRequest{ + Page: typeutil.ToPtr(queryCertListPage), + PerPage: typeutil.ToPtr(queryCertListPerPage), + UsageMode: typeutil.ToPtr(int32(0)), + } + queryCertListResp, err := u.sdkClient.QueryCertList(queryCertListReq) + u.logger.Debug("sdk request 'cdn.QueryCertList'", slog.Any("request", queryCertListReq), slog.Any("response", queryCertListResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'cdn.QueryCertList': %w", err) + } + + if queryCertListResp.ReturnObj != nil { + for _, certRecord := range queryCertListResp.ReturnObj.Results { + // 对比证书通用名称 + if !strings.EqualFold(certX509.Subject.CommonName, certRecord.CN) { + continue + } + + // 对比证书扩展名称 + if !slices.Equal(certX509.DNSNames, certRecord.SANs) { + continue + } + + // 对比证书有效期 + if !certX509.NotBefore.Equal(time.Unix(certRecord.IssueTime, 0).UTC()) { + continue + } else if !certX509.NotAfter.Equal(time.Unix(certRecord.ExpiresTime, 0).UTC()) { + continue + } + + // 查询证书详情 + // REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=108&api=10899&data=161&isNormal=1&vid=154 + queryCertDetailReq := &ctyuncdn.QueryCertDetailRequest{ + Id: typeutil.ToPtr(certRecord.Id), + } + queryCertDetailResp, err := u.sdkClient.QueryCertDetail(queryCertDetailReq) + u.logger.Debug("sdk request 'cdn.QueryCertDetail'", slog.Any("request", queryCertDetailReq), slog.Any("response", queryCertDetailResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'cdn.QueryCertDetail': %w", err) + } else if queryCertDetailResp.ReturnObj != nil && queryCertDetailResp.ReturnObj.Result != nil { + var isSameCert bool + if queryCertDetailResp.ReturnObj.Result.Certs == certPEM { + isSameCert = true + } else { + oldCertX509, err := certutil.ParseCertificateFromPEM(queryCertDetailResp.ReturnObj.Result.Certs) + if err != nil { + continue + } + + isSameCert = certutil.EqualCertificate(certX509, oldCertX509) + } + + // 如果已存在相同证书,直接返回 + if isSameCert { + u.logger.Info("ssl certificate already exists") + return &uploader.UploadResult{ + CertId: fmt.Sprintf("%d", queryCertDetailResp.ReturnObj.Result.Id), + CertName: queryCertDetailResp.ReturnObj.Result.Name, + }, nil + } + } + } + } + + if queryCertListResp.ReturnObj == nil || len(queryCertListResp.ReturnObj.Results) < int(queryCertListPerPage) { + break + } else { + queryCertListPage++ + } + } + + // 生成新证书名(需符合天翼云命名规则) + certName := fmt.Sprintf("certimate-%d", time.Now().UnixMilli()) + + // 创建证书 + // REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=108&api=10893&data=161&isNormal=1&vid=154 + createCertReq := &ctyuncdn.CreateCertRequest{ + Name: typeutil.ToPtr(certName), + Certs: typeutil.ToPtr(certPEM), + Key: typeutil.ToPtr(privkeyPEM), + } + createCertResp, err := u.sdkClient.CreateCert(createCertReq) + u.logger.Debug("sdk request 'cdn.CreateCert'", slog.Any("request", createCertReq), slog.Any("response", createCertResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'cdn.CreateCert': %w", err) + } + + return &uploader.UploadResult{ + CertId: fmt.Sprintf("%d", createCertResp.ReturnObj.Id), + CertName: certName, + }, nil +} + +func createSdkClient(accessKeyId, secretAccessKey string) (*ctyuncdn.Client, error) { + return ctyuncdn.NewClient(accessKeyId, secretAccessKey) +} diff --git a/internal/pkg/core/uploader/providers/ctcccloud-cdn/ctcccloud_cdn_test.go b/internal/pkg/core/uploader/providers/ctcccloud-cdn/ctcccloud_cdn_test.go new file mode 100644 index 00000000..4582132b --- /dev/null +++ b/internal/pkg/core/uploader/providers/ctcccloud-cdn/ctcccloud_cdn_test.go @@ -0,0 +1,72 @@ +package ctcccloudcdn + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/ctcccloud-cdn" +) + +var ( + fInputCertPath string + fInputKeyPath string + fAccessKeyId string + fSecretAccessKey string +) + +func init() { + argsPrefix := "CERTIMATE_UPLOADER_CTCCCLOUDCDN_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "") + flag.StringVar(&fSecretAccessKey, argsPrefix+"SECRETACCESSKEY", "", "") +} + +/* +Shell command to run this test: + + go test -v ./ctcccloud_cdn_test.go -args \ + --CERTIMATE_UPLOADER_CTCCCLOUDCDN_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_UPLOADER_CTCCCLOUDCDN_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_CTCCCLOUDCDN_ACCESSKEYID="your-access-key-id" \ + --CERTIMATE_DEPLOYER_CTCCCLOUDCDN_SECRETACCESSKEY="your-secret-access-key" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId), + fmt.Sprintf("SECRETACCESSKEY: %v", fSecretAccessKey), + }, "\n")) + + uploader, err := provider.NewUploader(&provider.UploaderConfig{ + AccessKeyId: fAccessKeyId, + SecretAccessKey: fSecretAccessKey, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := uploader.Upload(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + sres, _ := json.Marshal(res) + t.Logf("ok: %s", string(sres)) + }) +} diff --git a/internal/pkg/core/uploader/providers/wangsu-certificate/wangsu_certificate.go b/internal/pkg/core/uploader/providers/wangsu-certificate/wangsu_certificate.go index 6a12ceda..2a20f4e8 100644 --- a/internal/pkg/core/uploader/providers/wangsu-certificate/wangsu_certificate.go +++ b/internal/pkg/core/uploader/providers/wangsu-certificate/wangsu_certificate.go @@ -1,4 +1,4 @@ -package jdcloudssl +package wangsucertificate import ( "context" @@ -9,9 +9,8 @@ import ( "strings" "time" - wangsusdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/wangsu/certificate" - "github.com/usual2970/certimate/internal/pkg/core/uploader" + wangsusdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/wangsu/certificate" certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" typeutil "github.com/usual2970/certimate/internal/pkg/utils/type" ) diff --git a/internal/pkg/core/uploader/providers/wangsu-certificate/wangsu_certificate_test.go b/internal/pkg/core/uploader/providers/wangsu-certificate/wangsu_certificate_test.go index bdec8cfe..818fc7d0 100644 --- a/internal/pkg/core/uploader/providers/wangsu-certificate/wangsu_certificate_test.go +++ b/internal/pkg/core/uploader/providers/wangsu-certificate/wangsu_certificate_test.go @@ -1,4 +1,4 @@ -package jdcloudssl_test +package wangsucertificate_test import ( "context" @@ -20,7 +20,7 @@ var ( ) func init() { - argsPrefix := "CERTIMATE_UPLOADER_JDCLOUDSSL_" + argsPrefix := "CERTIMATE_UPLOADER_WANGSUCERTIFICATE_" flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") diff --git a/internal/pkg/sdk3rd/ctyun/cdn/api_create_cert.go b/internal/pkg/sdk3rd/ctyun/cdn/api_create_cert.go new file mode 100644 index 00000000..7a2290ab --- /dev/null +++ b/internal/pkg/sdk3rd/ctyun/cdn/api_create_cert.go @@ -0,0 +1,41 @@ +package cdn + +import ( + "context" + "net/http" +) + +type CreateCertRequest struct { + Name *string `json:"name,omitempty"` + Certs *string `json:"certs,omitempty"` + Key *string `json:"key,omitempty"` +} + +type CreateCertResponse struct { + baseResult + + ReturnObj *struct { + Id int32 `json:"id"` + } `json:"returnObj,omitempty"` +} + +func (c *Client) CreateCert(req *CreateCertRequest) (*CreateCertResponse, error) { + return c.CreateCertWithContext(context.Background(), req) +} + +func (c *Client) CreateCertWithContext(ctx context.Context, req *CreateCertRequest) (*CreateCertResponse, error) { + httpreq, err := c.newRequest(http.MethodPost, "/v1/cert/creat-cert") + if err != nil { + return nil, err + } else { + httpreq.SetBody(req) + httpreq.SetContext(ctx) + } + + result := &CreateCertResponse{} + if _, err := c.doRequestWithResult(httpreq, result); err != nil { + return result, err + } + + return result, nil +} diff --git a/internal/pkg/sdk3rd/ctyun/cdn/api_query_cert_detail.go b/internal/pkg/sdk3rd/ctyun/cdn/api_query_cert_detail.go new file mode 100644 index 00000000..02dfd5eb --- /dev/null +++ b/internal/pkg/sdk3rd/ctyun/cdn/api_query_cert_detail.go @@ -0,0 +1,51 @@ +package cdn + +import ( + "context" + "net/http" + "strconv" +) + +type QueryCertDetailRequest struct { + Id *int32 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + UsageMode *int32 `json:"usage_mode,omitempty"` +} + +type QueryCertDetailResponse struct { + baseResult + + ReturnObj *struct { + Result *CertDetail `json:"result,omitempty"` + } `json:"returnObj,omitempty"` +} + +func (c *Client) QueryCertDetail(req *QueryCertDetailRequest) (*QueryCertDetailResponse, error) { + return c.QueryCertDetailWithContext(context.Background(), req) +} + +func (c *Client) QueryCertDetailWithContext(ctx context.Context, req *QueryCertDetailRequest) (*QueryCertDetailResponse, error) { + httpreq, err := c.newRequest(http.MethodGet, "/v1/cert/query-cert-detail") + if err != nil { + return nil, err + } else { + if req.Id != nil { + httpreq.SetQueryParam("id", strconv.Itoa(int(*req.Id))) + } + if req.Name != nil { + httpreq.SetQueryParam("name", *req.Name) + } + if req.UsageMode != nil { + httpreq.SetQueryParam("usage_mode", strconv.Itoa(int(*req.UsageMode))) + } + + httpreq.SetContext(ctx) + } + + result := &QueryCertDetailResponse{} + if _, err := c.doRequestWithResult(httpreq, result); err != nil { + return result, err + } + + return result, nil +} diff --git a/internal/pkg/sdk3rd/ctyun/cdn/api_query_cert_list.go b/internal/pkg/sdk3rd/ctyun/cdn/api_query_cert_list.go new file mode 100644 index 00000000..0f134594 --- /dev/null +++ b/internal/pkg/sdk3rd/ctyun/cdn/api_query_cert_list.go @@ -0,0 +1,55 @@ +package cdn + +import ( + "context" + "net/http" + "strconv" +) + +type QueryCertListRequest struct { + Page *int32 `json:"page,omitempty"` + PerPage *int32 `json:"per_page,omitempty"` + UsageMode *int32 `json:"usage_mode,omitempty"` +} + +type QueryCertListResponse struct { + baseResult + + ReturnObj *struct { + Results []*CertRecord `json:"result,omitempty"` + Page int32 `json:"page,omitempty"` + PerPage int32 `json:"per_page,omitempty"` + TotalPage int32 `json:"total_page,omitempty"` + TotalRecords int32 `json:"total_records,omitempty"` + } `json:"returnObj,omitempty"` +} + +func (c *Client) QueryCertList(req *QueryCertListRequest) (*QueryCertListResponse, error) { + return c.QueryCertListWithContext(context.Background(), req) +} + +func (c *Client) QueryCertListWithContext(ctx context.Context, req *QueryCertListRequest) (*QueryCertListResponse, error) { + httpreq, err := c.newRequest(http.MethodGet, "/v1/cert/query-cert-list") + if err != nil { + return nil, err + } else { + if req.Page != nil { + httpreq.SetQueryParam("page", strconv.Itoa(int(*req.Page))) + } + if req.PerPage != nil { + httpreq.SetQueryParam("per_page", strconv.Itoa(int(*req.PerPage))) + } + if req.UsageMode != nil { + httpreq.SetQueryParam("usage_mode", strconv.Itoa(int(*req.UsageMode))) + } + + httpreq.SetContext(ctx) + } + + result := &QueryCertListResponse{} + if _, err := c.doRequestWithResult(httpreq, result); err != nil { + return result, err + } + + return result, nil +} diff --git a/internal/pkg/sdk3rd/ctyun/cdn/api_query_domain_detail.go b/internal/pkg/sdk3rd/ctyun/cdn/api_query_domain_detail.go new file mode 100644 index 00000000..9a29b4f2 --- /dev/null +++ b/internal/pkg/sdk3rd/ctyun/cdn/api_query_domain_detail.go @@ -0,0 +1,64 @@ +package cdn + +import ( + "context" + "net/http" +) + +type QueryDomainDetailRequest struct { + Domain *string `json:"domain,omitempty"` + ProductCode *string `json:"product_code,omitempty"` + FunctionNames *string `json:"function_names,omitempty"` +} + +type QueryDomainDetailResponse struct { + baseResult + + ReturnObj *struct { + Domain string `json:"domain"` + ProductCode string `json:"product_code"` + Status int32 `json:"status"` + AreaScope int32 `json:"area_scope"` + Cname string `json:"cname"` + HttpsStatus string `json:"https_status"` + HttpsBasic *struct { + HttpsForce string `json:"https_force"` + HttpForce string `json:"http_force"` + ForceStatus string `json:"force_status"` + OriginProtocol string `json:"origin_protocol"` + } `json:"https_basic,omitempty"` + CertName string `json:"cert_name"` + Ssl string `json:"ssl"` + SslStapling string `json:"ssl_stapling"` + } `json:"returnObj,omitempty"` +} + +func (c *Client) QueryDomainDetail(req *QueryDomainDetailRequest) (*QueryDomainDetailResponse, error) { + return c.QueryDomainDetailWithContext(context.Background(), req) +} + +func (c *Client) QueryDomainDetailWithContext(ctx context.Context, req *QueryDomainDetailRequest) (*QueryDomainDetailResponse, error) { + httpreq, err := c.newRequest(http.MethodGet, "/v1/domain/query-domain-detail") + if err != nil { + return nil, err + } else { + if req.Domain != nil { + httpreq.SetQueryParam("domain", *req.Domain) + } + if req.ProductCode != nil { + httpreq.SetQueryParam("product_code", *req.ProductCode) + } + if req.FunctionNames != nil { + httpreq.SetQueryParam("function_names", *req.FunctionNames) + } + + httpreq.SetContext(ctx) + } + + result := &QueryDomainDetailResponse{} + if _, err := c.doRequestWithResult(httpreq, result); err != nil { + return result, err + } + + return result, nil +} diff --git a/internal/pkg/sdk3rd/ctyun/cdn/api_update_domain.go b/internal/pkg/sdk3rd/ctyun/cdn/api_update_domain.go new file mode 100644 index 00000000..65dd918c --- /dev/null +++ b/internal/pkg/sdk3rd/ctyun/cdn/api_update_domain.go @@ -0,0 +1,37 @@ +package cdn + +import ( + "context" + "net/http" +) + +type UpdateDomainRequest struct { + Domain *string `json:"domain,omitempty"` + HttpsStatus *string `json:"https_status,omitempty"` + CertName *string `json:"cert_name,omitempty"` +} + +type UpdateDomainResponse struct { + baseResult +} + +func (c *Client) UpdateDomain(req *UpdateDomainRequest) (*UpdateDomainResponse, error) { + return c.UpdateDomainWithContext(context.Background(), req) +} + +func (c *Client) UpdateDomainWithContext(ctx context.Context, req *UpdateDomainRequest) (*UpdateDomainResponse, error) { + httpreq, err := c.newRequest(http.MethodPost, "/v1/domain/update-domain") + if err != nil { + return nil, err + } else { + httpreq.SetBody(req) + httpreq.SetContext(ctx) + } + + result := &UpdateDomainResponse{} + if _, err := c.doRequestWithResult(httpreq, result); err != nil { + return result, err + } + + return result, nil +} diff --git a/internal/pkg/sdk3rd/ctyun/cdn/client.go b/internal/pkg/sdk3rd/ctyun/cdn/client.go new file mode 100644 index 00000000..7a7896a6 --- /dev/null +++ b/internal/pkg/sdk3rd/ctyun/cdn/client.go @@ -0,0 +1,40 @@ +package cdn + +import ( + "time" + + "github.com/go-resty/resty/v2" + "github.com/usual2970/certimate/internal/pkg/sdk3rd/ctyun/openapi" +) + +const endpoint = "https://ctcdn-global.ctapi.ctyun.cn" + +type Client struct { + client *openapi.Client +} + +func NewClient(accessKeyId, secretAccessKey string) (*Client, error) { + client, err := openapi.NewClient(endpoint, accessKeyId, secretAccessKey) + if err != nil { + return nil, err + } + + return &Client{client: client}, nil +} + +func (c *Client) SetTimeout(timeout time.Duration) *Client { + c.client.SetTimeout(timeout) + return c +} + +func (c *Client) newRequest(method string, path string) (*resty.Request, error) { + return c.client.NewRequest(method, path) +} + +func (c *Client) doRequest(request *resty.Request) (*resty.Response, error) { + return c.client.DoRequest(request) +} + +func (c *Client) doRequestWithResult(request *resty.Request, result any) (*resty.Response, error) { + return c.client.DoRequestWithResult(request, result) +} diff --git a/internal/pkg/sdk3rd/ctyun/cdn/types.go b/internal/pkg/sdk3rd/ctyun/cdn/types.go new file mode 100644 index 00000000..c6366c56 --- /dev/null +++ b/internal/pkg/sdk3rd/ctyun/cdn/types.go @@ -0,0 +1,30 @@ +package cdn + +import "encoding/json" + +type baseResult struct { + StatusCode json.RawMessage `json:"statusCode,omitempty"` + Message *string `json:"message,omitempty"` + Error *string `json:"error,omitempty"` + ErrorMessage *string `json:"errorMessage,omitempty"` + RequestId *string `json:"requestId,omitempty"` +} + +type CertRecord struct { + Id int32 `json:"id"` + Name string `json:"name"` + CN string `json:"cn"` + SANs []string `json:"sans"` + UsageMode int32 `json:"usage_mode"` + State int32 `json:"state"` + ExpiresTime int64 `json:"expires"` + IssueTime int64 `json:"issue"` + Issuer string `json:"issuer"` + CreatedTime int64 `json:"created"` +} + +type CertDetail struct { + CertRecord + Certs string `json:"certs"` + Key string `json:"key"` +} diff --git a/internal/pkg/sdk3rd/ctyun/dns/api_add_record.go b/internal/pkg/sdk3rd/ctyun/dns/api_add_record.go index 8bcdcb21..86227e5f 100644 --- a/internal/pkg/sdk3rd/ctyun/dns/api_add_record.go +++ b/internal/pkg/sdk3rd/ctyun/dns/api_add_record.go @@ -29,16 +29,16 @@ func (c *Client) AddRecord(req *AddRecordRequest) (*AddRecordResponse, error) { } func (c *Client) AddRecordWithContext(ctx context.Context, req *AddRecordRequest) (*AddRecordResponse, error) { - request, err := c.newRequest(http.MethodPost, "/v2/addRecord") + httpreq, err := c.newRequest(http.MethodPost, "/v2/addRecord") if err != nil { return nil, err } else { - request.SetContext(ctx) - request.SetBody(req) + httpreq.SetBody(req) + httpreq.SetContext(ctx) } result := &AddRecordResponse{} - if _, err := c.doRequestWithResult(request, result); err != nil { + if _, err := c.doRequestWithResult(httpreq, result); err != nil { return result, err } diff --git a/internal/pkg/sdk3rd/ctyun/dns/api_delete_record.go b/internal/pkg/sdk3rd/ctyun/dns/api_delete_record.go index 679e6fcf..ad35a349 100644 --- a/internal/pkg/sdk3rd/ctyun/dns/api_delete_record.go +++ b/internal/pkg/sdk3rd/ctyun/dns/api_delete_record.go @@ -18,16 +18,16 @@ func (c *Client) DeleteRecord(req *DeleteRecordRequest) (*DeleteRecordResponse, } func (c *Client) DeleteRecordWithContext(ctx context.Context, req *DeleteRecordRequest) (*DeleteRecordResponse, error) { - request, err := c.newRequest(http.MethodPost, "/v2/deleteRecord") + httpreq, err := c.newRequest(http.MethodPost, "/v2/deleteRecord") if err != nil { return nil, err } else { - request.SetContext(ctx) - request.SetBody(req) + httpreq.SetBody(req) + httpreq.SetContext(ctx) } result := &DeleteRecordResponse{} - if _, err := c.doRequestWithResult(request, result); err != nil { + if _, err := c.doRequestWithResult(httpreq, result); err != nil { return result, err } diff --git a/internal/pkg/sdk3rd/ctyun/dns/api_query_record_list.go b/internal/pkg/sdk3rd/ctyun/dns/api_query_record_list.go index 4d7f1617..d94afcde 100644 --- a/internal/pkg/sdk3rd/ctyun/dns/api_query_record_list.go +++ b/internal/pkg/sdk3rd/ctyun/dns/api_query_record_list.go @@ -18,16 +18,7 @@ type QueryRecordListResponse struct { baseResult ReturnObj *struct { - Records []*struct { - RecordId int32 `json:"recordId"` - Host string `json:"host"` - Type string `json:"type"` - LineCode string `json:"lineCode"` - Value string `json:"value"` - TTL int32 `json:"ttl"` - State int32 `json:"state"` - Remark string `json:"remark"` - } `json:"records,omitempty"` + Records []*DnsRecord `json:"records,omitempty"` } `json:"returnObj,omitempty"` } @@ -36,16 +27,16 @@ func (c *Client) QueryRecordList(req *QueryRecordListRequest) (*QueryRecordListR } func (c *Client) QueryRecordListWithContext(ctx context.Context, req *QueryRecordListRequest) (*QueryRecordListResponse, error) { - request, err := c.newRequest(http.MethodGet, "/v2/queryRecordList") + httpreq, err := c.newRequest(http.MethodGet, "/v2/queryRecordList") if err != nil { return nil, err } else { - request.SetContext(ctx) - request.SetBody(req) + httpreq.SetBody(req) + httpreq.SetContext(ctx) } result := &QueryRecordListResponse{} - if _, err := c.doRequestWithResult(request, result); err != nil { + if _, err := c.doRequestWithResult(httpreq, result); err != nil { return result, err } diff --git a/internal/pkg/sdk3rd/ctyun/dns/api_update_record.go b/internal/pkg/sdk3rd/ctyun/dns/api_update_record.go index ca827ce3..e6dbdc7e 100644 --- a/internal/pkg/sdk3rd/ctyun/dns/api_update_record.go +++ b/internal/pkg/sdk3rd/ctyun/dns/api_update_record.go @@ -30,16 +30,16 @@ func (c *Client) UpdateRecord(req *UpdateRecordRequest) (*UpdateRecordResponse, } func (c *Client) UpdateRecordWithContext(ctx context.Context, req *UpdateRecordRequest) (*UpdateRecordResponse, error) { - request, err := c.newRequest(http.MethodPost, "/v2/updateRecord") + httpreq, err := c.newRequest(http.MethodPost, "/v2/updateRecord") if err != nil { return nil, err } else { - request.SetContext(ctx) - request.SetBody(req) + httpreq.SetBody(req) + httpreq.SetContext(ctx) } result := &UpdateRecordResponse{} - if _, err := c.doRequestWithResult(request, result); err != nil { + if _, err := c.doRequestWithResult(httpreq, result); err != nil { return result, err } diff --git a/internal/pkg/sdk3rd/ctyun/dns/types.go b/internal/pkg/sdk3rd/ctyun/dns/types.go index 8fe2eabd..bcd21d76 100644 --- a/internal/pkg/sdk3rd/ctyun/dns/types.go +++ b/internal/pkg/sdk3rd/ctyun/dns/types.go @@ -9,3 +9,14 @@ type baseResult struct { ErrorMessage *string `json:"errorMessage,omitempty"` RequestId *string `json:"requestId,omitempty"` } + +type DnsRecord struct { + RecordId int32 `json:"recordId"` + Host string `json:"host"` + Type string `json:"type"` + LineCode string `json:"lineCode"` + Value string `json:"value"` + TTL int32 `json:"ttl"` + State int32 `json:"state"` + Remark string `json:"remark"` +} diff --git a/internal/pkg/sdk3rd/ctyun/openapi/client.go b/internal/pkg/sdk3rd/ctyun/openapi/client.go index 6960c83c..ad790dc5 100644 --- a/internal/pkg/sdk3rd/ctyun/openapi/client.go +++ b/internal/pkg/sdk3rd/ctyun/openapi/client.go @@ -22,16 +22,16 @@ type Client struct { func NewClient(endpoint, accessKeyId, secretAccessKey string) (*Client, error) { if endpoint == "" { - return nil, fmt.Errorf("sdk error: unset endpoint") + return nil, fmt.Errorf("sdkerr: unset endpoint") } if _, err := url.Parse(endpoint); err != nil { - return nil, fmt.Errorf("sdk error: invalid endpoint: %w", err) + return nil, fmt.Errorf("sdkerr: invalid endpoint: %w", err) } if accessKeyId == "" { - return nil, fmt.Errorf("sdk error: unset accessKey") + return nil, fmt.Errorf("sdkerr: unset accessKey") } if secretAccessKey == "" { - return nil, fmt.Errorf("sdk error: unset secretKey") + return nil, fmt.Errorf("sdkerr: unset secretKey") } client := resty.New(). @@ -83,7 +83,7 @@ func NewClient(endpoint, accessKeyId, secretAccessKey string) (*Client, error) { hasher.Write([]byte(accessKeyId)) kak := hasher.Sum(nil) - // 生成 kdata + // 生成 kdate hasher = hmac.New(sha256.New, kak) hasher.Write([]byte(now.Format("20060102"))) kdate := hasher.Sum(nil) @@ -114,10 +114,10 @@ func (c *Client) SetTimeout(timeout time.Duration) *Client { func (c *Client) NewRequest(method string, path string) (*resty.Request, error) { if method == "" { - return nil, fmt.Errorf("sdk error: unset method") + return nil, fmt.Errorf("sdkerr: unset method") } if path == "" { - return nil, fmt.Errorf("sdk error: unset path") + return nil, fmt.Errorf("sdkerr: unset path") } req := c.client.R() @@ -128,7 +128,7 @@ func (c *Client) NewRequest(method string, path string) (*resty.Request, error) func (c *Client) DoRequest(request *resty.Request) (*resty.Response, error) { if request == nil { - return nil, fmt.Errorf("sdk error: nil request") + return nil, fmt.Errorf("sdkerr: nil request") } // WARN: @@ -136,9 +136,9 @@ func (c *Client) DoRequest(request *resty.Request) (*resty.Response, error) { resp, err := request.Send() if err != nil { - return resp, fmt.Errorf("sdk error: failed to send request: %w", err) + return resp, fmt.Errorf("sdkerr: failed to send request: %w", err) } else if resp.IsError() { - return resp, fmt.Errorf("sdk error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String()) + return resp, fmt.Errorf("sdkerr: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String()) } return resp, nil @@ -146,7 +146,7 @@ func (c *Client) DoRequest(request *resty.Request) (*resty.Response, error) { func (c *Client) DoRequestWithResult(request *resty.Request, result any) (*resty.Response, error) { if request == nil { - return nil, fmt.Errorf("sdk error: nil request") + return nil, fmt.Errorf("sdkerr: nil request") } response, err := c.DoRequest(request) @@ -159,7 +159,7 @@ func (c *Client) DoRequestWithResult(request *resty.Request, result any) (*resty if len(response.Body()) != 0 { if err := json.Unmarshal(response.Body(), &result); err != nil { - return response, fmt.Errorf("sdk error: failed to unmarshal response: %w", err) + return response, fmt.Errorf("sdkerr: failed to unmarshal response: %w", err) } } diff --git a/internal/pkg/utils/slice/slice.go b/internal/pkg/utils/slice/iter.go similarity index 100% rename from internal/pkg/utils/slice/slice.go rename to internal/pkg/utils/slice/iter.go diff --git a/internal/pkg/utils/type/assert.go b/internal/pkg/utils/type/assert.go index 509a77cc..e1413041 100644 --- a/internal/pkg/utils/type/assert.go +++ b/internal/pkg/utils/type/assert.go @@ -1,6 +1,8 @@ package typeutil -import "reflect" +import ( + "reflect" +) // 判断对象是否为 nil。 // 与直接使用 `obj == nil` 不同,该函数会正确判断接口类型对象的真实值是否为空。 diff --git a/internal/pkg/utils/type/cast.go b/internal/pkg/utils/type/cast.go index 77eb9dad..8c34dc0f 100644 --- a/internal/pkg/utils/type/cast.go +++ b/internal/pkg/utils/type/cast.go @@ -1,6 +1,8 @@ package typeutil -import "reflect" +import ( + "reflect" +) // 将对象转换为指针。 // diff --git a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx index 59e5146e..ba8f04b9 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx @@ -48,6 +48,7 @@ import DeployNodeConfigFormBaotaWAFSiteConfig from "./DeployNodeConfigFormBaotaW import DeployNodeConfigFormBunnyCDNConfig from "./DeployNodeConfigFormBunnyCDNConfig.tsx"; import DeployNodeConfigFormBytePlusCDNConfig from "./DeployNodeConfigFormBytePlusCDNConfig"; import DeployNodeConfigFormCdnflyConfig from "./DeployNodeConfigFormCdnflyConfig"; +import DeployNodeConfigFormCTCCCloudCDNConfig from "./DeployNodeConfigFormCTCCCloudCDNConfig"; import DeployNodeConfigFormDogeCloudCDNConfig from "./DeployNodeConfigFormDogeCloudCDNConfig"; import DeployNodeConfigFormEdgioApplicationsConfig from "./DeployNodeConfigFormEdgioApplicationsConfig"; import DeployNodeConfigFormFlexCDNConfig from "./DeployNodeConfigFormFlexCDNConfig"; @@ -265,6 +266,8 @@ const DeployNodeConfigForm = forwardRef; case DEPLOYMENT_PROVIDERS.CDNFLY: return ; + case DEPLOYMENT_PROVIDERS.CTCCCLOUD_CDN: + return ; case DEPLOYMENT_PROVIDERS.DOGECLOUD_CDN: return ; case DEPLOYMENT_PROVIDERS.EDGIO_APPLICATIONS: diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormCTCCCloudCDNConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormCTCCCloudCDNConfig.tsx new file mode 100644 index 00000000..b7f564e5 --- /dev/null +++ b/ui/src/components/workflow/node/DeployNodeConfigFormCTCCCloudCDNConfig.tsx @@ -0,0 +1,65 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { validDomainName } from "@/utils/validators"; + +type DeployNodeConfigFormCTCCCloudCDNConfigFieldValues = Nullish<{ + domain: string; +}>; + +export type DeployNodeConfigFormCTCCCloudCDNConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: DeployNodeConfigFormCTCCCloudCDNConfigFieldValues; + onValuesChange?: (values: DeployNodeConfigFormCTCCCloudCDNConfigFieldValues) => void; +}; + +const initFormModel = (): DeployNodeConfigFormCTCCCloudCDNConfigFieldValues => { + return {}; +}; + +const DeployNodeConfigFormCTCCCloudCDNConfig = ({ + form: formInst, + formName, + disabled, + initialValues, + onValuesChange, +}: DeployNodeConfigFormCTCCCloudCDNConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + domain: z + .string({ message: t("workflow_node.deploy.form.ctcccloud_cdn_domain.placeholder") }) + .refine((v) => validDomainName(v, { allowWildcard: true }), t("common.errmsg.domain_invalid")), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + +
+ ); +}; + +export default DeployNodeConfigFormCTCCCloudCDNConfig; diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts index 212ded80..29676de9 100644 --- a/ui/src/domain/provider.ts +++ b/ui/src/domain/provider.ts @@ -124,6 +124,7 @@ export const accessProvidersMap: Maphttps://cdn-console.ctyun.cn", "workflow_node.deploy.form.dogecloud_cdn_domain.label": "Doge Cloud CDN domain", "workflow_node.deploy.form.dogecloud_cdn_domain.placeholder": "Please enter Doge Cloud CDN domain name", - "workflow_node.deploy.form.dogecloud_cdn_domain.tooltip": "For more information, see https://console.dogecloud.com/", + "workflow_node.deploy.form.dogecloud_cdn_domain.tooltip": "For more information, see https://console.dogecloud.com", "workflow_node.deploy.form.edgio_applications_environment_id.label": "Edgio Applications environment ID", "workflow_node.deploy.form.edgio_applications_environment_id.placeholder": "Please enter Edgio Applications environment ID", "workflow_node.deploy.form.edgio_applications_environment_id.tooltip": "For more information, see https://edgio.app/", @@ -477,13 +480,13 @@ "workflow_node.deploy.form.jdcloud_alb_snidomain.tooltip": "For more information, see https://cns-console.jdcloud.com/host/loadBalance/list", "workflow_node.deploy.form.jdcloud_cdn_domain.label": "JD Cloud CDN domain", "workflow_node.deploy.form.jdcloud_cdn_domain.placeholder": "Please enter JD Cloud CDN domain name", - "workflow_node.deploy.form.jdcloud_cdn_domain.tooltip": "For more information, see https://cdn-console.jdcloud.com/", + "workflow_node.deploy.form.jdcloud_cdn_domain.tooltip": "For more information, see https://cdn-console.jdcloud.com", "workflow_node.deploy.form.jdcloud_live_domain.label": "JD Cloud Live Video play domain", "workflow_node.deploy.form.jdcloud_live_domain.placeholder": "Please enter JD Cloud Live Video play domain name", - "workflow_node.deploy.form.jdcloud_live_domain.tooltip": "For more information, see https://live-console.jdcloud.com/", + "workflow_node.deploy.form.jdcloud_live_domain.tooltip": "For more information, see https://live-console.jdcloud.com", "workflow_node.deploy.form.jdcloud_vod_domain.label": "JD Cloud VOD domain", "workflow_node.deploy.form.jdcloud_vod_domain.placeholder": "Please enter JD Cloud VOD domain name", - "workflow_node.deploy.form.jdcloud_vod_domain.tooltip": "For more information, see https://vod-console.jdcloud.com/", + "workflow_node.deploy.form.jdcloud_vod_domain.tooltip": "For more information, see https://vod-console.jdcloud.com", "workflow_node.deploy.form.k8s_namespace.label": "Kubernetes Namespace", "workflow_node.deploy.form.k8s_namespace.placeholder": "Please enter Kubernetes Namespace", "workflow_node.deploy.form.k8s_namespace.tooltip": "For more information, see https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/", diff --git a/ui/src/i18n/locales/zh/nls.provider.json b/ui/src/i18n/locales/zh/nls.provider.json index f021a586..2134e402 100644 --- a/ui/src/i18n/locales/zh/nls.provider.json +++ b/ui/src/i18n/locales/zh/nls.provider.json @@ -60,6 +60,12 @@ "provider.cmcccloud.dns": "移动云 - 云解析 DNS", "provider.constellix": "Constellix", "provider.ctcccloud": "天翼云", + "provider.ctcccloud.accessone": "天翼云 - 边缘安全加速平台 AccessOne", + "provider.ctcccloud.cdn": "天翼云 - 内容分发网络 CDN", + "provider.ctcccloud.cms_upload": "天翼云 - 上传到证书管理服务 CMS", + "provider.ctcccloud.elb": "天翼云 - 弹性负载均衡 ELB", + "provider.ctcccloud.icdn": "天翼云 - 全站加速 ICDN", + "provider.ctcccloud.lvdn": "天翼云 - 视频直播 LVDN", "provider.ctcccloud.smartdns": "天翼云 - 智能 DNS", "provider.cucccloud": "联通云", "provider.desec": "deSEC", diff --git a/ui/src/i18n/locales/zh/nls.workflow.nodes.json b/ui/src/i18n/locales/zh/nls.workflow.nodes.json index 7e216a69..b326b3e7 100644 --- a/ui/src/i18n/locales/zh/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/zh/nls.workflow.nodes.json @@ -225,7 +225,7 @@ "workflow_node.deploy.form.aliyun_fc_service_version.placeholder": "请选择阿里云 FC 服务版本", "workflow_node.deploy.form.aliyun_fc_domain.label": "阿里云 FC 自定义域名", "workflow_node.deploy.form.aliyun_fc_domain.placeholder": "请输入阿里云 FC 自定义域名(支持泛域名)", - "workflow_node.deploy.form.aliyun_fc_domain.tooltip": "这是什么?请参阅 see https://fcnext.console.aliyun.com/", + "workflow_node.deploy.form.aliyun_fc_domain.tooltip": "这是什么?请参阅 see https://fcnext.console.aliyun.com", "workflow_node.deploy.form.aliyun_ga_resource_type.label": "证书部署方式", "workflow_node.deploy.form.aliyun_ga_resource_type.placeholder": "请选择证书部署方式", "workflow_node.deploy.form.aliyun_ga_resource_type.option.accelerator.label": "替换指定全球加速器下的全部 HTTPS 监听的证书", @@ -397,6 +397,9 @@ "workflow_node.deploy.form.cdnfly_certificate_id.label": "Cdnfly 证书 ID", "workflow_node.deploy.form.cdnfly_certificate_id.placeholder": "请输入 Cdnfly 证书 ID", "workflow_node.deploy.form.cdnfly_certificate_id.tooltip": "请登录 Cdnfly 控制台查看。", + "workflow_node.deploy.form.ctcccloud_cdn_domain.label": "天翼云 CDN 加速域名", + "workflow_node.deploy.form.ctcccloud_cdn_domain.placeholder": "请输入天翼云 CDN 加速域名(支持泛域名)", + "workflow_node.deploy.form.ctcccloud_cdn_domain.tooltip": "这是什么?请参阅 https://cdn-console.ctyun.cn", "workflow_node.deploy.form.dogecloud_cdn_domain.label": "多吉云 CDN 加速域名", "workflow_node.deploy.form.dogecloud_cdn_domain.placeholder": "请输入多吉云 CDN 加速域名", "workflow_node.deploy.form.dogecloud_cdn_domain.tooltip": "这是什么?请参阅 https://console.dogecloud.com", @@ -476,13 +479,13 @@ "workflow_node.deploy.form.jdcloud_alb_snidomain.tooltip": "这是什么?请参阅 https://cns-console.jdcloud.com/host/loadBalance/list

不填写时,将替换监听器的默认证书;否则,将替换扩展域名证书。", "workflow_node.deploy.form.jdcloud_cdn_domain.label": "京东云 CDN 加速域名", "workflow_node.deploy.form.jdcloud_cdn_domain.placeholder": "请输入京东云 CDN 加速域名(支持泛域名)", - "workflow_node.deploy.form.jdcloud_cdn_domain.tooltip": "这是什么?请参阅 https://cdn-console.jdcloud.com/", + "workflow_node.deploy.form.jdcloud_cdn_domain.tooltip": "这是什么?请参阅 https://cdn-console.jdcloud.com", "workflow_node.deploy.form.jdcloud_live_domain.label": "京东云视频直播播放域名", "workflow_node.deploy.form.jdcloud_live_domain.placeholder": "请输入京东云视频直播播放域名", "workflow_node.deploy.form.jdcloud_live_domain.tooltip": "这是什么?请参阅 https://live-console.jdcloud.com", "workflow_node.deploy.form.jdcloud_vod_domain.label": "京东云视频点播加速域名", "workflow_node.deploy.form.jdcloud_vod_domain.placeholder": "请输入京东云视频点播加速域名", - "workflow_node.deploy.form.jdcloud_vod_domain.tooltip": "这是什么?请参阅 https://vod-console.jdcloud.com/", + "workflow_node.deploy.form.jdcloud_vod_domain.tooltip": "这是什么?请参阅 https://vod-console.jdcloud.com", "workflow_node.deploy.form.k8s_namespace.label": "Kubernetes 命名空间", "workflow_node.deploy.form.k8s_namespace.placeholder": "请输入 Kubernetes 命名空间", "workflow_node.deploy.form.k8s_namespace.tooltip": "这是什么?请参阅 https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/namespaces/",