diff --git a/README.md b/README.md
index f744e1fe..3aa3f158 100644
--- a/README.md
+++ b/README.md
@@ -130,7 +130,7 @@ make local.run
| [百度智能云](https://cloud.baidu.com/) | 可部署到百度智能云 CDN 等服务 |
| [华为云](https://www.huaweicloud.com/) | 可部署到华为云 CDN、ELB、WAF 等服务 |
| [火山引擎](https://www.volcengine.com/) | 可部署到火山引擎 TOS、CDN、DCDN、CLB、ImageX、Live 等服务 |
-| [京东云](https://www.jdcloud.com/) | 可部署到京东云 CDN、视频直播等服务 |
+| [京东云](https://www.jdcloud.com/) | 可部署到京东云 CDN、ALB、视频直播等服务 |
| [七牛云](https://www.qiniu.com/) | 可部署到七牛云 CDN、直播云等服务 |
| [白山云](https://www.baishan.com/) | 可部署到白山云 CDN |
| [多吉云](https://www.dogecloud.com/) | 可部署到多吉云 CDN |
diff --git a/README_EN.md b/README_EN.md
index 05003b60..c9a5bdf4 100644
--- a/README_EN.md
+++ b/README_EN.md
@@ -129,7 +129,7 @@ The following hosting providers are supported:
| [Baidu AI Cloud](https://intl.cloud.baidu.com/) | Supports deployment to Baidu AI CLoud CDN |
| [Huawei Cloud](https://www.huaweicloud.com/) | Supports deployment to Huawei Cloud CDN, ELB, WAF |
| [Volcengine](https://www.volcengine.com/) | Supports deployment to Volcengine TOS, CDN, DCDN, CLB, ImageX, Live |
-| [JD Cloud](https://www.jdcloud.com/) | Supports deployment to JD Cloud CDN, Live Video |
+| [JD Cloud](https://www.jdcloud.com/) | Supports deployment to JD Cloud CDN, ALB, Live Video |
| [Qiniu Cloud](https://www.qiniu.com/) | Supports deployment to Qiniu Cloud CDN, Pili |
| [Baishan Cloud](https://intl.baishancloud.com/) | Supports deployment to Baishan Cloud CDN |
| [Doge Cloud](https://www.dogecloud.com/) | Supports deployment to Doge Cloud CDN |
diff --git a/internal/deployer/providers.go b/internal/deployer/providers.go
index 9210a7e5..24fc32f4 100644
--- a/internal/deployer/providers.go
+++ b/internal/deployer/providers.go
@@ -30,6 +30,7 @@ import (
pHuaweiCloudCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/huaweicloud-cdn"
pHuaweiCloudELB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/huaweicloud-elb"
pHuaweiCloudWAF "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/huaweicloud-waf"
+ pJDCloudALB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/jdcloud-alb"
pJDCloudCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/jdcloud-cdn"
pJDCloudLive "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/jdcloud-live"
pK8sSecret "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/k8s-secret"
@@ -417,7 +418,7 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) {
}
}
- case domain.DeployProviderTypeJDCloudCDN, domain.DeployProviderTypeJDCloudLive:
+ case domain.DeployProviderTypeJDCloudALB, domain.DeployProviderTypeJDCloudCDN, domain.DeployProviderTypeJDCloudLive:
{
access := domain.AccessConfigForJDCloud{}
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
@@ -425,6 +426,17 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) {
}
switch options.Provider {
+ case domain.DeployProviderTypeJDCloudALB:
+ deployer, err := pJDCloudALB.NewDeployer(&pJDCloudALB.DeployerConfig{
+ AccessKeyId: access.AccessKeyId,
+ AccessKeySecret: access.AccessKeySecret,
+ RegionId: maps.GetValueAsString(options.ProviderDeployConfig, "regionId"),
+ ResourceType: pJDCloudALB.ResourceType(maps.GetValueAsString(options.ProviderDeployConfig, "resourceType")),
+ LoadbalancerId: maps.GetValueAsString(options.ProviderDeployConfig, "loadbalancerId"),
+ ListenerId: maps.GetValueAsString(options.ProviderDeployConfig, "listenerId"),
+ })
+ return deployer, err
+
case domain.DeployProviderTypeJDCloudCDN:
deployer, err := pJDCloudCDN.NewDeployer(&pJDCloudCDN.DeployerConfig{
AccessKeyId: access.AccessKeyId,
diff --git a/internal/domain/provider.go b/internal/domain/provider.go
index b39af710..63528231 100644
--- a/internal/domain/provider.go
+++ b/internal/domain/provider.go
@@ -127,6 +127,7 @@ const (
DeployProviderTypeHuaweiCloudCDN = DeployProviderType("huaweicloud-cdn")
DeployProviderTypeHuaweiCloudELB = DeployProviderType("huaweicloud-elb")
DeployProviderTypeHuaweiCloudWAF = DeployProviderType("huaweicloud-waf")
+ DeployProviderTypeJDCloudALB = DeployProviderType("jdcloud-alb")
DeployProviderTypeJDCloudCDN = DeployProviderType("jdcloud-cdn")
DeployProviderTypeJDCloudLive = DeployProviderType("jdcloud-live")
DeployProviderTypeKubernetesSecret = DeployProviderType("k8s-secret")
diff --git a/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb.go b/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb.go
index 27261a63..39600c7b 100644
--- a/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb.go
+++ b/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb.go
@@ -254,7 +254,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL
d.logger.Logt("已更新 ALB 监听配置", updateListenerAttributeResp)
} else {
- // 指定 SNI,需部署到扩展域名(支持泛域名)
+ // 指定 SNI,需部署到扩展域名
// 查询监听证书列表
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-listlistenercertificates
diff --git a/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb.go b/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb.go
index e18dccc4..21a1e471 100644
--- a/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb.go
+++ b/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb.go
@@ -211,8 +211,6 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL
// 修改监听配置
// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-setloadbalancerhttpslistenerattribute
- //
- // 注意修改监听配置要放在修改扩展域名之后
setLoadBalancerHTTPSListenerAttributeReq := &aliyunSlb.SetLoadBalancerHTTPSListenerAttributeRequest{
RegionId: tea.String(d.config.Region),
LoadBalancerId: tea.String(cloudLoadbalancerId),
@@ -226,7 +224,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL
d.logger.Logt("已更新 CLB HTTPS 监听配置", setLoadBalancerHTTPSListenerAttributeResp)
} else {
- // 指定 SNI,需部署到扩展域名(支持泛域名)
+ // 指定 SNI,需部署到扩展域名
// 查询扩展域名
// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-describedomainextensions
diff --git a/internal/pkg/core/deployer/providers/jdcloud-alb/consts.go b/internal/pkg/core/deployer/providers/jdcloud-alb/consts.go
new file mode 100644
index 00000000..13525c20
--- /dev/null
+++ b/internal/pkg/core/deployer/providers/jdcloud-alb/consts.go
@@ -0,0 +1,10 @@
+package jdcloudalb
+
+type ResourceType string
+
+const (
+ // 资源类型:部署到指定负载均衡器。
+ RESOURCE_TYPE_LOADBALANCER = ResourceType("loadbalancer")
+ // 资源类型:部署到指定监听器。
+ RESOURCE_TYPE_LISTENER = ResourceType("listener")
+)
diff --git a/internal/pkg/core/deployer/providers/jdcloud-alb/jdcloud_alb.go b/internal/pkg/core/deployer/providers/jdcloud-alb/jdcloud_alb.go
new file mode 100644
index 00000000..a300bb4d
--- /dev/null
+++ b/internal/pkg/core/deployer/providers/jdcloud-alb/jdcloud_alb.go
@@ -0,0 +1,251 @@
+package jdcloudalb
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "strings"
+
+ jdCore "github.com/jdcloud-api/jdcloud-sdk-go/core"
+ jdCommon "github.com/jdcloud-api/jdcloud-sdk-go/services/common/models"
+ jdLbApi "github.com/jdcloud-api/jdcloud-sdk-go/services/lb/apis"
+ jdLbClient "github.com/jdcloud-api/jdcloud-sdk-go/services/lb/client"
+ jdLbModel "github.com/jdcloud-api/jdcloud-sdk-go/services/lb/models"
+ xerrors "github.com/pkg/errors"
+
+ "github.com/usual2970/certimate/internal/pkg/core/deployer"
+ "github.com/usual2970/certimate/internal/pkg/core/logger"
+ "github.com/usual2970/certimate/internal/pkg/core/uploader"
+ uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/jdcloud-ssl"
+ "github.com/usual2970/certimate/internal/pkg/utils/slices"
+)
+
+type DeployerConfig struct {
+ // 京东云 AccessKeyId。
+ AccessKeyId string `json:"accessKeyId"`
+ // 京东云 AccessKeySecret。
+ AccessKeySecret string `json:"accessKeySecret"`
+ // 京东云地域 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"`
+ // SNI 域名(支持泛域名)。
+ // 部署资源类型为 [RESOURCE_TYPE_LOADBALANCER]、[RESOURCE_TYPE_LISTENER] 时选填。
+ Domain string `json:"domain,omitempty"`
+}
+
+type DeployerProvider struct {
+ config *DeployerConfig
+ logger logger.Logger
+ sdkClient *jdLbClient.LbClient
+ 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.AccessKeySecret)
+ if err != nil {
+ return nil, xerrors.Wrap(err, "failed to create sdk client")
+ }
+
+ uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
+ AccessKeyId: config.AccessKeyId,
+ AccessKeySecret: config.AccessKeySecret,
+ })
+ if err != nil {
+ return nil, xerrors.Wrap(err, "failed to create ssl uploader")
+ }
+
+ return &DeployerProvider{
+ config: config,
+ logger: logger.NewNilLogger(),
+ sdkClient: client,
+ sslUploader: uploader,
+ }, nil
+}
+
+func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
+ d.logger = logger
+ return d
+}
+
+func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
+ // 上传证书到 SSL
+ upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
+ if err != nil {
+ return nil, xerrors.Wrap(err, "failed to upload certificate file")
+ } else {
+ d.logger.Logt("certificate file uploaded", 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://docs.jdcloud.com/cn/load-balancer/api/describeloadbalancer
+ describeLoadBalancerReq := jdLbApi.NewDescribeLoadBalancerRequest(d.config.RegionId, d.config.LoadbalancerId)
+ describeLoadBalancerResp, err := d.sdkClient.DescribeLoadBalancer(describeLoadBalancerReq)
+ if err != nil {
+ return xerrors.Wrap(err, "failed to execute sdk request 'lb.DescribeLoadBalancer'")
+ } else {
+ d.logger.Logt("已查询到负载均衡器详情", describeLoadBalancerResp)
+ }
+
+ // 查询监听器列表
+ // REF: https://docs.jdcloud.com/cn/load-balancer/api/describelisteners
+ listenerIds := make([]string, 0)
+ describeListenersPageNumber := 1
+ describeListenersPageSize := 100
+ for {
+ describeListenersReq := jdLbApi.NewDescribeListenersRequest(d.config.RegionId)
+ describeListenersReq.SetFilters([]jdCommon.Filter{{Name: "loadBalancerId", Values: []string{d.config.LoadbalancerId}}})
+ describeListenersReq.SetPageSize(describeListenersPageNumber)
+ describeListenersReq.SetPageSize(describeListenersPageSize)
+ describeListenersResp, err := d.sdkClient.DescribeListeners(describeListenersReq)
+ if err != nil {
+ return xerrors.Wrap(err, "failed to execute sdk request 'lb.DescribeListeners'")
+ }
+
+ for _, listener := range describeListenersResp.Result.Listeners {
+ if strings.EqualFold(listener.Protocol, "https") || strings.EqualFold(listener.Protocol, "tls") {
+ listenerIds = append(listenerIds, listener.ListenerId)
+ }
+ }
+
+ if len(describeListenersResp.Result.Listeners) < int(describeListenersPageSize) {
+ break
+ } else {
+ describeListenersPageNumber++
+ }
+ }
+
+ // 遍历更新监听器证书
+ if len(listenerIds) == 0 {
+ return errors.New("listener not found")
+ } else {
+ d.logger.Logt("已查询到负载均衡器下的全部 HTTPS/TLS 监听器", listenerIds)
+
+ var errs []error
+
+ for _, listenerId := range listenerIds {
+ 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://docs.jdcloud.com/cn/load-balancer/api/describelistener
+ describeListenerReq := jdLbApi.NewDescribeListenerRequest(d.config.RegionId, cloudListenerId)
+ describeListenerResp, err := d.sdkClient.DescribeListener(describeListenerReq)
+ if err != nil {
+ return xerrors.Wrap(err, "failed to execute sdk request 'lb.DescribeListener'")
+ } else {
+ d.logger.Logt("已查询到监听器详情", describeListenerResp)
+ }
+
+ if d.config.Domain == "" {
+ // 未指定 SNI,只需部署到监听器
+
+ // 修改监听器信息
+ // REF: https://docs.jdcloud.com/cn/load-balancer/api/updatelistener
+ updateListenerReq := jdLbApi.NewUpdateListenerRequest(d.config.RegionId, cloudListenerId)
+ updateListenerReq.SetCertificateSpecs([]jdLbModel.CertificateSpec{{CertificateId: cloudCertId}})
+ updateListenerResp, err := d.sdkClient.UpdateListener(updateListenerReq)
+ if err != nil {
+ return xerrors.Wrap(err, "failed to execute sdk request 'lb.UpdateListener'")
+ } else {
+ d.logger.Logt("已修改监听器信息", updateListenerResp)
+ }
+ } else {
+ // 指定 SNI,需部署到扩展证书
+
+ extCertSpecs := slices.Filter(describeListenerResp.Result.Listener.ExtensionCertificateSpecs, func(extCertSpec jdLbModel.ExtensionCertificateSpec) bool {
+ return extCertSpec.Domain == d.config.Domain
+ })
+ if len(extCertSpecs) == 0 {
+ return errors.New("extension certificate spec not found")
+ }
+
+ // 批量修改扩展证书
+ // REF: https://docs.jdcloud.com/cn/load-balancer/api/updatelistenercertificates
+ updateListenerCertificatesReq := jdLbApi.NewUpdateListenerCertificatesRequest(
+ d.config.RegionId,
+ cloudListenerId,
+ slices.Map(extCertSpecs, func(extCertSpec jdLbModel.ExtensionCertificateSpec) jdLbModel.ExtCertificateUpdateSpec {
+ return jdLbModel.ExtCertificateUpdateSpec{
+ CertificateBindId: extCertSpec.CertificateBindId,
+ CertificateId: &cloudCertId,
+ Domain: &extCertSpec.Domain,
+ }
+ }),
+ )
+ updateListenerCertificatesResp, err := d.sdkClient.UpdateListenerCertificates(updateListenerCertificatesReq)
+ if err != nil {
+ return xerrors.Wrap(err, "failed to execute sdk request 'lb.UpdateListenerCertificates'")
+ } else {
+ d.logger.Logt("已批量修改扩展证书", updateListenerCertificatesResp)
+ }
+ }
+
+ return nil
+}
+
+func createSdkClient(accessKeyId, accessKeySecret string) (*jdLbClient.LbClient, error) {
+ clientCredentials := jdCore.NewCredentials(accessKeyId, accessKeySecret)
+ client := jdLbClient.NewLbClient(clientCredentials)
+ return client, nil
+}
diff --git a/internal/pkg/core/deployer/providers/jdcloud-alb/jdcloud_alb_test.go b/internal/pkg/core/deployer/providers/jdcloud-alb/jdcloud_alb_test.go
new file mode 100644
index 00000000..9c9cc9cc
--- /dev/null
+++ b/internal/pkg/core/deployer/providers/jdcloud-alb/jdcloud_alb_test.go
@@ -0,0 +1,118 @@
+package jdcloudalb_test
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "os"
+ "strings"
+ "testing"
+
+ provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/jdcloud-alb"
+)
+
+var (
+ fInputCertPath string
+ fInputKeyPath string
+ fAccessKeyId string
+ fAccessKeySecret string
+ fRegionId string
+ fLoadbalancerId string
+ fListenerId string
+)
+
+func init() {
+ argsPrefix := "CERTIMATE_DEPLOYER_JDCLOUDALB_"
+
+ flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
+ flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
+ flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
+ flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "")
+ flag.StringVar(&fRegionId, argsPrefix+"REGIONID", "", "")
+ flag.StringVar(&fLoadbalancerId, argsPrefix+"LOADBALANCERID", "", "")
+ flag.StringVar(&fListenerId, argsPrefix+"LISTENERID", "", "")
+}
+
+/*
+Shell command to run this test:
+
+ go test -v ./jdcloud_alb_test.go -args \
+ --CERTIMATE_DEPLOYER_JDCLOUDALB_INPUTCERTPATH="/path/to/your-input-cert.pem" \
+ --CERTIMATE_DEPLOYER_JDCLOUDALB_INPUTKEYPATH="/path/to/your-input-key.pem" \
+ --CERTIMATE_DEPLOYER_JDCLOUDALB_ACCESSKEYID="your-access-key-id" \
+ --CERTIMATE_DEPLOYER_JDCLOUDALB_ACCESSKEYSECRET="your-secret-access-key" \
+ --CERTIMATE_DEPLOYER_JDCLOUDALB_REGION_ID="cn-north-1" \
+ --CERTIMATE_DEPLOYER_JDCLOUDALB_LOADBALANCERID="your-alb-loadbalancer-id" \
+ --CERTIMATE_DEPLOYER_JDCLOUDALB_LISTENERID="your-alb-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("ACCESSKEYSECRET: %v", fAccessKeySecret),
+ fmt.Sprintf("REGIONID: %v", fRegionId),
+ fmt.Sprintf("LOADBALANCERID: %v", fLoadbalancerId),
+ }, "\n"))
+
+ deployer, err := provider.NewDeployer(&provider.DeployerConfig{
+ AccessKeyId: fAccessKeyId,
+ AccessKeySecret: fAccessKeySecret,
+ 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("ACCESSKEYSECRET: %v", fAccessKeySecret),
+ fmt.Sprintf("REGIONID: %v", fRegionId),
+ fmt.Sprintf("LISTENERID: %v", fListenerId),
+ }, "\n"))
+
+ deployer, err := provider.NewDeployer(&provider.DeployerConfig{
+ AccessKeyId: fAccessKeyId,
+ AccessKeySecret: fAccessKeySecret,
+ 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/deployer/providers/jdcloud-cdn/jdcloud_cdn_test.go b/internal/pkg/core/deployer/providers/jdcloud-cdn/jdcloud_cdn_test.go
index b95636dc..2d2f7ed0 100644
--- a/internal/pkg/core/deployer/providers/jdcloud-cdn/jdcloud_cdn_test.go
+++ b/internal/pkg/core/deployer/providers/jdcloud-cdn/jdcloud_cdn_test.go
@@ -20,7 +20,7 @@ var (
)
func init() {
- argsPrefix := "CERTIMATE_DEPLOYER_BAIDUCLOUDCDN_"
+ argsPrefix := "CERTIMATE_DEPLOYER_JDCLOUDCDN_"
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
diff --git a/internal/pkg/core/deployer/providers/jdcloud-live/jdcloud_live_test.go b/internal/pkg/core/deployer/providers/jdcloud-live/jdcloud_live_test.go
index 510198cc..076202b5 100644
--- a/internal/pkg/core/deployer/providers/jdcloud-live/jdcloud_live_test.go
+++ b/internal/pkg/core/deployer/providers/jdcloud-live/jdcloud_live_test.go
@@ -20,7 +20,7 @@ var (
)
func init() {
- argsPrefix := "CERTIMATE_DEPLOYER_BAIDUCLOUDCDN_"
+ argsPrefix := "CERTIMATE_DEPLOYER_JDCLOUDLIVE_"
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
diff --git a/internal/pkg/core/deployer/providers/tencentcloud-clb/tencentcloud_clb.go b/internal/pkg/core/deployer/providers/tencentcloud-clb/tencentcloud_clb.go
index acaf84b2..521f3e34 100644
--- a/internal/pkg/core/deployer/providers/tencentcloud-clb/tencentcloud_clb.go
+++ b/internal/pkg/core/deployer/providers/tencentcloud-clb/tencentcloud_clb.go
@@ -138,7 +138,7 @@ func (d *DeployerProvider) deployViaSslService(ctx context.Context, cloudCertId
// 未指定 SNI,只需部署到监听器
deployCertificateInstanceReq.InstanceIdList = common.StringPtrs([]string{fmt.Sprintf("%s|%s", d.config.LoadbalancerId, d.config.ListenerId)})
} else {
- // 指定 SNI,需部署到域名(支持泛域名)
+ // 指定 SNI,需部署到域名
deployCertificateInstanceReq.InstanceIdList = common.StringPtrs([]string{fmt.Sprintf("%s|%s|%s", d.config.LoadbalancerId, d.config.ListenerId, d.config.Domain)})
}
deployCertificateInstanceResp, err := d.sdkClients.ssl.DeployCertificateInstance(deployCertificateInstanceReq)
diff --git a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx
index 3ae859b6..f62191d0 100644
--- a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx
+++ b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx
@@ -38,6 +38,7 @@ import DeployNodeConfigFormGcoreCDNConfig from "./DeployNodeConfigFormGcoreCDNCo
import DeployNodeConfigFormHuaweiCloudCDNConfig from "./DeployNodeConfigFormHuaweiCloudCDNConfig";
import DeployNodeConfigFormHuaweiCloudELBConfig from "./DeployNodeConfigFormHuaweiCloudELBConfig";
import DeployNodeConfigFormHuaweiCloudWAFConfig from "./DeployNodeConfigFormHuaweiCloudWAFConfig";
+import DeployNodeConfigFormJDCloudALBConfig from "./DeployNodeConfigFormJDCloudALBConfig";
import DeployNodeConfigFormJDCloudCDNConfig from "./DeployNodeConfigFormJDCloudCDNConfig";
import DeployNodeConfigFormJDCloudLiveConfig from "./DeployNodeConfigFormJDCloudLiveConfig";
import DeployNodeConfigFormKubernetesSecretConfig from "./DeployNodeConfigFormKubernetesSecretConfig";
@@ -180,6 +181,8 @@ const DeployNodeConfigForm = forwardRef;
case DEPLOY_PROVIDERS.HUAWEICLOUD_WAF:
return ;
+ case DEPLOY_PROVIDERS.JDCLOUD_ALB:
+ return ;
case DEPLOY_PROVIDERS.JDCLOUD_CDN:
return ;
case DEPLOY_PROVIDERS.JDCLOUD_LIVE:
diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormJDCloudALBConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormJDCloudALBConfig.tsx
new file mode 100644
index 00000000..f54477ce
--- /dev/null
+++ b/ui/src/components/workflow/node/DeployNodeConfigFormJDCloudALBConfig.tsx
@@ -0,0 +1,142 @@
+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";
+import { validDomainName } from "@/utils/validators";
+
+type DeployNodeConfigFormJDCloudALBConfigFieldValues = Nullish<{
+ resourceType: string;
+ regionId: string;
+ loadbalancerId?: string;
+ listenerId?: string;
+ domain?: string;
+}>;
+
+export type DeployNodeConfigFormJDCloudALBConfigProps = {
+ form: FormInstance;
+ formName: string;
+ disabled?: boolean;
+ initialValues?: DeployNodeConfigFormJDCloudALBConfigFieldValues;
+ onValuesChange?: (values: DeployNodeConfigFormJDCloudALBConfigFieldValues) => void;
+};
+
+const RESOURCE_TYPE_LOADBALANCER = "loadbalancer" as const;
+const RESOURCE_TYPE_LISTENER = "listener" as const;
+
+const initFormModel = (): DeployNodeConfigFormJDCloudALBConfigFieldValues => {
+ return {};
+};
+
+const DeployNodeConfigFormJDCloudALBConfig = ({
+ form: formInst,
+ formName,
+ disabled,
+ initialValues,
+ onValuesChange,
+}: DeployNodeConfigFormJDCloudALBConfigProps) => {
+ 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.jdcloud_alb_resource_type.placeholder"),
+ }),
+ regionId: z
+ .string({ message: t("workflow_node.deploy.form.jdcloud_alb_region_id.placeholder") })
+ .nonempty(t("workflow_node.deploy.form.jdcloud_alb_region_id.placeholder"))
+ .trim(),
+ loadbalancerId: z
+ .string()
+ .max(64, t("common.errmsg.string_max", { max: 64 }))
+ .trim()
+ .nullish()
+ .refine((v) => fieldResourceType !== RESOURCE_TYPE_LOADBALANCER || !!v?.trim(), t("workflow_node.deploy.form.jdcloud_alb_loadbalancer_id.placeholder")),
+ listenerId: z
+ .string()
+ .max(64, t("common.errmsg.string_max", { max: 64 }))
+ .trim()
+ .nullish()
+ .refine((v) => fieldResourceType !== RESOURCE_TYPE_LISTENER || !!v?.trim(), t("workflow_node.deploy.form.jdcloud_alb_listener_id.placeholder")),
+ domain: z
+ .string()
+ .nullish()
+ .refine((v) => {
+ if (![RESOURCE_TYPE_LOADBALANCER, RESOURCE_TYPE_LISTENER].includes(fieldResourceType)) return true;
+ return !v || validDomainName(v!, { allowWildcard: true });
+ }, t("common.errmsg.domain_invalid")),
+ });
+ const formRule = createSchemaFieldRule(formSchema);
+
+ const fieldResourceType = Form.useWatch("resourceType", formInst);
+
+ const handleFormChange = (_: unknown, values: z.infer) => {
+ onValuesChange?.(values);
+ };
+
+ return (
+
+
+
+
+ }
+ >
+
+
+
+
+ }
+ >
+
+
+
+
+
+ }
+ >
+
+
+
+
+
+ }
+ >
+
+
+
+
+ );
+};
+
+export default DeployNodeConfigFormJDCloudALBConfig;
diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts
index a2b09baf..e2475247 100644
--- a/ui/src/domain/provider.ts
+++ b/ui/src/domain/provider.ts
@@ -221,6 +221,7 @@ export const DEPLOY_PROVIDERS = Object.freeze({
HUAWEICLOUD_CDN: `${ACCESS_PROVIDERS.HUAWEICLOUD}-cdn`,
HUAWEICLOUD_ELB: `${ACCESS_PROVIDERS.HUAWEICLOUD}-elb`,
HUAWEICLOUD_WAF: `${ACCESS_PROVIDERS.HUAWEICLOUD}-waf`,
+ JDCLOUD_ALB: `${ACCESS_PROVIDERS.JDCLOUD}-alb`,
JDCLOUD_CDN: `${ACCESS_PROVIDERS.JDCLOUD}-cdn`,
JDCLOUD_LIVE: `${ACCESS_PROVIDERS.JDCLOUD}-live`,
KUBERNETES_SECRET: `${ACCESS_PROVIDERS.KUBERNETES}-secret`,
@@ -309,6 +310,7 @@ export const deployProvidersMap: Maphttps://console-intl.huaweicloud.com/console/#/waf/domain/list",
+ "workflow_node.deploy.form.jdcloud_alb_resource_type.label": "Resource type",
+ "workflow_node.deploy.form.jdcloud_alb_resource_type.placeholder": "Please select resource type",
+ "workflow_node.deploy.form.jdcloud_alb_resource_type.option.loadbalancer.label": "ALB load balancer",
+ "workflow_node.deploy.form.jdcloud_alb_resource_type.option.listener.label": "ALB listener",
+ "workflow_node.deploy.form.jdcloud_alb_region_id.label": "JD Cloud ALB region ID",
+ "workflow_node.deploy.form.jdcloud_alb_region_id.placeholder": "Please enter JD Cloud ALB region ID (e.g. cn-north-1)",
+ "workflow_node.deploy.form.jdcloud_alb_region_id.tooltip": "For more information, see https://docs.jdcloud.com/en/common-declaration/api/introduction",
+ "workflow_node.deploy.form.jdcloud_alb_loadbalancer_id.label": "JD Cloud ALB load balancer ID",
+ "workflow_node.deploy.form.jdcloud_alb_loadbalancer_id.placeholder": "Please enter JD Cloud ALB load balancer ID",
+ "workflow_node.deploy.form.jdcloud_alb_loadbalancer_id.tooltip": "For more information, see https://cns-console.jdcloud.com/host/loadBalance/list",
+ "workflow_node.deploy.form.jdcloud_alb_listener_id.label": "JD Cloud ALB listener ID",
+ "workflow_node.deploy.form.jdcloud_alb_listener_id.placeholder": "Please enter JD Cloud ALB listener ID",
+ "workflow_node.deploy.form.jdcloud_alb_listener_id.tooltip": "For more information, see https://cns-console.jdcloud.com/host/loadBalance/list",
+ "workflow_node.deploy.form.jdcloud_alb_snidomain.label": "JD Cloud ALB SNI domain (Optional)",
+ "workflow_node.deploy.form.jdcloud_alb_snidomain.placeholder": "Please enter JD Cloud ALB SNI domain name",
+ "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/",
diff --git a/ui/src/i18n/locales/zh/nls.provider.json b/ui/src/i18n/locales/zh/nls.provider.json
index 0d8e2911..dfa3022c 100644
--- a/ui/src/i18n/locales/zh/nls.provider.json
+++ b/ui/src/i18n/locales/zh/nls.provider.json
@@ -54,6 +54,7 @@
"provider.huaweicloud.elb": "华为云 - 弹性负载均衡 ELB",
"provider.huaweicloud.waf": "华为云 - Web 应用防火墙 WAF",
"provider.jdcloud": "京东云",
+ "provider.jdcloud.alb": "京东云 - 应用负载均衡 ALB",
"provider.jdcloud.cdn": "京东云 - 内容分发网络 CDN",
"provider.jdcloud.dns": "京东云 - 云解析 DNS",
"provider.jdcloud.live": "京东云 - 视频直播",
diff --git a/ui/src/i18n/locales/zh/nls.workflow.nodes.json b/ui/src/i18n/locales/zh/nls.workflow.nodes.json
index a7331d08..0e68333b 100644
--- a/ui/src/i18n/locales/zh/nls.workflow.nodes.json
+++ b/ui/src/i18n/locales/zh/nls.workflow.nodes.json
@@ -104,7 +104,7 @@
"workflow_node.deploy.form.aliyun_alb_listener_id.placeholder": "请输入阿里云 ALB 监听器 ID",
"workflow_node.deploy.form.aliyun_alb_listener_id.tooltip": "这是什么?请参阅 https://slb.console.aliyun.com/alb",
"workflow_node.deploy.form.aliyun_alb_snidomain.label": "阿里云 ALB 扩展域名(可选)",
- "workflow_node.deploy.form.aliyun_alb_snidomain.placeholder": "请输入阿里云 ALB 扩展域名",
+ "workflow_node.deploy.form.aliyun_alb_snidomain.placeholder": "请输入阿里云 ALB 扩展域名(支持泛域名)",
"workflow_node.deploy.form.aliyun_alb_snidomain.tooltip": "这是什么?请参阅 https://slb.console.aliyun.com/alb
不填写时,将替换监听器的默认证书。",
"workflow_node.deploy.form.aliyun_cas_deploy.guide": "小贴士:由于阿里云证书部署任务是异步的,此节点若执行成功仅代表已创建部署任务,实际部署结果需要你自行前往阿里云控制台查询。",
"workflow_node.deploy.form.aliyun_cas_deploy_region.label": "阿里云 CAS 服务地域",
@@ -136,7 +136,7 @@
"workflow_node.deploy.form.aliyun_clb_listener_port.placeholder": "请输入阿里云 CLB 监听端口",
"workflow_node.deploy.form.aliyun_clb_listener_port.tooltip": "这是什么?请参阅 https://slb.console.aliyun.com/clb",
"workflow_node.deploy.form.aliyun_clb_snidomain.label": "阿里云 CLB 扩展域名(可选)",
- "workflow_node.deploy.form.aliyun_clb_snidomain.placeholder": "请输入阿里云 CLB 扩展域名",
+ "workflow_node.deploy.form.aliyun_clb_snidomain.placeholder": "请输入阿里云 CLB 扩展域名(支持泛域名)",
"workflow_node.deploy.form.aliyun_clb_snidomain.tooltip": "这是什么?请参阅 https://slb.console.aliyun.com/clb
不填写时,将替换监听器的默认证书。",
"workflow_node.deploy.form.aliyun_cdn_domain.label": "阿里云 CDN 加速域名",
"workflow_node.deploy.form.aliyun_cdn_domain.placeholder": "请输入阿里云 CDN 加速域名(支持泛域名)",
@@ -267,6 +267,22 @@
"workflow_node.deploy.form.huaweicloud_waf_domain.label": "华为云 WAF 防护域名",
"workflow_node.deploy.form.huaweicloud_waf_domain.placeholder": "请输入华为云 WAF 防护域名(支持泛域名)",
"workflow_node.deploy.form.huaweicloud_waf_domain.tooltip": "这是什么?请参阅 https://console.huaweicloud.com/console/#/waf/domain/list",
+ "workflow_node.deploy.form.jdcloud_alb_resource_type.label": "证书替换方式",
+ "workflow_node.deploy.form.jdcloud_alb_resource_type.placeholder": "请选择证书替换方式",
+ "workflow_node.deploy.form.jdcloud_alb_resource_type.option.loadbalancer.label": "替换指定负载均衡器下的全部 HTTPS/TLS 监听的证书",
+ "workflow_node.deploy.form.jdcloud_alb_resource_type.option.listener.label": "替换指定负载均衡监听器的证书",
+ "workflow_node.deploy.form.jdcloud_alb_region_id.label": "京东云 ALB 服务地域 ID",
+ "workflow_node.deploy.form.jdcloud_alb_region_id.placeholder": "请输入京东云 ALB 服务地域 ID(例如:cn-north-1",
+ "workflow_node.deploy.form.jdcloud_alb_region_id.tooltip": "这是什么?请参阅 https://docs.jdcloud.com/cn/common-declaration/api/introduction",
+ "workflow_node.deploy.form.jdcloud_alb_loadbalancer_id.label": "京东云 ALB 负载均衡器 ID",
+ "workflow_node.deploy.form.jdcloud_alb_loadbalancer_id.placeholder": "请输入京东云 ALB 负载均衡器 ID",
+ "workflow_node.deploy.form.jdcloud_alb_loadbalancer_id.tooltip": "这是什么?请参阅 https://cns-console.jdcloud.com/host/loadBalance/list",
+ "workflow_node.deploy.form.jdcloud_alb_listener_id.label": "京东云 ALB 监听器 ID",
+ "workflow_node.deploy.form.jdcloud_alb_listener_id.placeholder": "请输入京东云 ALB 监听器 ID",
+ "workflow_node.deploy.form.jdcloud_alb_listener_id.tooltip": "这是什么?请参阅 https://cns-console.jdcloud.com/host/loadBalance/list",
+ "workflow_node.deploy.form.jdcloud_alb_snidomain.label": "京东云 ALB 扩展域名(可选)",
+ "workflow_node.deploy.form.jdcloud_alb_snidomain.placeholder": "请输入京东云 ALB 扩展域名(支持泛域名)",
+ "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/",