diff --git a/internal/deployer/providers.go b/internal/deployer/providers.go
index f905285d..8479cade 100644
--- a/internal/deployer/providers.go
+++ b/internal/deployer/providers.go
@@ -46,6 +46,7 @@ import (
pCTCCCloudAO "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ctcccloud-ao"
pCTCCCloudCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ctcccloud-cdn"
pCTCCCloudCMS "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ctcccloud-cms"
+ pCTCCCloudELB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ctcccloud-elb"
pCTCCCloudICDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ctcccloud-icdn"
pCTCCCloudLVDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ctcccloud-lvdn"
pDogeCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/dogecloud-cdn"
@@ -626,7 +627,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
return deployer, err
}
- case domain.DeploymentProviderTypeCTCCCloudAO, domain.DeploymentProviderTypeCTCCCloudCDN, domain.DeploymentProviderTypeCTCCCloudCMS, domain.DeploymentProviderTypeCTCCCloudICDN, domain.DeploymentProviderTypeCTCCCloudLVDN:
+ case domain.DeploymentProviderTypeCTCCCloudAO, domain.DeploymentProviderTypeCTCCCloudCDN, domain.DeploymentProviderTypeCTCCCloudCMS, domain.DeploymentProviderTypeCTCCCloudELB, domain.DeploymentProviderTypeCTCCCloudICDN, domain.DeploymentProviderTypeCTCCCloudLVDN:
{
access := domain.AccessConfigForCTCCCloud{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
@@ -657,6 +658,17 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
})
return deployer, err
+ case domain.DeploymentProviderTypeCTCCCloudELB:
+ deployer, err := pCTCCCloudELB.NewDeployer(&pCTCCCloudELB.DeployerConfig{
+ AccessKeyId: access.AccessKeyId,
+ SecretAccessKey: access.SecretAccessKey,
+ RegionId: maputil.GetString(options.ProviderServiceConfig, "regionId"),
+ ResourceType: pCTCCCloudELB.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")),
+ LoadbalancerId: maputil.GetString(options.ProviderServiceConfig, "loadbalancerId"),
+ ListenerId: maputil.GetString(options.ProviderServiceConfig, "listenerId"),
+ })
+ return deployer, err
+
case domain.DeploymentProviderTypeCTCCCloudICDN:
deployer, err := pCTCCCloudICDN.NewDeployer(&pCTCCCloudICDN.DeployerConfig{
AccessKeyId: access.AccessKeyId,
diff --git a/internal/domain/provider.go b/internal/domain/provider.go
index 924f58bd..47ba0e72 100644
--- a/internal/domain/provider.go
+++ b/internal/domain/provider.go
@@ -218,7 +218,7 @@ const (
DeploymentProviderTypeCTCCCloudAO = DeploymentProviderType(AccessProviderTypeCTCCCloud + "-ao")
DeploymentProviderTypeCTCCCloudCDN = DeploymentProviderType(AccessProviderTypeCTCCCloud + "-cdn")
DeploymentProviderTypeCTCCCloudCMS = DeploymentProviderType(AccessProviderTypeCTCCCloud + "-cms")
- DeploymentProviderTypeCTCCCloudELB = DeploymentProviderType(AccessProviderTypeCTCCCloud + "-elb") // (预留)
+ DeploymentProviderTypeCTCCCloudELB = DeploymentProviderType(AccessProviderTypeCTCCCloud + "-elb")
DeploymentProviderTypeCTCCCloudICDN = DeploymentProviderType(AccessProviderTypeCTCCCloud + "-icdn")
DeploymentProviderTypeCTCCCloudLVDN = DeploymentProviderType(AccessProviderTypeCTCCCloud + "-ldvn")
DeploymentProviderTypeDogeCloudCDN = DeploymentProviderType(AccessProviderTypeDogeCloud + "-cdn")
diff --git a/internal/pkg/core/deployer/providers/ctcccloud-elb/consts.go b/internal/pkg/core/deployer/providers/ctcccloud-elb/consts.go
new file mode 100644
index 00000000..263e66ed
--- /dev/null
+++ b/internal/pkg/core/deployer/providers/ctcccloud-elb/consts.go
@@ -0,0 +1,10 @@
+package ctcccloudelb
+
+type ResourceType string
+
+const (
+ // 资源类型:部署到指定负载均衡器。
+ RESOURCE_TYPE_LOADBALANCER = ResourceType("loadbalancer")
+ // 资源类型:部署到指定监听器。
+ RESOURCE_TYPE_LISTENER = ResourceType("listener")
+)
diff --git a/internal/pkg/core/deployer/providers/ctcccloud-elb/ctcccloud_elb.go b/internal/pkg/core/deployer/providers/ctcccloud-elb/ctcccloud_elb.go
new file mode 100644
index 00000000..f1dfb3f6
--- /dev/null
+++ b/internal/pkg/core/deployer/providers/ctcccloud-elb/ctcccloud_elb.go
@@ -0,0 +1,199 @@
+package ctcccloudelb
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "log/slog"
+ "strings"
+
+ "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-elb"
+ ctyunelb "github.com/usual2970/certimate/internal/pkg/sdk3rd/ctyun/elb"
+ typeutil "github.com/usual2970/certimate/internal/pkg/utils/type"
+)
+
+type DeployerConfig struct {
+ // 天翼云 AccessKeyId。
+ AccessKeyId string `json:"accessKeyId"`
+ // 天翼云 SecretAccessKey。
+ SecretAccessKey string `json:"secretAccessKey"`
+ // 天翼云资源池 ID。
+ RegionId string `json:"regionId"`
+ // 部署资源类型。
+ ResourceType ResourceType `json:"resourceType"`
+ // 负载均衡实例 ID。
+ // 部署资源类型为 [RESOURCE_TYPE_LOADBALANCER] 时必填。
+ LoadbalancerId string `json:"loadbalancerId,omitempty"`
+ // 负载均衡监听器 ID。
+ // 部署资源类型为 [RESOURCE_TYPE_LISTENER] 时必填。
+ ListenerId string `json:"listenerId,omitempty"`
+}
+
+type DeployerProvider struct {
+ config *DeployerConfig
+ logger *slog.Logger
+ sdkClient *ctyunelb.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,
+ RegionId: config.RegionId,
+ })
+ 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) {
+ // 上传证书到 ELB
+ 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))
+ }
+
+ // 根据部署资源类型决定部署方式
+ switch d.config.ResourceType {
+ case RESOURCE_TYPE_LOADBALANCER:
+ if err := d.deployToLoadbalancer(ctx, upres.CertId); err != nil {
+ return nil, err
+ }
+
+ case RESOURCE_TYPE_LISTENER:
+ if err := d.deployToListener(ctx, upres.CertId); err != nil {
+ return nil, err
+ }
+
+ default:
+ return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType)
+ }
+
+ return &deployer.DeployResult{}, nil
+}
+
+func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId string) error {
+ if d.config.LoadbalancerId == "" {
+ return errors.New("config `loadbalancerId` is required")
+ }
+
+ // 查询监听列表
+ // REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=24&api=5654&data=88&isNormal=1&vid=82
+ listenerIds := make([]string, 0)
+ for {
+ select {
+ case <-ctx.Done():
+ return ctx.Err()
+ default:
+ }
+
+ listListenerReq := &ctyunelb.ListListenerRequest{
+ RegionID: typeutil.ToPtr(d.config.RegionId),
+ LoadBalancerID: typeutil.ToPtr(d.config.LoadbalancerId),
+ }
+ listListenerResp, err := d.sdkClient.ListListener(listListenerReq)
+ d.logger.Debug("sdk request 'elb.ListListener'", slog.Any("request", listListenerReq), slog.Any("response", listListenerResp))
+ if err != nil {
+ return fmt.Errorf("failed to execute sdk request 'elb.ListListener': %w", err)
+ }
+
+ for _, listener := range listListenerResp.ReturnObj {
+ if strings.EqualFold(listener.Protocol, "HTTPS") {
+ listenerIds = append(listenerIds, listener.ID)
+ }
+ }
+
+ break
+ }
+
+ // 遍历更新监听证书
+ if len(listenerIds) == 0 {
+ d.logger.Info("no elb listeners to deploy")
+ } else {
+ d.logger.Info("found https listeners to deploy", slog.Any("listenerIds", listenerIds))
+ var errs []error
+
+ for _, listenerId := range listenerIds {
+ select {
+ case <-ctx.Done():
+ return ctx.Err()
+ default:
+ if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil {
+ errs = append(errs, err)
+ }
+ }
+ }
+
+ if len(errs) > 0 {
+ return errors.Join(errs...)
+ }
+ }
+
+ return nil
+}
+
+func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId string) error {
+ if d.config.ListenerId == "" {
+ return errors.New("config `listenerId` is required")
+ }
+
+ // 更新监听
+ if err := d.updateListenerCertificate(ctx, d.config.ListenerId, cloudCertId); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudListenerId string, cloudCertId string) error {
+ // 更新监听器
+ // REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=24&api=5652&data=88&isNormal=1&vid=82
+ setLoadBalancerHTTPSListenerAttributeReq := &ctyunelb.UpdateListenerRequest{
+ RegionID: typeutil.ToPtr(d.config.RegionId),
+ ListenerID: typeutil.ToPtr(cloudListenerId),
+ CertificateID: typeutil.ToPtr(cloudCertId),
+ }
+ setLoadBalancerHTTPSListenerAttributeResp, err := d.sdkClient.UpdateListener(setLoadBalancerHTTPSListenerAttributeReq)
+ d.logger.Debug("sdk request 'elb.UpdateListener'", slog.Any("request", setLoadBalancerHTTPSListenerAttributeReq), slog.Any("response", setLoadBalancerHTTPSListenerAttributeResp))
+ if err != nil {
+ return fmt.Errorf("failed to execute sdk request 'elb.UpdateListener': %w", err)
+ }
+
+ return nil
+}
+
+func createSdkClient(accessKeyId, secretAccessKey string) (*ctyunelb.Client, error) {
+ return ctyunelb.NewClient(accessKeyId, secretAccessKey)
+}
diff --git a/internal/pkg/core/deployer/providers/ctcccloud-elb/ctcccloud_elb_test.go b/internal/pkg/core/deployer/providers/ctcccloud-elb/ctcccloud_elb_test.go
new file mode 100644
index 00000000..86a23a2f
--- /dev/null
+++ b/internal/pkg/core/deployer/providers/ctcccloud-elb/ctcccloud_elb_test.go
@@ -0,0 +1,118 @@
+package ctcccloudelb_test
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "os"
+ "strings"
+ "testing"
+
+ provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ctcccloud-elb"
+)
+
+var (
+ fInputCertPath string
+ fInputKeyPath string
+ fAccessKeyId string
+ fSecretAccessKey string
+ fRegionId string
+ fLoadbalancerId string
+ fListenerId string
+)
+
+func init() {
+ argsPrefix := "CERTIMATE_DEPLOYER_CTCCCLOUDELB_"
+
+ flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
+ flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
+ flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
+ flag.StringVar(&fSecretAccessKey, argsPrefix+"SECRETACCESSKEY", "", "")
+ flag.StringVar(&fRegionId, argsPrefix+"REGIONID", "", "")
+ flag.StringVar(&fLoadbalancerId, argsPrefix+"LOADBALANCERID", "", "")
+ flag.StringVar(&fListenerId, argsPrefix+"LISTENERID", "", "")
+}
+
+/*
+Shell command to run this test:
+
+ go test -v ./ctcccloud_elb_test.go -args \
+ --CERTIMATE_DEPLOYER_CTCCCLOUDELB_INPUTCERTPATH="/path/to/your-input-cert.pem" \
+ --CERTIMATE_DEPLOYER_CTCCCLOUDELB_INPUTKEYPATH="/path/to/your-input-key.pem" \
+ --CERTIMATE_DEPLOYER_CTCCCLOUDELB_ACCESSKEYID="your-access-key-id" \
+ --CERTIMATE_DEPLOYER_CTCCCLOUDELB_SECRETACCESSKEY="your-secret-access-key" \
+ --CERTIMATE_DEPLOYER_CTCCCLOUDELB_REGIONID="your-region-id" \
+ --CERTIMATE_DEPLOYER_CTCCCLOUDELB_LOADBALANCERID="your-elb-instance-id" \
+ --CERTIMATE_DEPLOYER_CTCCCLOUDELB_LISTENERID="your-elb-listener-id"
+*/
+func TestDeploy(t *testing.T) {
+ flag.Parse()
+
+ t.Run("Deploy_ToLoadbalancer", 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("REGIONID: %v", fRegionId),
+ fmt.Sprintf("LOADBALANCERID: %v", fLoadbalancerId),
+ }, "\n"))
+
+ deployer, err := provider.NewDeployer(&provider.DeployerConfig{
+ AccessKeyId: fAccessKeyId,
+ SecretAccessKey: fSecretAccessKey,
+ RegionId: fRegionId,
+ ResourceType: provider.RESOURCE_TYPE_LOADBALANCER,
+ LoadbalancerId: fLoadbalancerId,
+ })
+ 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)
+ })
+
+ t.Run("Deploy_ToListener", 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("REGIONID: %v", fRegionId),
+ fmt.Sprintf("LISTENERID: %v", fListenerId),
+ }, "\n"))
+
+ deployer, err := provider.NewDeployer(&provider.DeployerConfig{
+ AccessKeyId: fAccessKeyId,
+ SecretAccessKey: fSecretAccessKey,
+ RegionId: fRegionId,
+ ResourceType: provider.RESOURCE_TYPE_LISTENER,
+ ListenerId: fListenerId,
+ })
+ 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-elb/ctcccloud_elb.go b/internal/pkg/core/uploader/providers/ctcccloud-elb/ctcccloud_elb.go
new file mode 100644
index 00000000..f6fa16c9
--- /dev/null
+++ b/internal/pkg/core/uploader/providers/ctcccloud-elb/ctcccloud_elb.go
@@ -0,0 +1,133 @@
+package ctcccloudelb
+
+import (
+ "context"
+ "fmt"
+ "log/slog"
+ "time"
+
+ "github.com/google/uuid"
+
+ "github.com/usual2970/certimate/internal/pkg/core/uploader"
+ ctyunelb "github.com/usual2970/certimate/internal/pkg/sdk3rd/ctyun/elb"
+ 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"`
+ // 天翼云资源池 ID。
+ RegionId string `json:"regionId"`
+}
+
+type UploaderProvider struct {
+ config *UploaderConfig
+ logger *slog.Logger
+ sdkClient *ctyunelb.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=24&api=5692&data=88&isNormal=1&vid=82
+ listCertificateReq := &ctyunelb.ListCertificateRequest{
+ RegionID: typeutil.ToPtr(u.config.RegionId),
+ }
+ listCertificateResp, err := u.sdkClient.ListCertificate(listCertificateReq)
+ u.logger.Debug("sdk request 'elb.ListCertificate'", slog.Any("request", listCertificateReq), slog.Any("response", listCertificateResp))
+ if err != nil {
+ return nil, fmt.Errorf("failed to execute sdk request 'elb.ListCertificate': %w", err)
+ } else {
+ for _, certRecord := range listCertificateResp.ReturnObj {
+ var isSameCert bool
+ if certRecord.Certificate == certPEM {
+ isSameCert = true
+ } else {
+ oldCertX509, err := certutil.ParseCertificateFromPEM(certRecord.Certificate)
+ if err != nil {
+ continue
+ }
+
+ isSameCert = certutil.EqualCertificate(certX509, oldCertX509)
+ }
+
+ // 如果已存在相同证书,直接返回
+ if isSameCert {
+ u.logger.Info("ssl certificate already exists")
+ return &uploader.UploadResult{
+ CertId: certRecord.ID,
+ CertName: certRecord.Name,
+ }, nil
+ }
+ }
+ }
+
+ // 生成新证书名(需符合天翼云命名规则)
+ certName := fmt.Sprintf("certimate-%d", time.Now().UnixMilli())
+
+ // 创建证书
+ // REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=24&api=5685&data=88&isNormal=1&vid=82
+ createCertificateReq := &ctyunelb.CreateCertificateRequest{
+ ClientToken: typeutil.ToPtr(generateClientToken()),
+ RegionID: typeutil.ToPtr(u.config.RegionId),
+ Name: typeutil.ToPtr(certName),
+ Description: typeutil.ToPtr("upload from certimate"),
+ Type: typeutil.ToPtr("Server"),
+ Certificate: typeutil.ToPtr(certPEM),
+ PrivateKey: typeutil.ToPtr(privkeyPEM),
+ }
+ createCertificateResp, err := u.sdkClient.CreateCertificate(createCertificateReq)
+ u.logger.Debug("sdk request 'elb.CreateCertificate'", slog.Any("request", createCertificateReq), slog.Any("response", createCertificateResp))
+ if err != nil {
+ return nil, fmt.Errorf("failed to execute sdk request 'elb.CreateCertificate': %w", err)
+ }
+
+ return &uploader.UploadResult{
+ CertId: createCertificateResp.ReturnObj.ID,
+ CertName: certName,
+ }, nil
+}
+
+func createSdkClient(accessKeyId, secretAccessKey string) (*ctyunelb.Client, error) {
+ return ctyunelb.NewClient(accessKeyId, secretAccessKey)
+}
+
+func generateClientToken() string {
+ return uuid.New().String()
+}
diff --git a/internal/pkg/core/uploader/providers/ctcccloud-elb/ctcccloud_elb_test.go b/internal/pkg/core/uploader/providers/ctcccloud-elb/ctcccloud_elb_test.go
new file mode 100644
index 00000000..a3c1c752
--- /dev/null
+++ b/internal/pkg/core/uploader/providers/ctcccloud-elb/ctcccloud_elb_test.go
@@ -0,0 +1,77 @@
+package ctcccloudelb_test
+
+import (
+ "context"
+ "encoding/json"
+ "flag"
+ "fmt"
+ "os"
+ "strings"
+ "testing"
+
+ provider "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/ctcccloud-elb"
+)
+
+var (
+ fInputCertPath string
+ fInputKeyPath string
+ fAccessKeyId string
+ fSecretAccessKey string
+ fRegionId string
+)
+
+func init() {
+ argsPrefix := "CERTIMATE_UPLOADER_CTCCCLOUDELB_"
+
+ flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
+ flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
+ flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
+ flag.StringVar(&fSecretAccessKey, argsPrefix+"SECRETACCESSKEY", "", "")
+ flag.StringVar(&fRegionId, argsPrefix+"REGIONID", "", "")
+}
+
+/*
+Shell command to run this test:
+
+ go test -v ./ctcccloud_elb_test.go -args \
+ --CERTIMATE_UPLOADER_CTCCCLOUDELB_INPUTCERTPATH="/path/to/your-input-cert.pem" \
+ --CERTIMATE_UPLOADER_CTCCCLOUDELB_INPUTKEYPATH="/path/to/your-input-key.pem" \
+ --CERTIMATE_UPLOADER_CTCCCLOUDELB_ACCESSKEYID="your-access-key-id" \
+ --CERTIMATE_UPLOADER_CTCCCLOUDELB_SECRETACCESSKEY="your-secret-access-key" \
+ --CERTIMATE_UPLOADER_CTCCCLOUDELB_REGIONID="your-region-id"
+*/
+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("REGIONID: %v", fRegionId),
+ }, "\n"))
+
+ uploader, err := provider.NewUploader(&provider.UploaderConfig{
+ AccessKeyId: fAccessKeyId,
+ SecretAccessKey: fSecretAccessKey,
+ RegionId: fRegionId,
+ })
+ 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/sdk3rd/ctyun/elb/api_create_certificate.go b/internal/pkg/sdk3rd/ctyun/elb/api_create_certificate.go
new file mode 100644
index 00000000..af17cdb7
--- /dev/null
+++ b/internal/pkg/sdk3rd/ctyun/elb/api_create_certificate.go
@@ -0,0 +1,45 @@
+package elb
+
+import (
+ "context"
+ "net/http"
+)
+
+type CreateCertificateRequest struct {
+ ClientToken *string `json:"clientToken,omitempty"`
+ RegionID *string `json:"regionID,omitempty"`
+ Name *string `json:"name,omitempty"`
+ Description *string `json:"description,omitempty"`
+ Type *string `json:"type,omitempty"`
+ Certificate *string `json:"certificate,omitempty"`
+ PrivateKey *string `json:"privateKey,omitempty"`
+}
+
+type CreateCertificateResponse struct {
+ baseResult
+
+ ReturnObj *struct {
+ ID string `json:"id"`
+ } `json:"returnObj,omitempty"`
+}
+
+func (c *Client) CreateCertificate(req *CreateCertificateRequest) (*CreateCertificateResponse, error) {
+ return c.CreateCertificateWithContext(context.Background(), req)
+}
+
+func (c *Client) CreateCertificateWithContext(ctx context.Context, req *CreateCertificateRequest) (*CreateCertificateResponse, error) {
+ httpreq, err := c.newRequest(http.MethodPost, "/v4/elb/create-certificate")
+ if err != nil {
+ return nil, err
+ } else {
+ httpreq.SetBody(req)
+ httpreq.SetContext(ctx)
+ }
+
+ result := &CreateCertificateResponse{}
+ if _, err := c.doRequestWithResult(httpreq, result); err != nil {
+ return result, err
+ }
+
+ return result, nil
+}
diff --git a/internal/pkg/sdk3rd/ctyun/elb/api_list_certificate.go b/internal/pkg/sdk3rd/ctyun/elb/api_list_certificate.go
new file mode 100644
index 00000000..7a55adfb
--- /dev/null
+++ b/internal/pkg/sdk3rd/ctyun/elb/api_list_certificate.go
@@ -0,0 +1,56 @@
+package elb
+
+import (
+ "context"
+ "net/http"
+)
+
+type ListCertificateRequest struct {
+ ClientToken *string `json:"clientToken,omitempty"`
+ RegionID *string `json:"regionID,omitempty"`
+ IDs *string `json:"IDs,omitempty"`
+ Name *string `json:"name,omitempty"`
+ Type *string `json:"type,omitempty"`
+}
+
+type ListCertificateResponse struct {
+ baseResult
+
+ ReturnObj []*CertificateRecord `json:"returnObj,omitempty"`
+}
+
+func (c *Client) ListCertificate(req *ListCertificateRequest) (*ListCertificateResponse, error) {
+ return c.ListCertificateWithContext(context.Background(), req)
+}
+
+func (c *Client) ListCertificateWithContext(ctx context.Context, req *ListCertificateRequest) (*ListCertificateResponse, error) {
+ httpreq, err := c.newRequest(http.MethodGet, "/v4/elb/list-certificate")
+ if err != nil {
+ return nil, err
+ } else {
+ if req.ClientToken != nil {
+ httpreq.SetQueryParam("clientToken", *req.ClientToken)
+ }
+ if req.RegionID != nil {
+ httpreq.SetQueryParam("regionID", *req.RegionID)
+ }
+ if req.IDs != nil {
+ httpreq.SetQueryParam("IDs", *req.IDs)
+ }
+ if req.Name != nil {
+ httpreq.SetQueryParam("name", *req.Name)
+ }
+ if req.Type != nil {
+ httpreq.SetQueryParam("type", *req.Type)
+ }
+
+ httpreq.SetContext(ctx)
+ }
+
+ result := &ListCertificateResponse{}
+ if _, err := c.doRequestWithResult(httpreq, result); err != nil {
+ return result, err
+ }
+
+ return result, nil
+}
diff --git a/internal/pkg/sdk3rd/ctyun/elb/api_list_listener.go b/internal/pkg/sdk3rd/ctyun/elb/api_list_listener.go
new file mode 100644
index 00000000..fe37deaa
--- /dev/null
+++ b/internal/pkg/sdk3rd/ctyun/elb/api_list_listener.go
@@ -0,0 +1,64 @@
+package elb
+
+import (
+ "context"
+ "net/http"
+)
+
+type ListListenerRequest struct {
+ ClientToken *string `json:"clientToken,omitempty"`
+ RegionID *string `json:"regionID,omitempty"`
+ ProjectID *string `json:"projectID,omitempty"`
+ IDs *string `json:"IDs,omitempty"`
+ Name *string `json:"name,omitempty"`
+ LoadBalancerID *string `json:"loadBalancerID,omitempty"`
+ AccessControlID *string `json:"accessControlID,omitempty"`
+}
+
+type ListListenerResponse struct {
+ baseResult
+
+ ReturnObj []*ListenerRecord `json:"returnObj,omitempty"`
+}
+
+func (c *Client) ListListener(req *ListListenerRequest) (*ListListenerResponse, error) {
+ return c.ListListenerWithContext(context.Background(), req)
+}
+
+func (c *Client) ListListenerWithContext(ctx context.Context, req *ListListenerRequest) (*ListListenerResponse, error) {
+ httpreq, err := c.newRequest(http.MethodGet, "/v4/elb/list-listener")
+ if err != nil {
+ return nil, err
+ } else {
+ if req.ClientToken != nil {
+ httpreq.SetQueryParam("clientToken", *req.ClientToken)
+ }
+ if req.RegionID != nil {
+ httpreq.SetQueryParam("regionID", *req.RegionID)
+ }
+ if req.ProjectID != nil {
+ httpreq.SetQueryParam("projectID", *req.ProjectID)
+ }
+ if req.IDs != nil {
+ httpreq.SetQueryParam("IDs", *req.IDs)
+ }
+ if req.Name != nil {
+ httpreq.SetQueryParam("name", *req.Name)
+ }
+ if req.LoadBalancerID != nil {
+ httpreq.SetQueryParam("loadBalancerID", *req.LoadBalancerID)
+ }
+ if req.LoadBalancerID != nil {
+ httpreq.SetQueryParam("accessControlID", *req.AccessControlID)
+ }
+
+ httpreq.SetContext(ctx)
+ }
+
+ result := &ListListenerResponse{}
+ if _, err := c.doRequestWithResult(httpreq, result); err != nil {
+ return result, err
+ }
+
+ return result, nil
+}
diff --git a/internal/pkg/sdk3rd/ctyun/elb/api_show_listener.go b/internal/pkg/sdk3rd/ctyun/elb/api_show_listener.go
new file mode 100644
index 00000000..a57a8bea
--- /dev/null
+++ b/internal/pkg/sdk3rd/ctyun/elb/api_show_listener.go
@@ -0,0 +1,48 @@
+package elb
+
+import (
+ "context"
+ "net/http"
+)
+
+type ShowListenerRequest struct {
+ ClientToken *string `json:"clientToken,omitempty"`
+ RegionID *string `json:"regionID,omitempty"`
+ ListenerID *string `json:"listenerID,omitempty"`
+}
+
+type ShowListenerResponse struct {
+ baseResult
+
+ ReturnObj []*ListenerRecord `json:"returnObj,omitempty"`
+}
+
+func (c *Client) ShowListener(req *ShowListenerRequest) (*ShowListenerResponse, error) {
+ return c.ShowListenerWithContext(context.Background(), req)
+}
+
+func (c *Client) ShowListenerWithContext(ctx context.Context, req *ShowListenerRequest) (*ShowListenerResponse, error) {
+ httpreq, err := c.newRequest(http.MethodGet, "/v4/elb/show-listener")
+ if err != nil {
+ return nil, err
+ } else {
+ if req.ClientToken != nil {
+ httpreq.SetQueryParam("clientToken", *req.ClientToken)
+ }
+ if req.RegionID != nil {
+ httpreq.SetQueryParam("regionID", *req.RegionID)
+ }
+ if req.ListenerID != nil {
+ httpreq.SetQueryParam("listenerID", *req.ListenerID)
+ }
+
+ httpreq.SetContext(ctx)
+ }
+
+ result := &ShowListenerResponse{}
+ if _, err := c.doRequestWithResult(httpreq, result); err != nil {
+ return result, err
+ }
+
+ return result, nil
+}
diff --git a/internal/pkg/sdk3rd/ctyun/elb/api_update_listener.go b/internal/pkg/sdk3rd/ctyun/elb/api_update_listener.go
new file mode 100644
index 00000000..845d9100
--- /dev/null
+++ b/internal/pkg/sdk3rd/ctyun/elb/api_update_listener.go
@@ -0,0 +1,44 @@
+package elb
+
+import (
+ "context"
+ "net/http"
+)
+
+type UpdateListenerRequest struct {
+ ClientToken *string `json:"clientToken,omitempty"`
+ RegionID *string `json:"regionID,omitempty"`
+ ListenerID *string `json:"listenerID,omitempty"`
+ Name *string `json:"name,omitempty"`
+ Description *string `json:"description,omitempty"`
+ CertificateID *string `json:"certificateID,omitempty"`
+ CaEnabled *bool `json:"caEnabled,omitempty"`
+ ClientCertificateID *string `json:"clientCertificateID,omitempty"`
+}
+
+type UpdateListenerResponse struct {
+ baseResult
+
+ ReturnObj []*ListenerRecord `json:"returnObj,omitempty"`
+}
+
+func (c *Client) UpdateListener(req *UpdateListenerRequest) (*UpdateListenerResponse, error) {
+ return c.UpdateListenerWithContext(context.Background(), req)
+}
+
+func (c *Client) UpdateListenerWithContext(ctx context.Context, req *UpdateListenerRequest) (*UpdateListenerResponse, error) {
+ httpreq, err := c.newRequest(http.MethodPost, "/v4/elb/update-listener")
+ if err != nil {
+ return nil, err
+ } else {
+ httpreq.SetBody(req)
+ httpreq.SetContext(ctx)
+ }
+
+ result := &UpdateListenerResponse{}
+ if _, err := c.doRequestWithResult(httpreq, result); err != nil {
+ return result, err
+ }
+
+ return result, nil
+}
diff --git a/internal/pkg/sdk3rd/ctyun/elb/client.go b/internal/pkg/sdk3rd/ctyun/elb/client.go
new file mode 100644
index 00000000..a71effa3
--- /dev/null
+++ b/internal/pkg/sdk3rd/ctyun/elb/client.go
@@ -0,0 +1,50 @@
+package elb
+
+import (
+ "fmt"
+ "time"
+
+ "github.com/go-resty/resty/v2"
+ "github.com/usual2970/certimate/internal/pkg/sdk3rd/ctyun/openapi"
+)
+
+const endpoint = "https://ctelb-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 baseResultInterface) (*resty.Response, error) {
+ response, err := c.client.DoRequestWithResult(request, result)
+ if err == nil {
+ statusCode := result.GetStatusCode()
+ errorCode := result.GetError()
+ if (statusCode != "" && statusCode != "200" && statusCode != "800") || (errorCode != "" && errorCode != "SUCCESS") {
+ return response, fmt.Errorf("sdkerr: api error, code='%s', message='%s', errorCode='%s', description='%s'", statusCode, result.GetMessage(), result.GetMessage(), result.GetDescription())
+ }
+ }
+
+ return response, err
+}
diff --git a/internal/pkg/sdk3rd/ctyun/elb/types.go b/internal/pkg/sdk3rd/ctyun/elb/types.go
new file mode 100644
index 00000000..4f2971b8
--- /dev/null
+++ b/internal/pkg/sdk3rd/ctyun/elb/types.go
@@ -0,0 +1,104 @@
+package elb
+
+import (
+ "bytes"
+ "encoding/json"
+ "strconv"
+)
+
+type baseResultInterface interface {
+ GetStatusCode() string
+ GetMessage() string
+ GetError() string
+ GetDescription() string
+}
+
+type baseResult struct {
+ StatusCode json.RawMessage `json:"statusCode,omitempty"`
+ Message *string `json:"message,omitempty"`
+ Error *string `json:"error,omitempty"`
+ Description *string `json:"description,omitempty"`
+ RequestId *string `json:"requestId,omitempty"`
+}
+
+func (r *baseResult) GetStatusCode() string {
+ if r.StatusCode == nil {
+ return ""
+ }
+
+ decoder := json.NewDecoder(bytes.NewReader(r.StatusCode))
+ token, err := decoder.Token()
+ if err != nil {
+ return ""
+ }
+
+ switch t := token.(type) {
+ case string:
+ return t
+ case float64:
+ return strconv.FormatFloat(t, 'f', -1, 64)
+ case json.Number:
+ return t.String()
+ default:
+ return ""
+ }
+}
+
+func (r *baseResult) GetMessage() string {
+ if r.Message == nil {
+ return ""
+ }
+
+ return *r.Message
+}
+
+func (r *baseResult) GetError() string {
+ if r.Error == nil {
+ return ""
+ }
+
+ return *r.Error
+}
+
+func (r *baseResult) GetDescription() string {
+ if r.Description == nil {
+ return ""
+ }
+
+ return *r.Description
+}
+
+var _ baseResultInterface = (*baseResult)(nil)
+
+type CertificateRecord struct {
+ ID string `json:"ID"`
+ RegionID string `json:"regionID"`
+ AzName string `json:"azName"`
+ ProjectID string `json:"projectID"`
+ Name string `json:"name"`
+ Description string `json:"description"`
+ Type string `json:"type"`
+ Certificate string `json:"certificate"`
+ PrivateKey string `json:"privateKey"`
+ Status string `json:"status"`
+ CreatedTime string `json:"createdTime"`
+ UpdatedTime string `json:"updatedTime"`
+}
+
+type ListenerRecord struct {
+ ID string `json:"ID"`
+ RegionID string `json:"regionID"`
+ AzName string `json:"azName"`
+ ProjectID string `json:"projectID"`
+ Name string `json:"name"`
+ Description string `json:"description"`
+ LoadBalancerID string `json:"loadBalancerID"`
+ Protocol string `json:"protocol"`
+ ProtocolPort int32 `json:"protocolPort"`
+ CertificateID string `json:"certificateID,omitempty"`
+ CaEnabled bool `json:"caEnabled"`
+ ClientCertificateID string `json:"clientCertificateID,omitempty"`
+ Status string `json:"status"`
+ CreatedTime string `json:"createdTime"`
+ UpdatedTime string `json:"updatedTime"`
+}
diff --git a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx
index e1bee0c9..15f627a9 100644
--- a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx
+++ b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx
@@ -50,6 +50,7 @@ import DeployNodeConfigFormBytePlusCDNConfig from "./DeployNodeConfigFormBytePlu
import DeployNodeConfigFormCdnflyConfig from "./DeployNodeConfigFormCdnflyConfig";
import DeployNodeConfigFormCTCCCloudAOConfig from "./DeployNodeConfigFormCTCCCloudAOConfig";
import DeployNodeConfigFormCTCCCloudCDNConfig from "./DeployNodeConfigFormCTCCCloudCDNConfig";
+import DeployNodeConfigFormCTCCCloudELBConfig from "./DeployNodeConfigFormCTCCCloudELBConfig";
import DeployNodeConfigFormCTCCCloudICDNConfig from "./DeployNodeConfigFormCTCCCloudICDNConfig";
import DeployNodeConfigFormCTCCCloudLVDNConfig from "./DeployNodeConfigFormCTCCCloudLVDNConfig";
import DeployNodeConfigFormDogeCloudCDNConfig from "./DeployNodeConfigFormDogeCloudCDNConfig";
@@ -273,6 +274,8 @@ const DeployNodeConfigForm = forwardRef;
case DEPLOYMENT_PROVIDERS.CTCCCLOUD_CDN:
return ;
+ case DEPLOYMENT_PROVIDERS.CTCCCLOUD_ELB:
+ return ;
case DEPLOYMENT_PROVIDERS.CTCCCLOUD_ICDN:
return ;
case DEPLOYMENT_PROVIDERS.CTCCCLOUD_LVDN:
diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormCTCCCloudELBConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormCTCCCloudELBConfig.tsx
new file mode 100644
index 00000000..6577c35c
--- /dev/null
+++ b/ui/src/components/workflow/node/DeployNodeConfigFormCTCCCloudELBConfig.tsx
@@ -0,0 +1,121 @@
+import { useTranslation } from "react-i18next";
+import { Form, type FormInstance, Input, Select } from "antd";
+import { createSchemaFieldRule } from "antd-zod";
+import { z } from "zod";
+
+import Show from "@/components/Show";
+
+type DeployNodeConfigFormCTCCCloudELBConfigFieldValues = Nullish<{
+ regionId: string;
+ resourceType: string;
+ loadbalancerId?: string;
+ listenerId?: string;
+}>;
+
+export type DeployNodeConfigFormCTCCCloudELBConfigProps = {
+ form: FormInstance;
+ formName: string;
+ disabled?: boolean;
+ initialValues?: DeployNodeConfigFormCTCCCloudELBConfigFieldValues;
+ onValuesChange?: (values: DeployNodeConfigFormCTCCCloudELBConfigFieldValues) => void;
+};
+
+const RESOURCE_TYPE_LOADBALANCER = "loadbalancer" as const;
+const RESOURCE_TYPE_LISTENER = "listener" as const;
+
+const initFormModel = (): DeployNodeConfigFormCTCCCloudELBConfigFieldValues => {
+ return {
+ resourceType: RESOURCE_TYPE_LISTENER,
+ };
+};
+
+const DeployNodeConfigFormCTCCCloudELBConfig = ({
+ form: formInst,
+ formName,
+ disabled,
+ initialValues,
+ onValuesChange,
+}: DeployNodeConfigFormCTCCCloudELBConfigProps) => {
+ const { t } = useTranslation();
+
+ const formSchema = z.object({
+ resourceType: z.union([z.literal(RESOURCE_TYPE_LOADBALANCER), z.literal(RESOURCE_TYPE_LISTENER)], {
+ message: t("workflow_node.deploy.form.ctcccloud_elb_resource_type.placeholder"),
+ }),
+ regionId: z
+ .string({ message: t("workflow_node.deploy.form.ctcccloud_elb_region_id.placeholder") })
+ .nonempty(t("workflow_node.deploy.form.ctcccloud_elb_region_id.placeholder")),
+ loadbalancerId: z
+ .string()
+ .max(64, t("common.errmsg.string_max", { max: 64 }))
+ .nullish()
+ .refine((v) => fieldResourceType !== RESOURCE_TYPE_LOADBALANCER || !!v?.trim(), t("workflow_node.deploy.form.ctcccloud_elb_loadbalancer_id.placeholder")),
+ listenerId: z
+ .string()
+ .max(64, t("common.errmsg.string_max", { max: 64 }))
+ .nullish()
+ .refine((v) => fieldResourceType !== RESOURCE_TYPE_LISTENER || !!v?.trim(), t("workflow_node.deploy.form.ctcccloud_elb_listener_id.placeholder")),
+ });
+ const formRule = createSchemaFieldRule(formSchema);
+
+ const fieldResourceType = Form.useWatch("resourceType", formInst);
+
+ const handleFormChange = (_: unknown, values: z.infer) => {
+ onValuesChange?.(values);
+ };
+
+ return (
+
+
+
+
+ }
+ >
+
+
+
+
+ }
+ >
+
+
+
+
+
+ }
+ >
+
+
+
+
+ );
+};
+
+export default DeployNodeConfigFormCTCCCloudELBConfig;
diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts
index d54494ad..1100e797 100644
--- a/ui/src/domain/provider.ts
+++ b/ui/src/domain/provider.ts
@@ -416,6 +416,7 @@ export const DEPLOYMENT_PROVIDERS = Object.freeze({
CTCCCLOUD_AO: `${ACCESS_PROVIDERS.CTCCCLOUD}-ao`,
CTCCCLOUD_CDN: `${ACCESS_PROVIDERS.CTCCCLOUD}-cdn`,
CTCCCLOUD_CMS: `${ACCESS_PROVIDERS.CTCCCLOUD}-cms`,
+ CTCCCLOUD_ELB: `${ACCESS_PROVIDERS.CTCCCLOUD}-elb`,
CTCCCLOUD_ICDN: `${ACCESS_PROVIDERS.CTCCCLOUD}-icdn`,
CTCCCLOUD_LVDN: `${ACCESS_PROVIDERS.CTCCCLOUD}-lvdn`,
DOGECLOUD_CDN: `${ACCESS_PROVIDERS.DOGECLOUD}-cdn`,
@@ -578,6 +579,7 @@ export const deploymentProvidersMap: Maphttps://cdn-console.ctyun.cn",
+ "workflow_node.deploy.form.ctcccloud_elb_resource_type.label": "Resource type",
+ "workflow_node.deploy.form.ctcccloud_elb_resource_type.placeholder": "Please select resource type",
+ "workflow_node.deploy.form.ctcccloud_elb_resource_type.option.certificate.label": "ELB certificate",
+ "workflow_node.deploy.form.ctcccloud_elb_resource_type.option.loadbalancer.label": "ELB load balancer",
+ "workflow_node.deploy.form.ctcccloud_elb_resource_type.option.listener.label": "ELB listener",
+ "workflow_node.deploy.form.ctcccloud_elb_region_id.label": "CTCC StateCloud ELB region ID",
+ "workflow_node.deploy.form.ctcccloud_elb_region_id.placeholder": "Please enter CTCC StateCloud ELB region ID",
+ "workflow_node.deploy.form.ctcccloud_elb_region_id.tooltip": "For more information, see https://www.ctyun.cn/document/10026755/10196575",
+ "workflow_node.deploy.form.ctcccloud_elb_loadbalancer_id.label": "CTCC StateCloud ELB load balancer ID",
+ "workflow_node.deploy.form.ctcccloud_elb_loadbalancer_id.placeholder": "Please enter CTCC StateCloud ELB load balancer ID",
+ "workflow_node.deploy.form.ctcccloud_elb_loadbalancer_id.tooltip": "For more information, see https://console.ctyun.cn/network/index/#/elb/elbList",
+ "workflow_node.deploy.form.ctcccloud_elb_listener_id.label": "CTCC StateCloud ELB listener ID",
+ "workflow_node.deploy.form.ctcccloud_elb_listener_id.placeholder": "Please enter CTCC StateCloud ELB listener ID",
+ "workflow_node.deploy.form.ctcccloud_elb_listener_id.tooltip": "For more information, see https://console.ctyun.cn/network/index/#/elb/elbList",
"workflow_node.deploy.form.ctcccloud_icdn_domain.label": "CTCC StateCloud ICDN domain",
"workflow_node.deploy.form.ctcccloud_icdn_domain.placeholder": "Please enter CTCC StateCloud ICDN domain name",
"workflow_node.deploy.form.ctcccloud_icdn_domain.tooltip": "For more information, see https://cdn-console.ctyun.cn",
diff --git a/ui/src/i18n/locales/zh/nls.workflow.nodes.json b/ui/src/i18n/locales/zh/nls.workflow.nodes.json
index f22a23e7..18ad43fe 100644
--- a/ui/src/i18n/locales/zh/nls.workflow.nodes.json
+++ b/ui/src/i18n/locales/zh/nls.workflow.nodes.json
@@ -403,6 +403,19 @@
"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.ctcccloud_elb_resource_type.label": "证书部署方式",
+ "workflow_node.deploy.form.ctcccloud_elb_resource_type.placeholder": "请选择证书部署方式",
+ "workflow_node.deploy.form.ctcccloud_elb_resource_type.option.loadbalancer.label": "替换指定负载均衡器下的全部 HTTPS 监听器的证书",
+ "workflow_node.deploy.form.ctcccloud_elb_resource_type.option.listener.label": "替换指定监听器的证书",
+ "workflow_node.deploy.form.ctcccloud_elb_region_id.label": "天翼云 ELB 资源池 ID",
+ "workflow_node.deploy.form.ctcccloud_elb_region_id.placeholder": "请输入天翼云 ELB 资源池 ID",
+ "workflow_node.deploy.form.ctcccloud_elb_region_id.tooltip": "这是什么?请参阅 https://www.ctyun.cn/document/10026755/10196575",
+ "workflow_node.deploy.form.ctcccloud_elb_loadbalancer_id.label": "天翼云 ELB 负载均衡器 ID",
+ "workflow_node.deploy.form.ctcccloud_elb_loadbalancer_id.placeholder": "请输入天翼云 ELB 负载均衡器 ID",
+ "workflow_node.deploy.form.ctcccloud_elb_loadbalancer_id.tooltip": "这是什么?请参阅 https://console.ctyun.cn/network/index/#/elb/elbList",
+ "workflow_node.deploy.form.ctcccloud_elb_listener_id.label": "天翼云 ELB 监听器 ID",
+ "workflow_node.deploy.form.ctcccloud_elb_listener_id.placeholder": "请输入天翼云 ELB 监听器 ID",
+ "workflow_node.deploy.form.ctcccloud_elb_listener_id.tooltip": "这是什么?请参阅 https://console.ctyun.cn/network/index/#/elb/elbList",
"workflow_node.deploy.form.ctcccloud_icdn_domain.label": "天翼云 ICDN 加速域名",
"workflow_node.deploy.form.ctcccloud_icdn_domain.placeholder": "请输入天翼云 ICDN 加速域名(支持泛域名)",
"workflow_node.deploy.form.ctcccloud_icdn_domain.tooltip": "这是什么?请参阅 https://cdn-console.ctyun.cn",