From c846945905c20daf7ae32da6cfce905cb7323b1a Mon Sep 17 00:00:00 2001
From: Fu Diwei <fudiwei@sina.com>
Date: Fri, 20 Dec 2024 22:58:47 +0800
Subject: [PATCH] refactor(deployer): reimplement deploy service

---
 internal/deployer/aliyun_alb.go      | 281 --------------------
 internal/deployer/aliyun_cdn.go      |  88 -------
 internal/deployer/aliyun_clb.go      | 286 --------------------
 internal/deployer/aliyun_dcdn.go     |  95 -------
 internal/deployer/aliyun_nlb.go      | 245 -----------------
 internal/deployer/aliyun_oss.go      |  86 ------
 internal/deployer/baiducloud_cdn.go  |  80 ------
 internal/deployer/byteplus_cdn.go    | 116 --------
 internal/deployer/deployer.go        |  94 +++----
 internal/deployer/dogecloud_cdn.go   |  88 -------
 internal/deployer/factory.go         |  16 +-
 internal/deployer/huaweicloud_cdn.go | 143 ----------
 internal/deployer/huaweicloud_elb.go | 378 ---------------------------
 internal/deployer/k8s_secret.go      | 136 ----------
 internal/deployer/local.go           | 163 ------------
 internal/deployer/qiniu_cdn.go       | 113 --------
 internal/deployer/ssh.go             | 209 ---------------
 internal/deployer/ssh_test.go        |  12 -
 internal/deployer/tencent_cdn.go     | 194 --------------
 internal/deployer/tencent_clb.go     | 328 -----------------------
 internal/deployer/tencent_cos.go     | 107 --------
 internal/deployer/tencent_ecdn.go    | 154 -----------
 internal/deployer/tencent_teo.go     | 119 ---------
 internal/deployer/volcengine_cdn.go  | 116 --------
 internal/deployer/volcengine_live.go | 148 -----------
 internal/deployer/webhook.go         |  66 -----
 26 files changed, 48 insertions(+), 3813 deletions(-)
 delete mode 100644 internal/deployer/aliyun_alb.go
 delete mode 100644 internal/deployer/aliyun_cdn.go
 delete mode 100644 internal/deployer/aliyun_clb.go
 delete mode 100644 internal/deployer/aliyun_dcdn.go
 delete mode 100644 internal/deployer/aliyun_nlb.go
 delete mode 100644 internal/deployer/aliyun_oss.go
 delete mode 100644 internal/deployer/baiducloud_cdn.go
 delete mode 100644 internal/deployer/byteplus_cdn.go
 delete mode 100644 internal/deployer/dogecloud_cdn.go
 delete mode 100644 internal/deployer/huaweicloud_cdn.go
 delete mode 100644 internal/deployer/huaweicloud_elb.go
 delete mode 100644 internal/deployer/k8s_secret.go
 delete mode 100644 internal/deployer/local.go
 delete mode 100644 internal/deployer/qiniu_cdn.go
 delete mode 100644 internal/deployer/ssh.go
 delete mode 100644 internal/deployer/ssh_test.go
 delete mode 100644 internal/deployer/tencent_cdn.go
 delete mode 100644 internal/deployer/tencent_clb.go
 delete mode 100644 internal/deployer/tencent_cos.go
 delete mode 100644 internal/deployer/tencent_ecdn.go
 delete mode 100644 internal/deployer/tencent_teo.go
 delete mode 100644 internal/deployer/volcengine_cdn.go
 delete mode 100644 internal/deployer/volcengine_live.go
 delete mode 100644 internal/deployer/webhook.go

diff --git a/internal/deployer/aliyun_alb.go b/internal/deployer/aliyun_alb.go
deleted file mode 100644
index 2e05320e..00000000
--- a/internal/deployer/aliyun_alb.go
+++ /dev/null
@@ -1,281 +0,0 @@
-package deployer
-
-import (
-	"context"
-	"encoding/json"
-	"errors"
-	"fmt"
-	"strings"
-
-	aliyunAlb "github.com/alibabacloud-go/alb-20200616/v2/client"
-	aliyunOpen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
-	"github.com/alibabacloud-go/tea/tea"
-	xerrors "github.com/pkg/errors"
-
-	"github.com/usual2970/certimate/internal/domain"
-	"github.com/usual2970/certimate/internal/pkg/core/uploader"
-	uploaderAliyunCas "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas"
-)
-
-type AliyunALBDeployer struct {
-	option *DeployerOption
-	infos  []string
-
-	sdkClient   *aliyunAlb.Client
-	sslUploader uploader.Uploader
-}
-
-func NewAliyunALBDeployer(option *DeployerOption) (Deployer, error) {
-	access := &domain.AliyunAccess{}
-	if err := json.Unmarshal([]byte(option.Access), access); err != nil {
-		return nil, xerrors.Wrap(err, "failed to get access")
-	}
-
-	client, err := (&AliyunALBDeployer{}).createSdkClient(
-		access.AccessKeyId,
-		access.AccessKeySecret,
-		option.DeployConfig.GetConfigAsString("region"),
-	)
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to create sdk client")
-	}
-
-	aliCasRegion := option.DeployConfig.GetConfigAsString("region")
-	if aliCasRegion != "" {
-		// 阿里云 CAS 服务接入点是独立于 ALB 服务的
-		// 国内版接入点:华东一杭州
-		// 国际版接入点:亚太东南一新加坡
-		if !strings.HasPrefix(aliCasRegion, "cn-") {
-			aliCasRegion = "ap-southeast-1"
-		} else {
-			aliCasRegion = "cn-hangzhou"
-		}
-	}
-	uploader, err := uploaderAliyunCas.New(&uploaderAliyunCas.AliyunCASUploaderConfig{
-		AccessKeyId:     access.AccessKeyId,
-		AccessKeySecret: access.AccessKeySecret,
-		Region:          aliCasRegion,
-	})
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to create ssl uploader")
-	}
-
-	return &AliyunALBDeployer{
-		option:      option,
-		infos:       make([]string, 0),
-		sdkClient:   client,
-		sslUploader: uploader,
-	}, nil
-}
-
-func (d *AliyunALBDeployer) GetID() string {
-	return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
-}
-
-func (d *AliyunALBDeployer) GetInfos() []string {
-	return d.infos
-}
-
-func (d *AliyunALBDeployer) Deploy(ctx context.Context) error {
-	switch d.option.DeployConfig.GetConfigAsString("resourceType") {
-	case "loadbalancer":
-		if err := d.deployToLoadbalancer(ctx); err != nil {
-			return err
-		}
-	case "listener":
-		if err := d.deployToListener(ctx); err != nil {
-			return err
-		}
-	default:
-		return errors.New("unsupported resource type")
-	}
-
-	return nil
-}
-
-func (d *AliyunALBDeployer) createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunAlb.Client, error) {
-	if region == "" {
-		region = "cn-hangzhou" // ALB 服务默认区域:华东一杭州
-	}
-
-	aConfig := &aliyunOpen.Config{
-		AccessKeyId:     tea.String(accessKeyId),
-		AccessKeySecret: tea.String(accessKeySecret),
-	}
-
-	var endpoint string
-	switch region {
-	case "cn-hangzhou-finance":
-		endpoint = "alb.cn-hangzhou.aliyuncs.com"
-	default:
-		endpoint = fmt.Sprintf("alb.%s.aliyuncs.com", region)
-	}
-	aConfig.Endpoint = tea.String(endpoint)
-
-	client, err := aliyunAlb.NewClient(aConfig)
-	if err != nil {
-		return nil, err
-	}
-
-	return client, nil
-}
-
-func (d *AliyunALBDeployer) deployToLoadbalancer(ctx context.Context) error {
-	aliLoadbalancerId := d.option.DeployConfig.GetConfigAsString("loadbalancerId")
-	if aliLoadbalancerId == "" {
-		return errors.New("`loadbalancerId` is required")
-	}
-
-	aliListenerIds := make([]string, 0)
-
-	// 查询负载均衡实例的详细信息
-	// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-getloadbalancerattribute
-	getLoadBalancerAttributeReq := &aliyunAlb.GetLoadBalancerAttributeRequest{
-		LoadBalancerId: tea.String(aliLoadbalancerId),
-	}
-	getLoadBalancerAttributeResp, err := d.sdkClient.GetLoadBalancerAttribute(getLoadBalancerAttributeReq)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to execute sdk request 'alb.GetLoadBalancerAttribute'")
-	}
-
-	d.infos = append(d.infos, toStr("已查询到 ALB 负载均衡实例", getLoadBalancerAttributeResp))
-
-	// 查询 HTTPS 监听列表
-	// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-listlisteners
-	listListenersPage := 1
-	listListenersLimit := int32(100)
-	var listListenersToken *string = nil
-	for {
-		listListenersReq := &aliyunAlb.ListListenersRequest{
-			MaxResults:       tea.Int32(listListenersLimit),
-			NextToken:        listListenersToken,
-			LoadBalancerIds:  []*string{tea.String(aliLoadbalancerId)},
-			ListenerProtocol: tea.String("HTTPS"),
-		}
-		listListenersResp, err := d.sdkClient.ListListeners(listListenersReq)
-		if err != nil {
-			return xerrors.Wrap(err, "failed to execute sdk request 'alb.ListListeners'")
-		}
-
-		if listListenersResp.Body.Listeners != nil {
-			for _, listener := range listListenersResp.Body.Listeners {
-				aliListenerIds = append(aliListenerIds, *listener.ListenerId)
-			}
-		}
-
-		if listListenersResp.Body.NextToken == nil {
-			break
-		} else {
-			listListenersToken = listListenersResp.Body.NextToken
-			listListenersPage += 1
-		}
-	}
-
-	d.infos = append(d.infos, toStr("已查询到 ALB 负载均衡实例下的全部 HTTPS 监听", aliListenerIds))
-
-	// 查询 QUIC 监听列表
-	// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-listlisteners
-	listListenersPage = 1
-	listListenersToken = nil
-	for {
-		listListenersReq := &aliyunAlb.ListListenersRequest{
-			MaxResults:       tea.Int32(listListenersLimit),
-			NextToken:        listListenersToken,
-			LoadBalancerIds:  []*string{tea.String(aliLoadbalancerId)},
-			ListenerProtocol: tea.String("QUIC"),
-		}
-		listListenersResp, err := d.sdkClient.ListListeners(listListenersReq)
-		if err != nil {
-			return xerrors.Wrap(err, "failed to execute sdk request 'alb.ListListeners'")
-		}
-
-		if listListenersResp.Body.Listeners != nil {
-			for _, listener := range listListenersResp.Body.Listeners {
-				aliListenerIds = append(aliListenerIds, *listener.ListenerId)
-			}
-		}
-
-		if listListenersResp.Body.NextToken == nil {
-			break
-		} else {
-			listListenersToken = listListenersResp.Body.NextToken
-			listListenersPage += 1
-		}
-	}
-
-	d.infos = append(d.infos, toStr("已查询到 ALB 负载均衡实例下的全部 QUIC 监听", aliListenerIds))
-
-	// 上传证书到 SSL
-	upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
-	if err != nil {
-		return err
-	}
-
-	d.infos = append(d.infos, toStr("已上传证书", upres))
-
-	// 批量更新监听证书
-	var errs []error
-	for _, aliListenerId := range aliListenerIds {
-		if err := d.updateListenerCertificate(ctx, aliListenerId, upres.CertId); err != nil {
-			errs = append(errs, err)
-		}
-	}
-	if len(errs) > 0 {
-		return errors.Join(errs...)
-	}
-
-	return nil
-}
-
-func (d *AliyunALBDeployer) deployToListener(ctx context.Context) error {
-	aliListenerId := d.option.DeployConfig.GetConfigAsString("listenerId")
-	if aliListenerId == "" {
-		return errors.New("`listenerId` is required")
-	}
-
-	// 上传证书到 SSL
-	upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
-	if err != nil {
-		return err
-	}
-
-	d.infos = append(d.infos, toStr("已上传证书", upres))
-
-	// 更新监听
-	if err := d.updateListenerCertificate(ctx, aliListenerId, upres.CertId); err != nil {
-		return err
-	}
-
-	return nil
-}
-
-func (d *AliyunALBDeployer) updateListenerCertificate(ctx context.Context, aliListenerId string, aliCertId string) error {
-	// 查询监听的属性
-	// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-getlistenerattribute
-	getListenerAttributeReq := &aliyunAlb.GetListenerAttributeRequest{
-		ListenerId: tea.String(aliListenerId),
-	}
-	getListenerAttributeResp, err := d.sdkClient.GetListenerAttribute(getListenerAttributeReq)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to execute sdk request 'alb.GetListenerAttribute'")
-	}
-
-	d.infos = append(d.infos, toStr("已查询到 ALB 监听配置", getListenerAttributeResp))
-
-	// 修改监听的属性
-	// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-updatelistenerattribute
-	updateListenerAttributeReq := &aliyunAlb.UpdateListenerAttributeRequest{
-		ListenerId: tea.String(aliListenerId),
-		Certificates: []*aliyunAlb.UpdateListenerAttributeRequestCertificates{{
-			CertificateId: tea.String(aliCertId),
-		}},
-	}
-	updateListenerAttributeResp, err := d.sdkClient.UpdateListenerAttribute(updateListenerAttributeReq)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to execute sdk request 'alb.UpdateListenerAttribute'")
-	}
-
-	d.infos = append(d.infos, toStr("已更新 ALB 监听配置", updateListenerAttributeResp))
-
-	return nil
-}
diff --git a/internal/deployer/aliyun_cdn.go b/internal/deployer/aliyun_cdn.go
deleted file mode 100644
index a23ce8a5..00000000
--- a/internal/deployer/aliyun_cdn.go
+++ /dev/null
@@ -1,88 +0,0 @@
-package deployer
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-	"time"
-
-	aliyunCdn "github.com/alibabacloud-go/cdn-20180510/v5/client"
-	aliyunOpen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
-	"github.com/alibabacloud-go/tea/tea"
-	xerrors "github.com/pkg/errors"
-
-	"github.com/usual2970/certimate/internal/domain"
-)
-
-type AliyunCDNDeployer struct {
-	option *DeployerOption
-	infos  []string
-
-	sdkClient *aliyunCdn.Client
-}
-
-func NewAliyunCDNDeployer(option *DeployerOption) (Deployer, error) {
-	access := &domain.AliyunAccess{}
-	if err := json.Unmarshal([]byte(option.Access), access); err != nil {
-		return nil, xerrors.Wrap(err, "failed to get access")
-	}
-
-	client, err := (&AliyunCDNDeployer{}).createSdkClient(
-		access.AccessKeyId,
-		access.AccessKeySecret,
-	)
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to create sdk client")
-	}
-
-	return &AliyunCDNDeployer{
-		option:    option,
-		infos:     make([]string, 0),
-		sdkClient: client,
-	}, nil
-}
-
-func (d *AliyunCDNDeployer) GetID() string {
-	return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
-}
-
-func (d *AliyunCDNDeployer) GetInfos() []string {
-	return d.infos
-}
-
-func (d *AliyunCDNDeployer) Deploy(ctx context.Context) error {
-	// 设置 CDN 域名域名证书
-	// REF: https://help.aliyun.com/zh/cdn/developer-reference/api-cdn-2018-05-10-setcdndomainsslcertificate
-	setCdnDomainSSLCertificateReq := &aliyunCdn.SetCdnDomainSSLCertificateRequest{
-		DomainName:  tea.String(d.option.DeployConfig.GetConfigAsString("domain")),
-		CertRegion:  tea.String(d.option.DeployConfig.GetConfigOrDefaultAsString("region", "cn-hangzhou")),
-		CertName:    tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
-		CertType:    tea.String("upload"),
-		SSLProtocol: tea.String("on"),
-		SSLPub:      tea.String(d.option.Certificate.Certificate),
-		SSLPri:      tea.String(d.option.Certificate.PrivateKey),
-	}
-	setCdnDomainSSLCertificateResp, err := d.sdkClient.SetCdnDomainSSLCertificate(setCdnDomainSSLCertificateReq)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to execute sdk request 'cdn.SetCdnDomainSSLCertificate'")
-	}
-
-	d.infos = append(d.infos, toStr("已设置 CDN 域名证书", setCdnDomainSSLCertificateResp))
-
-	return nil
-}
-
-func (d *AliyunCDNDeployer) createSdkClient(accessKeyId, accessKeySecret string) (*aliyunCdn.Client, error) {
-	aConfig := &aliyunOpen.Config{
-		AccessKeyId:     tea.String(accessKeyId),
-		AccessKeySecret: tea.String(accessKeySecret),
-		Endpoint:        tea.String("cdn.aliyuncs.com"),
-	}
-
-	client, err := aliyunCdn.NewClient(aConfig)
-	if err != nil {
-		return nil, err
-	}
-
-	return client, nil
-}
diff --git a/internal/deployer/aliyun_clb.go b/internal/deployer/aliyun_clb.go
deleted file mode 100644
index 95d32b5d..00000000
--- a/internal/deployer/aliyun_clb.go
+++ /dev/null
@@ -1,286 +0,0 @@
-package deployer
-
-import (
-	"context"
-	"encoding/json"
-	"errors"
-	"fmt"
-
-	aliyunOpen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
-	aliyunSlb "github.com/alibabacloud-go/slb-20140515/v4/client"
-	"github.com/alibabacloud-go/tea/tea"
-	xerrors "github.com/pkg/errors"
-
-	"github.com/usual2970/certimate/internal/domain"
-	"github.com/usual2970/certimate/internal/pkg/core/uploader"
-	uploaderAliyunSlb "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-slb"
-)
-
-type AliyunCLBDeployer struct {
-	option *DeployerOption
-	infos  []string
-
-	sdkClient   *aliyunSlb.Client
-	sslUploader uploader.Uploader
-}
-
-func NewAliyunCLBDeployer(option *DeployerOption) (Deployer, error) {
-	access := &domain.AliyunAccess{}
-	if err := json.Unmarshal([]byte(option.Access), access); err != nil {
-		return nil, xerrors.Wrap(err, "failed to get access")
-	}
-
-	client, err := (&AliyunCLBDeployer{}).createSdkClient(
-		access.AccessKeyId,
-		access.AccessKeySecret,
-		option.DeployConfig.GetConfigAsString("region"),
-	)
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to create sdk client")
-	}
-
-	uploader, err := uploaderAliyunSlb.New(&uploaderAliyunSlb.AliyunSLBUploaderConfig{
-		AccessKeyId:     access.AccessKeyId,
-		AccessKeySecret: access.AccessKeySecret,
-		Region:          option.DeployConfig.GetConfigAsString("region"),
-	})
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to create ssl uploader")
-	}
-
-	return &AliyunCLBDeployer{
-		option:      option,
-		infos:       make([]string, 0),
-		sdkClient:   client,
-		sslUploader: uploader,
-	}, nil
-}
-
-func (d *AliyunCLBDeployer) GetID() string {
-	return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
-}
-
-func (d *AliyunCLBDeployer) GetInfos() []string {
-	return d.infos
-}
-
-func (d *AliyunCLBDeployer) Deploy(ctx context.Context) error {
-	switch d.option.DeployConfig.GetConfigAsString("resourceType") {
-	case "loadbalancer":
-		if err := d.deployToLoadbalancer(ctx); err != nil {
-			return err
-		}
-	case "listener":
-		if err := d.deployToListener(ctx); err != nil {
-			return err
-		}
-	default:
-		return errors.New("unsupported resource type")
-	}
-
-	return nil
-}
-
-func (d *AliyunCLBDeployer) createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunSlb.Client, error) {
-	if region == "" {
-		region = "cn-hangzhou" // CLB(SLB) 服务默认区域:华东一杭州
-	}
-
-	aConfig := &aliyunOpen.Config{
-		AccessKeyId:     tea.String(accessKeyId),
-		AccessKeySecret: tea.String(accessKeySecret),
-	}
-
-	var endpoint string
-	switch region {
-	case
-		"cn-hangzhou",
-		"cn-hangzhou-finance",
-		"cn-shanghai-finance-1",
-		"cn-shenzhen-finance-1":
-		endpoint = "slb.aliyuncs.com"
-	default:
-		endpoint = fmt.Sprintf("slb.%s.aliyuncs.com", region)
-	}
-	aConfig.Endpoint = tea.String(endpoint)
-
-	client, err := aliyunSlb.NewClient(aConfig)
-	if err != nil {
-		return nil, err
-	}
-
-	return client, nil
-}
-
-func (d *AliyunCLBDeployer) deployToLoadbalancer(ctx context.Context) error {
-	aliLoadbalancerId := d.option.DeployConfig.GetConfigAsString("loadbalancerId")
-	aliListenerPorts := make([]int32, 0)
-	if aliLoadbalancerId == "" {
-		return errors.New("`loadbalancerId` is required")
-	}
-
-	// 查询负载均衡实例的详细信息
-	// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-describeloadbalancerattribute
-	describeLoadBalancerAttributeReq := &aliyunSlb.DescribeLoadBalancerAttributeRequest{
-		RegionId:       tea.String(d.option.DeployConfig.GetConfigAsString("region")),
-		LoadBalancerId: tea.String(aliLoadbalancerId),
-	}
-	describeLoadBalancerAttributeResp, err := d.sdkClient.DescribeLoadBalancerAttribute(describeLoadBalancerAttributeReq)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to execute sdk request 'slb.DescribeLoadBalancerAttribute'")
-	}
-
-	d.infos = append(d.infos, toStr("已查询到 CLB 负载均衡实例", describeLoadBalancerAttributeResp))
-
-	// 查询 HTTPS 监听列表
-	// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-describeloadbalancerlisteners
-	listListenersPage := 1
-	listListenersLimit := int32(100)
-	var listListenersToken *string = nil
-	for {
-		describeLoadBalancerListenersReq := &aliyunSlb.DescribeLoadBalancerListenersRequest{
-			RegionId:         tea.String(d.option.DeployConfig.GetConfigAsString("region")),
-			MaxResults:       tea.Int32(listListenersLimit),
-			NextToken:        listListenersToken,
-			LoadBalancerId:   []*string{tea.String(aliLoadbalancerId)},
-			ListenerProtocol: tea.String("https"),
-		}
-		describeLoadBalancerListenersResp, err := d.sdkClient.DescribeLoadBalancerListeners(describeLoadBalancerListenersReq)
-		if err != nil {
-			return xerrors.Wrap(err, "failed to execute sdk request 'slb.DescribeLoadBalancerListeners'")
-		}
-
-		if describeLoadBalancerListenersResp.Body.Listeners != nil {
-			for _, listener := range describeLoadBalancerListenersResp.Body.Listeners {
-				aliListenerPorts = append(aliListenerPorts, *listener.ListenerPort)
-			}
-		}
-
-		if describeLoadBalancerListenersResp.Body.NextToken == nil {
-			break
-		} else {
-			listListenersToken = describeLoadBalancerListenersResp.Body.NextToken
-			listListenersPage += 1
-		}
-	}
-
-	d.infos = append(d.infos, toStr("已查询到 CLB 负载均衡实例下的全部 HTTPS 监听", aliListenerPorts))
-
-	// 上传证书到 SLB
-	upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
-	if err != nil {
-		return err
-	}
-
-	d.infos = append(d.infos, toStr("已上传证书", upres))
-
-	// 批量更新监听证书
-	var errs []error
-	for _, aliListenerPort := range aliListenerPorts {
-		if err := d.updateListenerCertificate(ctx, aliLoadbalancerId, aliListenerPort, upres.CertId); err != nil {
-			errs = append(errs, err)
-		}
-	}
-	if len(errs) > 0 {
-		return errors.Join(errs...)
-	}
-
-	return nil
-}
-
-func (d *AliyunCLBDeployer) deployToListener(ctx context.Context) error {
-	aliLoadbalancerId := d.option.DeployConfig.GetConfigAsString("loadbalancerId")
-	if aliLoadbalancerId == "" {
-		return errors.New("`loadbalancerId` is required")
-	}
-
-	aliListenerPort := d.option.DeployConfig.GetConfigAsInt32("listenerPort")
-	if aliListenerPort == 0 {
-		return errors.New("`listenerPort` is required")
-	}
-
-	// 上传证书到 SLB
-	upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
-	if err != nil {
-		return err
-	}
-
-	d.infos = append(d.infos, toStr("已上传证书", upres))
-
-	// 更新监听
-	if err := d.updateListenerCertificate(ctx, aliLoadbalancerId, aliListenerPort, upres.CertId); err != nil {
-		return err
-	}
-
-	return nil
-}
-
-func (d *AliyunCLBDeployer) updateListenerCertificate(ctx context.Context, aliLoadbalancerId string, aliListenerPort int32, aliCertId string) error {
-	// 查询监听配置
-	// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-describeloadbalancerhttpslistenerattribute
-	describeLoadBalancerHTTPSListenerAttributeReq := &aliyunSlb.DescribeLoadBalancerHTTPSListenerAttributeRequest{
-		LoadBalancerId: tea.String(aliLoadbalancerId),
-		ListenerPort:   tea.Int32(aliListenerPort),
-	}
-	describeLoadBalancerHTTPSListenerAttributeResp, err := d.sdkClient.DescribeLoadBalancerHTTPSListenerAttribute(describeLoadBalancerHTTPSListenerAttributeReq)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to execute sdk request 'slb.DescribeLoadBalancerHTTPSListenerAttribute'")
-	}
-
-	d.infos = append(d.infos, toStr("已查询到 CLB HTTPS 监听配置", describeLoadBalancerHTTPSListenerAttributeResp))
-
-	// 查询扩展域名
-	// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-describedomainextensions
-	describeDomainExtensionsReq := &aliyunSlb.DescribeDomainExtensionsRequest{
-		RegionId:       tea.String(d.option.DeployConfig.GetConfigAsString("region")),
-		LoadBalancerId: tea.String(aliLoadbalancerId),
-		ListenerPort:   tea.Int32(aliListenerPort),
-	}
-	describeDomainExtensionsResp, err := d.sdkClient.DescribeDomainExtensions(describeDomainExtensionsReq)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to execute sdk request 'slb.DescribeDomainExtensions'")
-	}
-
-	d.infos = append(d.infos, toStr("已查询到 CLB 扩展域名", describeDomainExtensionsResp))
-
-	// 遍历修改扩展域名
-	// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-setdomainextensionattribute
-	//
-	// 这里仅修改跟被替换证书一致的扩展域名
-	if describeDomainExtensionsResp.Body.DomainExtensions != nil && describeDomainExtensionsResp.Body.DomainExtensions.DomainExtension != nil {
-		for _, domainExtension := range describeDomainExtensionsResp.Body.DomainExtensions.DomainExtension {
-			if *domainExtension.ServerCertificateId != *describeLoadBalancerHTTPSListenerAttributeResp.Body.ServerCertificateId {
-				continue
-			}
-
-			setDomainExtensionAttributeReq := &aliyunSlb.SetDomainExtensionAttributeRequest{
-				RegionId:            tea.String(d.option.DeployConfig.GetConfigAsString("region")),
-				DomainExtensionId:   tea.String(*domainExtension.DomainExtensionId),
-				ServerCertificateId: tea.String(aliCertId),
-			}
-			_, err := d.sdkClient.SetDomainExtensionAttribute(setDomainExtensionAttributeReq)
-			if err != nil {
-				return xerrors.Wrap(err, "failed to execute sdk request 'slb.SetDomainExtensionAttribute'")
-			}
-		}
-	}
-
-	// 修改监听配置
-	// 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.option.DeployConfig.GetConfigAsString("region")),
-		LoadBalancerId:      tea.String(aliLoadbalancerId),
-		ListenerPort:        tea.Int32(aliListenerPort),
-		ServerCertificateId: tea.String(aliCertId),
-	}
-	setLoadBalancerHTTPSListenerAttributeResp, err := d.sdkClient.SetLoadBalancerHTTPSListenerAttribute(setLoadBalancerHTTPSListenerAttributeReq)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to execute sdk request 'slb.SetLoadBalancerHTTPSListenerAttribute'")
-	}
-
-	d.infos = append(d.infos, toStr("已更新 CLB HTTPS 监听配置", setLoadBalancerHTTPSListenerAttributeResp))
-
-	return nil
-}
diff --git a/internal/deployer/aliyun_dcdn.go b/internal/deployer/aliyun_dcdn.go
deleted file mode 100644
index 0f969787..00000000
--- a/internal/deployer/aliyun_dcdn.go
+++ /dev/null
@@ -1,95 +0,0 @@
-package deployer
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-	"strings"
-	"time"
-
-	aliyunOpen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
-	aliyunDcdn "github.com/alibabacloud-go/dcdn-20180115/v3/client"
-	"github.com/alibabacloud-go/tea/tea"
-	xerrors "github.com/pkg/errors"
-
-	"github.com/usual2970/certimate/internal/domain"
-)
-
-type AliyunDCDNDeployer struct {
-	option *DeployerOption
-	infos  []string
-
-	sdkClient *aliyunDcdn.Client
-}
-
-func NewAliyunDCDNDeployer(option *DeployerOption) (Deployer, error) {
-	access := &domain.AliyunAccess{}
-	if err := json.Unmarshal([]byte(option.Access), access); err != nil {
-		return nil, xerrors.Wrap(err, "failed to get access")
-	}
-
-	client, err := (&AliyunDCDNDeployer{}).createSdkClient(
-		access.AccessKeyId,
-		access.AccessKeySecret,
-	)
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to create sdk client")
-	}
-
-	return &AliyunDCDNDeployer{
-		option:    option,
-		infos:     make([]string, 0),
-		sdkClient: client,
-	}, nil
-}
-
-func (d *AliyunDCDNDeployer) GetID() string {
-	return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
-}
-
-func (d *AliyunDCDNDeployer) GetInfos() []string {
-	return d.infos
-}
-
-func (d *AliyunDCDNDeployer) Deploy(ctx context.Context) error {
-	// 支持泛解析域名,在 Aliyun DCDN 中泛解析域名表示为 .example.com
-	domain := d.option.DeployConfig.GetConfigAsString("domain")
-	if strings.HasPrefix(domain, "*") {
-		domain = strings.TrimPrefix(domain, "*")
-	}
-
-	// 配置域名证书
-	// REF: https://help.aliyun.com/zh/edge-security-acceleration/dcdn/developer-reference/api-dcdn-2018-01-15-setdcdndomainsslcertificate
-	setDcdnDomainSSLCertificateReq := &aliyunDcdn.SetDcdnDomainSSLCertificateRequest{
-		DomainName:  tea.String(domain),
-		CertRegion:  tea.String(d.option.DeployConfig.GetConfigOrDefaultAsString("region", "cn-hangzhou")),
-		CertName:    tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
-		CertType:    tea.String("upload"),
-		SSLProtocol: tea.String("on"),
-		SSLPub:      tea.String(d.option.Certificate.Certificate),
-		SSLPri:      tea.String(d.option.Certificate.PrivateKey),
-	}
-	setDcdnDomainSSLCertificateResp, err := d.sdkClient.SetDcdnDomainSSLCertificate(setDcdnDomainSSLCertificateReq)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to execute sdk request 'dcdn.SetDcdnDomainSSLCertificate'")
-	}
-
-	d.infos = append(d.infos, toStr("已配置 DCDN 域名证书", setDcdnDomainSSLCertificateResp))
-
-	return nil
-}
-
-func (d *AliyunDCDNDeployer) createSdkClient(accessKeyId, accessKeySecret string) (*aliyunDcdn.Client, error) {
-	aConfig := &aliyunOpen.Config{
-		AccessKeyId:     tea.String(accessKeyId),
-		AccessKeySecret: tea.String(accessKeySecret),
-		Endpoint:        tea.String("dcdn.aliyuncs.com"),
-	}
-
-	client, err := aliyunDcdn.NewClient(aConfig)
-	if err != nil {
-		return nil, err
-	}
-
-	return client, nil
-}
diff --git a/internal/deployer/aliyun_nlb.go b/internal/deployer/aliyun_nlb.go
deleted file mode 100644
index bbaddd7b..00000000
--- a/internal/deployer/aliyun_nlb.go
+++ /dev/null
@@ -1,245 +0,0 @@
-package deployer
-
-import (
-	"context"
-	"encoding/json"
-	"errors"
-	"fmt"
-	"strings"
-
-	aliyunOpen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
-	aliyunNlb "github.com/alibabacloud-go/nlb-20220430/v2/client"
-	"github.com/alibabacloud-go/tea/tea"
-	xerrors "github.com/pkg/errors"
-
-	"github.com/usual2970/certimate/internal/domain"
-	"github.com/usual2970/certimate/internal/pkg/core/uploader"
-	uploaderAliyunCas "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas"
-)
-
-type AliyunNLBDeployer struct {
-	option *DeployerOption
-	infos  []string
-
-	sdkClient   *aliyunNlb.Client
-	sslUploader uploader.Uploader
-}
-
-func NewAliyunNLBDeployer(option *DeployerOption) (Deployer, error) {
-	access := &domain.AliyunAccess{}
-	if err := json.Unmarshal([]byte(option.Access), access); err != nil {
-		return nil, xerrors.Wrap(err, "failed to get access")
-	}
-
-	client, err := (&AliyunNLBDeployer{}).createSdkClient(
-		access.AccessKeyId,
-		access.AccessKeySecret,
-		option.DeployConfig.GetConfigAsString("region"),
-	)
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to create sdk client")
-	}
-
-	aliCasRegion := option.DeployConfig.GetConfigAsString("region")
-	if aliCasRegion != "" {
-		// 阿里云 CAS 服务接入点是独立于 NLB 服务的
-		// 国内版接入点:华东一杭州
-		// 国际版接入点:亚太东南一新加坡
-		if !strings.HasPrefix(aliCasRegion, "cn-") {
-			aliCasRegion = "ap-southeast-1"
-		} else {
-			aliCasRegion = "cn-hangzhou"
-		}
-	}
-	uploader, err := uploaderAliyunCas.New(&uploaderAliyunCas.AliyunCASUploaderConfig{
-		AccessKeyId:     access.AccessKeyId,
-		AccessKeySecret: access.AccessKeySecret,
-		Region:          aliCasRegion,
-	})
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to create ssl uploader")
-	}
-
-	return &AliyunNLBDeployer{
-		option:      option,
-		infos:       make([]string, 0),
-		sdkClient:   client,
-		sslUploader: uploader,
-	}, nil
-}
-
-func (d *AliyunNLBDeployer) GetID() string {
-	return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
-}
-
-func (d *AliyunNLBDeployer) GetInfos() []string {
-	return d.infos
-}
-
-func (d *AliyunNLBDeployer) Deploy(ctx context.Context) error {
-	switch d.option.DeployConfig.GetConfigAsString("resourceType") {
-	case "loadbalancer":
-		if err := d.deployToLoadbalancer(ctx); err != nil {
-			return err
-		}
-	case "listener":
-		if err := d.deployToListener(ctx); err != nil {
-			return err
-		}
-	default:
-		return errors.New("unsupported resource type")
-	}
-
-	return nil
-}
-
-func (d *AliyunNLBDeployer) createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunNlb.Client, error) {
-	if region == "" {
-		region = "cn-hangzhou" // NLB 服务默认区域:华东一杭州
-	}
-
-	aConfig := &aliyunOpen.Config{
-		AccessKeyId:     tea.String(accessKeyId),
-		AccessKeySecret: tea.String(accessKeySecret),
-	}
-
-	var endpoint string
-	switch region {
-	default:
-		endpoint = fmt.Sprintf("nlb.%s.aliyuncs.com", region)
-	}
-	aConfig.Endpoint = tea.String(endpoint)
-
-	client, err := aliyunNlb.NewClient(aConfig)
-	if err != nil {
-		return nil, err
-	}
-
-	return client, nil
-}
-
-func (d *AliyunNLBDeployer) deployToLoadbalancer(ctx context.Context) error {
-	aliLoadbalancerId := d.option.DeployConfig.GetConfigAsString("loadbalancerId")
-	if aliLoadbalancerId == "" {
-		return errors.New("`loadbalancerId` is required")
-	}
-
-	aliListenerIds := make([]string, 0)
-
-	// 查询负载均衡实例的详细信息
-	// REF: https://help.aliyun.com/zh/slb/network-load-balancer/developer-reference/api-nlb-2022-04-30-getloadbalancerattribute
-	getLoadBalancerAttributeReq := &aliyunNlb.GetLoadBalancerAttributeRequest{
-		LoadBalancerId: tea.String(aliLoadbalancerId),
-	}
-	getLoadBalancerAttributeResp, err := d.sdkClient.GetLoadBalancerAttribute(getLoadBalancerAttributeReq)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to execute sdk request 'nlb.GetLoadBalancerAttribute'")
-	}
-
-	d.infos = append(d.infos, toStr("已查询到 NLB 负载均衡实例", getLoadBalancerAttributeResp))
-
-	// 查询 TCPSSL 监听列表
-	// REF: https://help.aliyun.com/zh/slb/network-load-balancer/developer-reference/api-nlb-2022-04-30-listlisteners
-	listListenersPage := 1
-	listListenersLimit := int32(100)
-	var listListenersToken *string = nil
-	for {
-		listListenersReq := &aliyunNlb.ListListenersRequest{
-			MaxResults:       tea.Int32(listListenersLimit),
-			NextToken:        listListenersToken,
-			LoadBalancerIds:  []*string{tea.String(aliLoadbalancerId)},
-			ListenerProtocol: tea.String("TCPSSL"),
-		}
-		listListenersResp, err := d.sdkClient.ListListeners(listListenersReq)
-		if err != nil {
-			return xerrors.Wrap(err, "failed to execute sdk request 'nlb.ListListeners'")
-		}
-
-		if listListenersResp.Body.Listeners != nil {
-			for _, listener := range listListenersResp.Body.Listeners {
-				aliListenerIds = append(aliListenerIds, *listener.ListenerId)
-			}
-		}
-
-		if listListenersResp.Body.NextToken == nil {
-			break
-		} else {
-			listListenersToken = listListenersResp.Body.NextToken
-			listListenersPage += 1
-		}
-	}
-
-	d.infos = append(d.infos, toStr("已查询到 NLB 负载均衡实例下的全部 TCPSSL 监听", aliListenerIds))
-
-	// 上传证书到 SSL
-	upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
-	if err != nil {
-		return err
-	}
-
-	d.infos = append(d.infos, toStr("已上传证书", upres))
-
-	// 批量更新监听证书
-	var errs []error
-	for _, aliListenerId := range aliListenerIds {
-		if err := d.updateListenerCertificate(ctx, aliListenerId, upres.CertId); err != nil {
-			errs = append(errs, err)
-		}
-	}
-	if len(errs) > 0 {
-		return errors.Join(errs...)
-	}
-
-	return nil
-}
-
-func (d *AliyunNLBDeployer) deployToListener(ctx context.Context) error {
-	aliListenerId := d.option.DeployConfig.GetConfigAsString("listenerId")
-	if aliListenerId == "" {
-		return errors.New("`listenerId` is required")
-	}
-
-	// 上传证书到 SSL
-	upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
-	if err != nil {
-		return err
-	}
-
-	d.infos = append(d.infos, toStr("已上传证书", upres))
-
-	// 更新监听
-	if err := d.updateListenerCertificate(ctx, aliListenerId, upres.CertId); err != nil {
-		return err
-	}
-
-	return nil
-}
-
-func (d *AliyunNLBDeployer) updateListenerCertificate(ctx context.Context, aliListenerId string, aliCertId string) error {
-	// 查询监听的属性
-	// REF: https://help.aliyun.com/zh/slb/network-load-balancer/developer-reference/api-nlb-2022-04-30-getlistenerattribute
-	getListenerAttributeReq := &aliyunNlb.GetListenerAttributeRequest{
-		ListenerId: tea.String(aliListenerId),
-	}
-	getListenerAttributeResp, err := d.sdkClient.GetListenerAttribute(getListenerAttributeReq)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to execute sdk request 'nlb.GetListenerAttribute'")
-	}
-
-	d.infos = append(d.infos, toStr("已查询到 NLB 监听配置", getListenerAttributeResp))
-
-	// 修改监听的属性
-	// REF: https://help.aliyun.com/zh/slb/network-load-balancer/developer-reference/api-nlb-2022-04-30-updatelistenerattribute
-	updateListenerAttributeReq := &aliyunNlb.UpdateListenerAttributeRequest{
-		ListenerId:     tea.String(aliListenerId),
-		CertificateIds: []*string{tea.String(aliCertId)},
-	}
-	updateListenerAttributeResp, err := d.sdkClient.UpdateListenerAttribute(updateListenerAttributeReq)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to execute sdk request 'nlb.UpdateListenerAttribute'")
-	}
-
-	d.infos = append(d.infos, toStr("已更新 NLB 监听配置", updateListenerAttributeResp))
-
-	return nil
-}
diff --git a/internal/deployer/aliyun_oss.go b/internal/deployer/aliyun_oss.go
deleted file mode 100644
index d0045d89..00000000
--- a/internal/deployer/aliyun_oss.go
+++ /dev/null
@@ -1,86 +0,0 @@
-package deployer
-
-import (
-	"context"
-	"encoding/json"
-	"errors"
-	"fmt"
-
-	"github.com/aliyun/aliyun-oss-go-sdk/oss"
-	xerrors "github.com/pkg/errors"
-
-	"github.com/usual2970/certimate/internal/domain"
-)
-
-type AliyunOSSDeployer struct {
-	option *DeployerOption
-	infos  []string
-
-	sdkClient *oss.Client
-}
-
-func NewAliyunOSSDeployer(option *DeployerOption) (Deployer, error) {
-	access := &domain.AliyunAccess{}
-	if err := json.Unmarshal([]byte(option.Access), access); err != nil {
-		return nil, xerrors.Wrap(err, "failed to get access")
-	}
-
-	client, err := (&AliyunOSSDeployer{}).createSdkClient(
-		access.AccessKeyId,
-		access.AccessKeySecret,
-		option.DeployConfig.GetConfigAsString("endpoint"),
-	)
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to create sdk client")
-	}
-
-	return &AliyunOSSDeployer{
-		option:    option,
-		infos:     make([]string, 0),
-		sdkClient: client,
-	}, nil
-}
-
-func (d *AliyunOSSDeployer) GetID() string {
-	return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
-}
-
-func (d *AliyunOSSDeployer) GetInfos() []string {
-	return d.infos
-}
-
-func (d *AliyunOSSDeployer) Deploy(ctx context.Context) error {
-	aliBucket := d.option.DeployConfig.GetConfigAsString("bucket")
-	if aliBucket == "" {
-		return errors.New("`bucket` is required")
-	}
-
-	// 为存储空间绑定自定义域名
-	// REF: https://help.aliyun.com/zh/oss/developer-reference/putcname
-	err := d.sdkClient.PutBucketCnameWithCertificate(aliBucket, oss.PutBucketCname{
-		Cname: d.option.DeployConfig.GetConfigAsString("domain"),
-		CertificateConfiguration: &oss.CertificateConfiguration{
-			Certificate: d.option.Certificate.Certificate,
-			PrivateKey:  d.option.Certificate.PrivateKey,
-			Force:       true,
-		},
-	})
-	if err != nil {
-		return xerrors.Wrap(err, "failed to execute sdk request 'oss.PutBucketCnameWithCertificate'")
-	}
-
-	return nil
-}
-
-func (d *AliyunOSSDeployer) createSdkClient(accessKeyId, accessKeySecret, endpoint string) (*oss.Client, error) {
-	if endpoint == "" {
-		endpoint = "oss.aliyuncs.com"
-	}
-
-	client, err := oss.New(endpoint, accessKeyId, accessKeySecret)
-	if err != nil {
-		return nil, err
-	}
-
-	return client, nil
-}
diff --git a/internal/deployer/baiducloud_cdn.go b/internal/deployer/baiducloud_cdn.go
deleted file mode 100644
index 31d789df..00000000
--- a/internal/deployer/baiducloud_cdn.go
+++ /dev/null
@@ -1,80 +0,0 @@
-package deployer
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-	"time"
-
-	bceCdn "github.com/baidubce/bce-sdk-go/services/cdn"
-	bceCdnApi "github.com/baidubce/bce-sdk-go/services/cdn/api"
-	xerrors "github.com/pkg/errors"
-
-	"github.com/usual2970/certimate/internal/domain"
-)
-
-type BaiduCloudCDNDeployer struct {
-	option *DeployerOption
-	infos  []string
-
-	sdkClient *bceCdn.Client
-}
-
-func NewBaiduCloudCDNDeployer(option *DeployerOption) (Deployer, error) {
-	access := &domain.BaiduCloudAccess{}
-	if err := json.Unmarshal([]byte(option.Access), access); err != nil {
-		return nil, xerrors.Wrap(err, "failed to get access")
-	}
-
-	client, err := (&BaiduCloudCDNDeployer{}).createSdkClient(
-		access.AccessKeyId,
-		access.SecretAccessKey,
-	)
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to create sdk client")
-	}
-
-	return &BaiduCloudCDNDeployer{
-		option:    option,
-		infos:     make([]string, 0),
-		sdkClient: client,
-	}, nil
-}
-
-func (d *BaiduCloudCDNDeployer) GetID() string {
-	return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
-}
-
-func (d *BaiduCloudCDNDeployer) GetInfos() []string {
-	return d.infos
-}
-
-func (d *BaiduCloudCDNDeployer) Deploy(ctx context.Context) error {
-	// 修改域名证书
-	// REF: https://cloud.baidu.com/doc/CDN/s/qjzuz2hp8
-	putCertResp, err := d.sdkClient.PutCert(
-		d.option.DeployConfig.GetConfigAsString("domain"),
-		&bceCdnApi.UserCertificate{
-			CertName:    fmt.Sprintf("certimate-%d", time.Now().UnixMilli()),
-			ServerData:  d.option.Certificate.Certificate,
-			PrivateData: d.option.Certificate.PrivateKey,
-		},
-		"ON",
-	)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to execute sdk request 'cdn.PutCert'")
-	}
-
-	d.infos = append(d.infos, toStr("已修改域名证书", putCertResp))
-
-	return nil
-}
-
-func (d *BaiduCloudCDNDeployer) createSdkClient(accessKeyId, secretAccessKey string) (*bceCdn.Client, error) {
-	client, err := bceCdn.NewClient(accessKeyId, secretAccessKey, "")
-	if err != nil {
-		return nil, err
-	}
-
-	return client, nil
-}
diff --git a/internal/deployer/byteplus_cdn.go b/internal/deployer/byteplus_cdn.go
deleted file mode 100644
index 17c51522..00000000
--- a/internal/deployer/byteplus_cdn.go
+++ /dev/null
@@ -1,116 +0,0 @@
-package deployer
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-	"strings"
-
-	bytepluscdn "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/byteplus-cdn"
-
-	"github.com/byteplus-sdk/byteplus-sdk-golang/service/cdn"
-	xerrors "github.com/pkg/errors"
-	"github.com/usual2970/certimate/internal/domain"
-	"github.com/usual2970/certimate/internal/pkg/core/uploader"
-)
-
-type ByteplusCDNDeployer struct {
-	option      *DeployerOption
-	infos       []string
-	sdkClient   *cdn.CDN
-	sslUploader uploader.Uploader
-}
-
-func NewByteplusCDNDeployer(option *DeployerOption) (Deployer, error) {
-	access := &domain.ByteplusAccess{}
-	if err := json.Unmarshal([]byte(option.Access), access); err != nil {
-		return nil, xerrors.Wrap(err, "failed to get access")
-	}
-	client := cdn.NewInstance()
-	client.Client.SetAccessKey(access.AccessKey)
-	client.Client.SetSecretKey(access.SecretKey)
-	uploader, err := bytepluscdn.New(&bytepluscdn.ByteplusCDNUploaderConfig{
-		AccessKey: access.AccessKey,
-		SecretKey: access.SecretKey,
-	})
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to create ssl uploader")
-	}
-	return &ByteplusCDNDeployer{
-		option:      option,
-		infos:       make([]string, 0),
-		sdkClient:   client,
-		sslUploader: uploader,
-	}, nil
-}
-
-func (d *ByteplusCDNDeployer) GetID() string {
-	return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
-}
-
-func (d *ByteplusCDNDeployer) GetInfos() []string {
-	return d.infos
-}
-
-func (d *ByteplusCDNDeployer) Deploy(ctx context.Context) error {
-	apiCtx := context.Background()
-	// 上传证书
-	upres, err := d.sslUploader.Upload(apiCtx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
-	if err != nil {
-		return err
-	}
-
-	d.infos = append(d.infos, toStr("已上传证书", upres))
-
-	domains := make([]string, 0)
-	configDomain := d.option.DeployConfig.GetConfigAsString("domain")
-	if strings.HasPrefix(configDomain, "*.") {
-		// 获取证书可以部署的域名
-		// REF: https://docs.byteplus.com/en/docs/byteplus-cdn/reference-describecertconfig-9ea17
-		describeCertConfigReq := &cdn.DescribeCertConfigRequest{
-			CertId: upres.CertId,
-		}
-		describeCertConfigResp, err := d.sdkClient.DescribeCertConfig(describeCertConfigReq)
-		if err != nil {
-			return xerrors.Wrap(err, "failed to execute sdk request 'cdn.DescribeCertConfig'")
-		}
-		for i := range describeCertConfigResp.Result.CertNotConfig {
-			// 当前未启用 HTTPS 的加速域名列表。
-			domains = append(domains, describeCertConfigResp.Result.CertNotConfig[i].Domain)
-		}
-		for i := range describeCertConfigResp.Result.OtherCertConfig {
-			// 已启用了 HTTPS 的加速域名列表。这些加速域名关联的证书不是您指定的证书。
-			domains = append(domains, describeCertConfigResp.Result.OtherCertConfig[i].Domain)
-		}
-		for i := range describeCertConfigResp.Result.SpecifiedCertConfig {
-			// 已启用了 HTTPS 的加速域名列表。这些加速域名关联了您指定的证书。
-			d.infos = append(d.infos, fmt.Sprintf("%s域名已配置该证书", describeCertConfigResp.Result.SpecifiedCertConfig[i].Domain))
-		}
-		if len(domains) == 0 {
-			if len(describeCertConfigResp.Result.SpecifiedCertConfig) > 0 {
-				// 所有匹配的域名都配置了该证书,跳过部署
-				return nil
-			} else {
-				return xerrors.Errorf("未查询到匹配的域名: %s", configDomain)
-			}
-		}
-	} else {
-		domains = append(domains, configDomain)
-	}
-	// 部署证书
-	// REF: https://github.com/byteplus-sdk/byteplus-sdk-golang/blob/master/service/cdn/api_list.go#L306
-	for i := range domains {
-		batchDeployCertReq := &cdn.BatchDeployCertRequest{
-			CertId: upres.CertId,
-			Domain: domains[i],
-		}
-		batchDeployCertResp, err := d.sdkClient.BatchDeployCert(batchDeployCertReq)
-		if err != nil {
-			return xerrors.Wrap(err, "failed to execute sdk request 'cdn.BatchDeployCert'")
-		} else {
-			d.infos = append(d.infos, toStr(fmt.Sprintf("%s域名的证书已修改", domains[i]), batchDeployCertResp))
-		}
-	}
-
-	return nil
-}
diff --git a/internal/deployer/deployer.go b/internal/deployer/deployer.go
index a467694d..c5013d9d 100644
--- a/internal/deployer/deployer.go
+++ b/internal/deployer/deployer.go
@@ -2,14 +2,13 @@ package deployer
 
 import (
 	"context"
-	"encoding/json"
-	"errors"
 	"fmt"
 
 	"github.com/pocketbase/pocketbase/models"
 
 	"github.com/usual2970/certimate/internal/applicant"
 	"github.com/usual2970/certimate/internal/domain"
+	"github.com/usual2970/certimate/internal/pkg/core/deployer"
 	"github.com/usual2970/certimate/internal/repository"
 )
 
@@ -79,7 +78,7 @@ func Gets(record *models.Record, cert *applicant.Certificate) ([]Deployer, error
 	}
 
 	for _, deployConfig := range deployConfigs {
-		deployer, err := getWithDeployConfig(record, cert, deployConfig)
+		deployer, err := newWithDeployConfig(record, cert, deployConfig)
 		if err != nil {
 			return nil, err
 		}
@@ -91,10 +90,10 @@ func Gets(record *models.Record, cert *applicant.Certificate) ([]Deployer, error
 }
 
 func GetWithTypeAndOption(deployType string, option *DeployerOption) (Deployer, error) {
-	return getWithTypeAndOption(deployType, option)
+	return newWithTypeAndOption(deployType, option)
 }
 
-func getWithDeployConfig(record *models.Record, cert *applicant.Certificate, deployConfig domain.DeployConfig) (Deployer, error) {
+func newWithDeployConfig(record *models.Record, cert *applicant.Certificate, deployConfig domain.DeployConfig) (Deployer, error) {
 	accessRepo := repository.NewAccessRepository()
 	access, err := accessRepo.GetById(context.Background(), deployConfig.Access)
 	if err != nil {
@@ -117,65 +116,38 @@ func getWithDeployConfig(record *models.Record, cert *applicant.Certificate, dep
 		}
 	}
 
-	return getWithTypeAndOption(deployConfig.Type, option)
+	return newWithTypeAndOption(deployConfig.Type, option)
 }
 
-func getWithTypeAndOption(deployType string, option *DeployerOption) (Deployer, error) {
-	switch deployType {
-	case targetAliyunOSS:
-		return NewAliyunOSSDeployer(option)
-	case targetAliyunCDN:
-		return NewAliyunCDNDeployer(option)
-	case targetAliyunDCDN:
-		return NewAliyunDCDNDeployer(option)
-	case targetAliyunCLB:
-		return NewAliyunCLBDeployer(option)
-	case targetAliyunALB:
-		return NewAliyunALBDeployer(option)
-	case targetAliyunNLB:
-		return NewAliyunNLBDeployer(option)
-	case targetTencentCloudCDN:
-		return NewTencentCDNDeployer(option)
-	case targetTencentCloudECDN:
-		return NewTencentECDNDeployer(option)
-	case targetTencentCloudCLB:
-		return NewTencentCLBDeployer(option)
-	case targetTencentCloudCOS:
-		return NewTencentCOSDeployer(option)
-	case targetTencentCloudEO:
-		return NewTencentTEODeployer(option)
-	case targetHuaweiCloudCDN:
-		return NewHuaweiCloudCDNDeployer(option)
-	case targetHuaweiCloudELB:
-		return NewHuaweiCloudELBDeployer(option)
-	case targetBaiduCloudCDN:
-		return NewBaiduCloudCDNDeployer(option)
-	case targetQiniuCDN:
-		return NewQiniuCDNDeployer(option)
-	case targetDogeCloudCDN:
-		return NewDogeCloudCDNDeployer(option)
-	case targetLocal:
-		return NewLocalDeployer(option)
-	case targetSSH:
-		return NewSSHDeployer(option)
-	case targetWebhook:
-		return NewWebhookDeployer(option)
-	case targetK8sSecret:
-		return NewK8sSecretDeployer(option)
-	case targetVolcEngineLive:
-		return NewVolcengineLiveDeployer(option)
-	case targetVolcEngineCDN:
-		return NewVolcengineCDNDeployer(option)
-	case targetBytePlusCDN:
-		return NewByteplusCDNDeployer(option)
+func newWithTypeAndOption(deployType string, option *DeployerOption) (Deployer, error) {
+	deployer, logger, err := createDeployer(deployType, option.AccessRecord.Config, option.DeployConfig.Config)
+	if err != nil {
+		return nil, err
 	}
-	return nil, errors.New("unsupported deploy target")
+
+	return &proxyDeployer{
+		option:   option,
+		logger:   logger,
+		deployer: deployer,
+	}, nil
 }
 
-func toStr(tag string, data any) string {
-	if data == nil {
-		return tag
-	}
-	byts, _ := json.Marshal(data)
-	return tag + ":" + string(byts)
+// TODO: 暂时使用代理模式以兼容之前版本代码,后续重新实现此处逻辑
+type proxyDeployer struct {
+	option   *DeployerOption
+	logger   deployer.Logger
+	deployer deployer.Deployer
+}
+
+func (d *proxyDeployer) GetID() string {
+	return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
+}
+
+func (d *proxyDeployer) GetInfos() []string {
+	return d.logger.GetRecords()
+}
+
+func (d *proxyDeployer) Deploy(ctx context.Context) error {
+	_, err := d.deployer.Deploy(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
+	return err
 }
diff --git a/internal/deployer/dogecloud_cdn.go b/internal/deployer/dogecloud_cdn.go
deleted file mode 100644
index 55e526c1..00000000
--- a/internal/deployer/dogecloud_cdn.go
+++ /dev/null
@@ -1,88 +0,0 @@
-package deployer
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-	"strconv"
-
-	xerrors "github.com/pkg/errors"
-
-	"github.com/usual2970/certimate/internal/domain"
-	"github.com/usual2970/certimate/internal/pkg/core/uploader"
-	uploaderDoge "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/dogecloud"
-	doge "github.com/usual2970/certimate/internal/pkg/vendors/dogecloud-sdk"
-)
-
-type DogeCloudCDNDeployer struct {
-	option *DeployerOption
-	infos  []string
-
-	sdkClient   *doge.Client
-	sslUploader uploader.Uploader
-}
-
-func NewDogeCloudCDNDeployer(option *DeployerOption) (Deployer, error) {
-	access := &domain.DogeCloudAccess{}
-	if err := json.Unmarshal([]byte(option.Access), access); err != nil {
-		return nil, xerrors.Wrap(err, "failed to get access")
-	}
-
-	client, err := (&DogeCloudCDNDeployer{}).createSdkClient(
-		access.AccessKey,
-		access.SecretKey,
-	)
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to create sdk client")
-	}
-
-	uploader, err := uploaderDoge.New(&uploaderDoge.DogeCloudUploaderConfig{
-		AccessKey: access.AccessKey,
-		SecretKey: access.SecretKey,
-	})
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to create ssl uploader")
-	}
-
-	return &DogeCloudCDNDeployer{
-		option:      option,
-		infos:       make([]string, 0),
-		sdkClient:   client,
-		sslUploader: uploader,
-	}, nil
-}
-
-func (d *DogeCloudCDNDeployer) GetID() string {
-	return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
-}
-
-func (d *DogeCloudCDNDeployer) GetInfos() []string {
-	return d.infos
-}
-
-func (d *DogeCloudCDNDeployer) Deploy(ctx context.Context) error {
-	// 上传证书到 CDN
-	upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
-	if err != nil {
-		return err
-	}
-
-	d.infos = append(d.infos, toStr("已上传证书", upres))
-
-	// 绑定证书
-	// REF: https://docs.dogecloud.com/cdn/api-cert-bind
-	bindCdnCertId, _ := strconv.ParseInt(upres.CertId, 10, 64)
-	bindCdnCertResp, err := d.sdkClient.BindCdnCertWithDomain(bindCdnCertId, d.option.DeployConfig.GetConfigAsString("domain"))
-	if err != nil {
-		return xerrors.Wrap(err, "failed to execute sdk request 'cdn.BindCdnCert'")
-	}
-
-	d.infos = append(d.infos, toStr("已绑定证书", bindCdnCertResp))
-
-	return nil
-}
-
-func (d *DogeCloudCDNDeployer) createSdkClient(accessKey, secretKey string) (*doge.Client, error) {
-	client := doge.NewClient(accessKey, secretKey)
-	return client, nil
-}
diff --git a/internal/deployer/factory.go b/internal/deployer/factory.go
index d0ab4621..f4fde3e7 100644
--- a/internal/deployer/factory.go
+++ b/internal/deployer/factory.go
@@ -362,9 +362,23 @@ func createDeployer(target string, accessConfig string, deployConfig map[string]
 				return nil, nil, fmt.Errorf("failed to unmarshal access config: %w", err)
 			}
 
+			variables := make(map[string]string)
+			if deployConfig != nil {
+				value, ok := deployConfig["variables"]
+				if ok {
+					kvs := make([]domain.KV, 0)
+					bts, _ := json.Marshal(value)
+					if err := json.Unmarshal(bts, &kvs); err == nil {
+						for _, kv := range kvs {
+							variables[kv.Key] = kv.Value
+						}
+					}
+				}
+			}
+
 			deployer, err := providerWebhook.NewWithLogger(&providerWebhook.WebhookDeployerConfig{
 				Url:       access.Url,
-				Variables: nil, // TODO: 尚未实现
+				Variables: variables,
 			}, logger)
 			return deployer, logger, err
 		}
diff --git a/internal/deployer/huaweicloud_cdn.go b/internal/deployer/huaweicloud_cdn.go
deleted file mode 100644
index d3d4014f..00000000
--- a/internal/deployer/huaweicloud_cdn.go
+++ /dev/null
@@ -1,143 +0,0 @@
-package deployer
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-
-	"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/global"
-	hcCdn "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/cdn/v2"
-	hcCdnModel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/cdn/v2/model"
-	hcCdnRegion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/cdn/v2/region"
-	xerrors "github.com/pkg/errors"
-
-	"github.com/usual2970/certimate/internal/domain"
-	"github.com/usual2970/certimate/internal/pkg/core/uploader"
-	uploaderHcScm "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/huaweicloud-scm"
-	"github.com/usual2970/certimate/internal/pkg/utils/cast"
-	hcCdnEx "github.com/usual2970/certimate/internal/pkg/vendors/huaweicloud-cdn-sdk"
-)
-
-type HuaweiCloudCDNDeployer struct {
-	option *DeployerOption
-	infos  []string
-
-	sdkClient   *hcCdnEx.Client
-	sslUploader uploader.Uploader
-}
-
-func NewHuaweiCloudCDNDeployer(option *DeployerOption) (Deployer, error) {
-	access := &domain.HuaweiCloudAccess{}
-	if err := json.Unmarshal([]byte(option.Access), access); err != nil {
-		return nil, xerrors.Wrap(err, "failed to get access")
-	}
-
-	client, err := (&HuaweiCloudCDNDeployer{}).createSdkClient(
-		access.AccessKeyId,
-		access.SecretAccessKey,
-		option.DeployConfig.GetConfigAsString("region"),
-	)
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to create sdk client")
-	}
-
-	uploader, err := uploaderHcScm.New(&uploaderHcScm.HuaweiCloudSCMUploaderConfig{
-		AccessKeyId:     access.AccessKeyId,
-		SecretAccessKey: access.SecretAccessKey,
-		Region:          "",
-	})
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to create ssl uploader")
-	}
-
-	return &HuaweiCloudCDNDeployer{
-		option:      option,
-		infos:       make([]string, 0),
-		sdkClient:   client,
-		sslUploader: uploader,
-	}, nil
-}
-
-func (d *HuaweiCloudCDNDeployer) GetID() string {
-	return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
-}
-
-func (d *HuaweiCloudCDNDeployer) GetInfos() []string {
-	return d.infos
-}
-
-func (d *HuaweiCloudCDNDeployer) Deploy(ctx context.Context) error {
-	// 上传证书到 SCM
-	upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
-	if err != nil {
-		return err
-	}
-
-	d.infos = append(d.infos, toStr("已上传证书", upres))
-
-	// 查询加速域名配置
-	// REF: https://support.huaweicloud.com/api-cdn/ShowDomainFullConfig.html
-	showDomainFullConfigReq := &hcCdnModel.ShowDomainFullConfigRequest{
-		DomainName: d.option.DeployConfig.GetConfigAsString("domain"),
-	}
-	showDomainFullConfigResp, err := d.sdkClient.ShowDomainFullConfig(showDomainFullConfigReq)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to execute sdk request 'cdn.ShowDomainFullConfig'")
-	}
-
-	d.infos = append(d.infos, toStr("已查询到加速域名配置", showDomainFullConfigResp))
-
-	// 更新加速域名配置
-	// REF: https://support.huaweicloud.com/api-cdn/UpdateDomainMultiCertificates.html
-	// REF: https://support.huaweicloud.com/usermanual-cdn/cdn_01_0306.html
-	updateDomainMultiCertificatesReqBodyContent := &hcCdnEx.UpdateDomainMultiCertificatesExRequestBodyContent{}
-	updateDomainMultiCertificatesReqBodyContent.DomainName = d.option.DeployConfig.GetConfigAsString("domain")
-	updateDomainMultiCertificatesReqBodyContent.HttpsSwitch = 1
-	updateDomainMultiCertificatesReqBodyContent.CertificateType = cast.Int32Ptr(2)
-	updateDomainMultiCertificatesReqBodyContent.SCMCertificateId = cast.StringPtr(upres.CertId)
-	updateDomainMultiCertificatesReqBodyContent.CertName = cast.StringPtr(upres.CertName)
-	updateDomainMultiCertificatesReqBodyContent = updateDomainMultiCertificatesReqBodyContent.MergeConfig(showDomainFullConfigResp.Configs)
-	updateDomainMultiCertificatesReq := &hcCdnEx.UpdateDomainMultiCertificatesExRequest{
-		Body: &hcCdnEx.UpdateDomainMultiCertificatesExRequestBody{
-			Https: updateDomainMultiCertificatesReqBodyContent,
-		},
-	}
-	updateDomainMultiCertificatesResp, err := d.sdkClient.UploadDomainMultiCertificatesEx(updateDomainMultiCertificatesReq)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to execute sdk request 'cdn.UploadDomainMultiCertificatesEx'")
-	}
-
-	d.infos = append(d.infos, toStr("已更新加速域名配置", updateDomainMultiCertificatesResp))
-
-	return nil
-}
-
-func (d *HuaweiCloudCDNDeployer) createSdkClient(accessKeyId, secretAccessKey, region string) (*hcCdnEx.Client, error) {
-	if region == "" {
-		region = "cn-north-1" // CDN 服务默认区域:华北一北京
-	}
-
-	auth, err := global.NewCredentialsBuilder().
-		WithAk(accessKeyId).
-		WithSk(secretAccessKey).
-		SafeBuild()
-	if err != nil {
-		return nil, err
-	}
-
-	hcRegion, err := hcCdnRegion.SafeValueOf(region)
-	if err != nil {
-		return nil, err
-	}
-
-	hcClient, err := hcCdn.CdnClientBuilder().
-		WithRegion(hcRegion).
-		WithCredential(auth).
-		SafeBuild()
-	if err != nil {
-		return nil, err
-	}
-
-	client := hcCdnEx.NewClient(hcClient)
-	return client, nil
-}
diff --git a/internal/deployer/huaweicloud_elb.go b/internal/deployer/huaweicloud_elb.go
deleted file mode 100644
index ccc6f9f5..00000000
--- a/internal/deployer/huaweicloud_elb.go
+++ /dev/null
@@ -1,378 +0,0 @@
-package deployer
-
-import (
-	"context"
-	"encoding/json"
-	"errors"
-	"fmt"
-
-	"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/basic"
-	"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/global"
-	hcElb "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/elb/v3"
-	hcElbModel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/elb/v3/model"
-	hcElbRegion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/elb/v3/region"
-	hcIam "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3"
-	hcIamModel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3/model"
-	hcIamRegion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3/region"
-	xerrors "github.com/pkg/errors"
-	"golang.org/x/exp/slices"
-
-	"github.com/usual2970/certimate/internal/domain"
-	"github.com/usual2970/certimate/internal/pkg/core/uploader"
-	uploaderHcElb "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/huaweicloud-elb"
-	"github.com/usual2970/certimate/internal/pkg/utils/cast"
-)
-
-type HuaweiCloudELBDeployer struct {
-	option *DeployerOption
-	infos  []string
-
-	sdkClient   *hcElb.ElbClient
-	sslUploader uploader.Uploader
-}
-
-func NewHuaweiCloudELBDeployer(option *DeployerOption) (Deployer, error) {
-	access := &domain.HuaweiCloudAccess{}
-	if err := json.Unmarshal([]byte(option.Access), access); err != nil {
-		return nil, xerrors.Wrap(err, "failed to get access")
-	}
-
-	client, err := (&HuaweiCloudELBDeployer{}).createSdkClient(
-		access.AccessKeyId,
-		access.SecretAccessKey,
-		option.DeployConfig.GetConfigAsString("region"),
-	)
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to create sdk client")
-	}
-
-	uploader, err := uploaderHcElb.New(&uploaderHcElb.HuaweiCloudELBUploaderConfig{
-		AccessKeyId:     access.AccessKeyId,
-		SecretAccessKey: access.SecretAccessKey,
-		Region:          option.DeployConfig.GetConfigAsString("region"),
-	})
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to create ssl uploader")
-	}
-
-	return &HuaweiCloudELBDeployer{
-		option:      option,
-		infos:       make([]string, 0),
-		sdkClient:   client,
-		sslUploader: uploader,
-	}, nil
-}
-
-func (d *HuaweiCloudELBDeployer) GetID() string {
-	return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
-}
-
-func (d *HuaweiCloudELBDeployer) GetInfos() []string {
-	return d.infos
-}
-
-func (d *HuaweiCloudELBDeployer) Deploy(ctx context.Context) error {
-	switch d.option.DeployConfig.GetConfigAsString("resourceType") {
-	case "certificate":
-		// 部署到指定证书
-		if err := d.deployToCertificate(ctx); err != nil {
-			return err
-		}
-	case "loadbalancer":
-		// 部署到指定负载均衡器
-		if err := d.deployToLoadbalancer(ctx); err != nil {
-			return err
-		}
-	case "listener":
-		// 部署到指定监听器
-		if err := d.deployToListener(ctx); err != nil {
-			return err
-		}
-	default:
-		return errors.New("unsupported resource type")
-	}
-
-	return nil
-}
-
-func (d *HuaweiCloudELBDeployer) createSdkClient(accessKeyId, secretAccessKey, region string) (*hcElb.ElbClient, error) {
-	if region == "" {
-		region = "cn-north-4" // ELB 服务默认区域:华北四北京
-	}
-
-	projectId, err := (&HuaweiCloudELBDeployer{}).getSdkProjectId(
-		accessKeyId,
-		secretAccessKey,
-		region,
-	)
-	if err != nil {
-		return nil, err
-	}
-
-	auth, err := basic.NewCredentialsBuilder().
-		WithAk(accessKeyId).
-		WithSk(secretAccessKey).
-		WithProjectId(projectId).
-		SafeBuild()
-	if err != nil {
-		return nil, err
-	}
-
-	hcRegion, err := hcElbRegion.SafeValueOf(region)
-	if err != nil {
-		return nil, err
-	}
-
-	hcClient, err := hcElb.ElbClientBuilder().
-		WithRegion(hcRegion).
-		WithCredential(auth).
-		SafeBuild()
-	if err != nil {
-		return nil, err
-	}
-
-	client := hcElb.NewElbClient(hcClient)
-	return client, nil
-}
-
-func (u *HuaweiCloudELBDeployer) getSdkProjectId(accessKeyId, secretAccessKey, region string) (string, error) {
-	if region == "" {
-		region = "cn-north-4" // IAM 服务默认区域:华北四北京
-	}
-
-	auth, err := global.NewCredentialsBuilder().
-		WithAk(accessKeyId).
-		WithSk(secretAccessKey).
-		SafeBuild()
-	if err != nil {
-		return "", err
-	}
-
-	hcRegion, err := hcIamRegion.SafeValueOf(region)
-	if err != nil {
-		return "", err
-	}
-
-	hcClient, err := hcIam.IamClientBuilder().
-		WithRegion(hcRegion).
-		WithCredential(auth).
-		SafeBuild()
-	if err != nil {
-		return "", err
-	}
-
-	client := hcIam.NewIamClient(hcClient)
-
-	request := &hcIamModel.KeystoneListProjectsRequest{
-		Name: &region,
-	}
-	response, err := client.KeystoneListProjects(request)
-	if err != nil {
-		return "", err
-	} else if response.Projects == nil || len(*response.Projects) == 0 {
-		return "", errors.New("no project found")
-	}
-
-	return (*response.Projects)[0].Id, nil
-}
-
-func (d *HuaweiCloudELBDeployer) deployToCertificate(ctx context.Context) error {
-	hcCertId := d.option.DeployConfig.GetConfigAsString("certificateId")
-	if hcCertId == "" {
-		return errors.New("`certificateId` is required")
-	}
-
-	// 更新证书
-	// REF: https://support.huaweicloud.com/api-elb/UpdateCertificate.html
-	updateCertificateReq := &hcElbModel.UpdateCertificateRequest{
-		CertificateId: hcCertId,
-		Body: &hcElbModel.UpdateCertificateRequestBody{
-			Certificate: &hcElbModel.UpdateCertificateOption{
-				Certificate: cast.StringPtr(d.option.Certificate.Certificate),
-				PrivateKey:  cast.StringPtr(d.option.Certificate.PrivateKey),
-			},
-		},
-	}
-	updateCertificateResp, err := d.sdkClient.UpdateCertificate(updateCertificateReq)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to execute sdk request 'elb.UpdateCertificate'")
-	}
-
-	d.infos = append(d.infos, toStr("已更新 ELB 证书", updateCertificateResp))
-
-	return nil
-}
-
-func (d *HuaweiCloudELBDeployer) deployToLoadbalancer(ctx context.Context) error {
-	hcLoadbalancerId := d.option.DeployConfig.GetConfigAsString("loadbalancerId")
-	if hcLoadbalancerId == "" {
-		return errors.New("`loadbalancerId` is required")
-	}
-
-	hcListenerIds := make([]string, 0)
-
-	// 查询负载均衡器详情
-	// REF: https://support.huaweicloud.com/api-elb/ShowLoadBalancer.html
-	showLoadBalancerReq := &hcElbModel.ShowLoadBalancerRequest{
-		LoadbalancerId: hcLoadbalancerId,
-	}
-	showLoadBalancerResp, err := d.sdkClient.ShowLoadBalancer(showLoadBalancerReq)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to execute sdk request 'elb.ShowLoadBalancer'")
-	}
-
-	d.infos = append(d.infos, toStr("已查询到 ELB 负载均衡器", showLoadBalancerResp))
-
-	// 查询监听器列表
-	// REF: https://support.huaweicloud.com/api-elb/ListListeners.html
-	listListenersLimit := int32(2000)
-	var listListenersMarker *string = nil
-	for {
-		listListenersReq := &hcElbModel.ListListenersRequest{
-			Limit:          cast.Int32Ptr(listListenersLimit),
-			Marker:         listListenersMarker,
-			Protocol:       &[]string{"HTTPS", "TERMINATED_HTTPS"},
-			LoadbalancerId: &[]string{showLoadBalancerResp.Loadbalancer.Id},
-		}
-		listListenersResp, err := d.sdkClient.ListListeners(listListenersReq)
-		if err != nil {
-			return xerrors.Wrap(err, "failed to execute sdk request 'elb.ListListeners'")
-		}
-
-		if listListenersResp.Listeners != nil {
-			for _, listener := range *listListenersResp.Listeners {
-				hcListenerIds = append(hcListenerIds, listener.Id)
-			}
-		}
-
-		if listListenersResp.Listeners == nil || len(*listListenersResp.Listeners) < int(listListenersLimit) {
-			break
-		} else {
-			listListenersMarker = listListenersResp.PageInfo.NextMarker
-		}
-	}
-
-	d.infos = append(d.infos, toStr("已查询到 ELB 负载均衡器下的监听器", hcListenerIds))
-
-	// 上传证书到 SCM
-	upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
-	if err != nil {
-		return err
-	}
-
-	d.infos = append(d.infos, toStr("已上传证书", upres))
-
-	// 批量更新监听器证书
-	var errs []error
-	for _, hcListenerId := range hcListenerIds {
-		if err := d.modifyListenerCertificate(ctx, hcListenerId, upres.CertId); err != nil {
-			errs = append(errs, err)
-		}
-	}
-	if len(errs) > 0 {
-		return errors.Join(errs...)
-	}
-
-	return nil
-}
-
-func (d *HuaweiCloudELBDeployer) deployToListener(ctx context.Context) error {
-	hcListenerId := d.option.DeployConfig.GetConfigAsString("listenerId")
-	if hcListenerId == "" {
-		return errors.New("`listenerId` is required")
-	}
-
-	// 上传证书到 SCM
-	upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
-	if err != nil {
-		return err
-	}
-
-	d.infos = append(d.infos, toStr("已上传证书", upres))
-
-	// 更新监听器证书
-	if err := d.modifyListenerCertificate(ctx, hcListenerId, upres.CertId); err != nil {
-		return err
-	}
-
-	return nil
-}
-
-func (d *HuaweiCloudELBDeployer) modifyListenerCertificate(ctx context.Context, hcListenerId string, hcCertId string) error {
-	// 查询监听器详情
-	// REF: https://support.huaweicloud.com/api-elb/ShowListener.html
-	showListenerReq := &hcElbModel.ShowListenerRequest{
-		ListenerId: hcListenerId,
-	}
-	showListenerResp, err := d.sdkClient.ShowListener(showListenerReq)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to execute sdk request 'elb.ShowListener'")
-	}
-
-	d.infos = append(d.infos, toStr("已查询到 ELB 监听器", showListenerResp))
-
-	// 更新监听器
-	// REF: https://support.huaweicloud.com/api-elb/UpdateListener.html
-	updateListenerReq := &hcElbModel.UpdateListenerRequest{
-		ListenerId: hcListenerId,
-		Body: &hcElbModel.UpdateListenerRequestBody{
-			Listener: &hcElbModel.UpdateListenerOption{
-				DefaultTlsContainerRef: cast.StringPtr(hcCertId),
-			},
-		},
-	}
-	if showListenerResp.Listener.SniContainerRefs != nil {
-		if len(showListenerResp.Listener.SniContainerRefs) > 0 {
-			// 如果开启 SNI,需替换同 SAN 的证书
-			sniCertIds := make([]string, 0)
-			sniCertIds = append(sniCertIds, hcCertId)
-
-			listOldCertificateReq := &hcElbModel.ListCertificatesRequest{
-				Id: &showListenerResp.Listener.SniContainerRefs,
-			}
-			listOldCertificateResp, err := d.sdkClient.ListCertificates(listOldCertificateReq)
-			if err != nil {
-				return xerrors.Wrap(err, "failed to execute sdk request 'elb.ListCertificates'")
-			}
-
-			showNewCertificateReq := &hcElbModel.ShowCertificateRequest{
-				CertificateId: hcCertId,
-			}
-			showNewCertificateResp, err := d.sdkClient.ShowCertificate(showNewCertificateReq)
-			if err != nil {
-				return xerrors.Wrap(err, "failed to execute sdk request 'elb.ShowCertificate'")
-			}
-
-			for _, certificate := range *listOldCertificateResp.Certificates {
-				oldCertificate := certificate
-				newCertificate := showNewCertificateResp.Certificate
-
-				if oldCertificate.SubjectAlternativeNames != nil && newCertificate.SubjectAlternativeNames != nil {
-					if slices.Equal(*oldCertificate.SubjectAlternativeNames, *newCertificate.SubjectAlternativeNames) {
-						continue
-					}
-				} else {
-					if oldCertificate.Domain == newCertificate.Domain {
-						continue
-					}
-				}
-
-				sniCertIds = append(sniCertIds, certificate.Id)
-			}
-
-			updateListenerReq.Body.Listener.SniContainerRefs = &sniCertIds
-		}
-
-		if showListenerResp.Listener.SniMatchAlgo != "" {
-			updateListenerReq.Body.Listener.SniMatchAlgo = cast.StringPtr(showListenerResp.Listener.SniMatchAlgo)
-		}
-	}
-	updateListenerResp, err := d.sdkClient.UpdateListener(updateListenerReq)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to execute sdk request 'elb.UpdateListener'")
-	}
-
-	d.infos = append(d.infos, toStr("已更新 ELB 监听器", updateListenerResp))
-
-	return nil
-}
diff --git a/internal/deployer/k8s_secret.go b/internal/deployer/k8s_secret.go
deleted file mode 100644
index 8a1c30ff..00000000
--- a/internal/deployer/k8s_secret.go
+++ /dev/null
@@ -1,136 +0,0 @@
-package deployer
-
-import (
-	"context"
-	"encoding/json"
-	"errors"
-	"fmt"
-	"strings"
-
-	xerrors "github.com/pkg/errors"
-	k8sCore "k8s.io/api/core/v1"
-	k8sMeta "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/client-go/kubernetes"
-	"k8s.io/client-go/rest"
-	"k8s.io/client-go/tools/clientcmd"
-
-	"github.com/usual2970/certimate/internal/domain"
-	"github.com/usual2970/certimate/internal/pkg/utils/x509"
-)
-
-type K8sSecretDeployer struct {
-	option *DeployerOption
-	infos  []string
-
-	k8sClient *kubernetes.Clientset
-}
-
-func NewK8sSecretDeployer(option *DeployerOption) (Deployer, error) {
-	access := &domain.KubernetesAccess{}
-	if err := json.Unmarshal([]byte(option.Access), access); err != nil {
-		return nil, xerrors.Wrap(err, "failed to get access")
-	}
-
-	client, err := (&K8sSecretDeployer{}).createK8sClient(access)
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to create k8s client")
-	}
-
-	return &K8sSecretDeployer{
-		option:    option,
-		infos:     make([]string, 0),
-		k8sClient: client,
-	}, nil
-}
-
-func (d *K8sSecretDeployer) GetID() string {
-	return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
-}
-
-func (d *K8sSecretDeployer) GetInfos() []string {
-	return d.infos
-}
-
-func (d *K8sSecretDeployer) Deploy(ctx context.Context) error {
-	namespace := d.option.DeployConfig.GetConfigAsString("namespace")
-	secretName := d.option.DeployConfig.GetConfigAsString("secretName")
-	secretDataKeyForCrt := d.option.DeployConfig.GetConfigOrDefaultAsString("secretDataKeyForCrt", "tls.crt")
-	secretDataKeyForKey := d.option.DeployConfig.GetConfigOrDefaultAsString("secretDataKeyForKey", "tls.key")
-	if namespace == "" {
-		namespace = "default"
-	}
-	if secretName == "" {
-		return errors.New("`secretName` is required")
-	}
-
-	certX509, err := x509.ParseCertificateFromPEM(d.option.Certificate.Certificate)
-	if err != nil {
-		return err
-	}
-
-	secretPayload := k8sCore.Secret{
-		TypeMeta: k8sMeta.TypeMeta{
-			Kind:       "Secret",
-			APIVersion: "v1",
-		},
-		ObjectMeta: k8sMeta.ObjectMeta{
-			Name: secretName,
-			Annotations: map[string]string{
-				"certimate/domains":             d.option.Domain,
-				"certimate/alt-names":           strings.Join(certX509.DNSNames, ","),
-				"certimate/common-name":         certX509.Subject.CommonName,
-				"certimate/issuer-organization": strings.Join(certX509.Issuer.Organization, ","),
-			},
-		},
-		Type: k8sCore.SecretType("kubernetes.io/tls"),
-	}
-	secretPayload.Data = make(map[string][]byte)
-	secretPayload.Data[secretDataKeyForCrt] = []byte(d.option.Certificate.Certificate)
-	secretPayload.Data[secretDataKeyForKey] = []byte(d.option.Certificate.PrivateKey)
-
-	// 获取 Secret 实例
-	_, err = d.k8sClient.CoreV1().Secrets(namespace).Get(context.TODO(), secretName, k8sMeta.GetOptions{})
-	if err != nil {
-		_, err = d.k8sClient.CoreV1().Secrets(namespace).Create(context.TODO(), &secretPayload, k8sMeta.CreateOptions{})
-		if err != nil {
-			return xerrors.Wrap(err, "failed to create k8s secret")
-		} else {
-			d.infos = append(d.infos, toStr("Certificate has been created in K8s Secret", nil))
-			return nil
-		}
-	}
-
-	// 更新 Secret 实例
-	_, err = d.k8sClient.CoreV1().Secrets(namespace).Update(context.TODO(), &secretPayload, k8sMeta.UpdateOptions{})
-	if err != nil {
-		return xerrors.Wrap(err, "failed to update k8s secret")
-	}
-
-	d.infos = append(d.infos, toStr("Certificate has been updated to K8s Secret", nil))
-
-	return nil
-}
-
-func (d *K8sSecretDeployer) createK8sClient(access *domain.KubernetesAccess) (*kubernetes.Clientset, error) {
-	var config *rest.Config
-	var err error
-	if access.KubeConfig == "" {
-		config, err = rest.InClusterConfig()
-	} else {
-		kubeConfig, err := clientcmd.NewClientConfigFromBytes([]byte(access.KubeConfig))
-		if err != nil {
-			return nil, err
-		}
-		config, err = kubeConfig.ClientConfig()
-	}
-	if err != nil {
-		return nil, err
-	}
-
-	client, err := kubernetes.NewForConfig(config)
-	if err != nil {
-		return nil, err
-	}
-
-	return client, nil
-}
diff --git a/internal/deployer/local.go b/internal/deployer/local.go
deleted file mode 100644
index 3c693d30..00000000
--- a/internal/deployer/local.go
+++ /dev/null
@@ -1,163 +0,0 @@
-package deployer
-
-import (
-	"bytes"
-	"context"
-	"errors"
-	"fmt"
-	"os/exec"
-	"runtime"
-
-	xerrors "github.com/pkg/errors"
-
-	"github.com/usual2970/certimate/internal/pkg/utils/fs"
-	"github.com/usual2970/certimate/internal/pkg/utils/x509"
-)
-
-type LocalDeployer struct {
-	option *DeployerOption
-	infos  []string
-}
-
-const (
-	certFormatPEM = "pem"
-	certFormatPFX = "pfx"
-	certFormatJKS = "jks"
-)
-
-const (
-	shellEnvSh         = "sh"
-	shellEnvCmd        = "cmd"
-	shellEnvPowershell = "powershell"
-)
-
-func NewLocalDeployer(option *DeployerOption) (Deployer, error) {
-	return &LocalDeployer{
-		option: option,
-		infos:  make([]string, 0),
-	}, nil
-}
-
-func (d *LocalDeployer) GetID() string {
-	return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
-}
-
-func (d *LocalDeployer) GetInfos() []string {
-	return []string{}
-}
-
-func (d *LocalDeployer) Deploy(ctx context.Context) error {
-	// 执行前置命令
-	preCommand := d.option.DeployConfig.GetConfigAsString("preCommand")
-	if preCommand != "" {
-		stdout, stderr, err := d.execCommand(preCommand)
-		if err != nil {
-			return xerrors.Wrapf(err, "failed to run pre-command, stdout: %s, stderr: %s", stdout, stderr)
-		}
-
-		d.infos = append(d.infos, toStr("执行前置命令成功", stdout))
-	}
-
-	// 写入证书和私钥文件
-	switch d.option.DeployConfig.GetConfigOrDefaultAsString("format", certFormatPEM) {
-	case certFormatPEM:
-		if err := fs.WriteFileString(d.option.DeployConfig.GetConfigAsString("certPath"), d.option.Certificate.Certificate); err != nil {
-			return err
-		}
-
-		d.infos = append(d.infos, toStr("保存证书成功", nil))
-
-		if err := fs.WriteFileString(d.option.DeployConfig.GetConfigAsString("keyPath"), d.option.Certificate.PrivateKey); err != nil {
-			return err
-		}
-
-		d.infos = append(d.infos, toStr("保存私钥成功", nil))
-
-	case certFormatPFX:
-		pfxData, err := x509.TransformCertificateFromPEMToPFX(
-			d.option.Certificate.Certificate,
-			d.option.Certificate.PrivateKey,
-			d.option.DeployConfig.GetConfigAsString("pfxPassword"),
-		)
-		if err != nil {
-			return err
-		}
-
-		if err := fs.WriteFile(d.option.DeployConfig.GetConfigAsString("certPath"), pfxData); err != nil {
-			return err
-		}
-
-		d.infos = append(d.infos, toStr("保存证书成功", nil))
-
-	case certFormatJKS:
-		jksData, err := x509.TransformCertificateFromPEMToJKS(
-			d.option.Certificate.Certificate,
-			d.option.Certificate.PrivateKey,
-			d.option.DeployConfig.GetConfigAsString("jksAlias"),
-			d.option.DeployConfig.GetConfigAsString("jksKeypass"),
-			d.option.DeployConfig.GetConfigAsString("jksStorepass"),
-		)
-		if err != nil {
-			return err
-		}
-
-		if err := fs.WriteFile(d.option.DeployConfig.GetConfigAsString("certPath"), jksData); err != nil {
-			return err
-		}
-
-		d.infos = append(d.infos, toStr("保存证书成功", nil))
-
-	default:
-		return errors.New("unsupported format")
-	}
-
-	// 执行命令
-	command := d.option.DeployConfig.GetConfigAsString("command")
-	if command != "" {
-		stdout, stderr, err := d.execCommand(command)
-		if err != nil {
-			return xerrors.Wrapf(err, "failed to run command, stdout: %s, stderr: %s", stdout, stderr)
-		}
-
-		d.infos = append(d.infos, toStr("执行命令成功", stdout))
-	}
-
-	return nil
-}
-
-func (d *LocalDeployer) execCommand(command string) (string, string, error) {
-	var cmd *exec.Cmd
-
-	switch d.option.DeployConfig.GetConfigAsString("shell") {
-	case shellEnvSh:
-		cmd = exec.Command("sh", "-c", command)
-
-	case shellEnvCmd:
-		cmd = exec.Command("cmd", "/C", command)
-
-	case shellEnvPowershell:
-		cmd = exec.Command("powershell", "-Command", command)
-
-	case "":
-		if runtime.GOOS == "windows" {
-			cmd = exec.Command("cmd", "/C", command)
-		} else {
-			cmd = exec.Command("sh", "-c", command)
-		}
-
-	default:
-		return "", "", errors.New("unsupported shell")
-	}
-
-	var stdoutBuf bytes.Buffer
-	cmd.Stdout = &stdoutBuf
-	var stderrBuf bytes.Buffer
-	cmd.Stderr = &stderrBuf
-
-	err := cmd.Run()
-	if err != nil {
-		return "", "", xerrors.Wrap(err, "failed to execute shell script")
-	}
-
-	return stdoutBuf.String(), stderrBuf.String(), nil
-}
diff --git a/internal/deployer/qiniu_cdn.go b/internal/deployer/qiniu_cdn.go
deleted file mode 100644
index bc638af9..00000000
--- a/internal/deployer/qiniu_cdn.go
+++ /dev/null
@@ -1,113 +0,0 @@
-package deployer
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-	"strings"
-
-	xerrors "github.com/pkg/errors"
-	"github.com/qiniu/go-sdk/v7/auth"
-
-	"github.com/usual2970/certimate/internal/domain"
-	"github.com/usual2970/certimate/internal/pkg/core/uploader"
-	uploaderQiniu "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/qiniu-sslcert"
-	qiniuEx "github.com/usual2970/certimate/internal/pkg/vendors/qiniu-sdk"
-)
-
-type QiniuCDNDeployer struct {
-	option *DeployerOption
-	infos  []string
-
-	sdkClient   *qiniuEx.Client
-	sslUploader uploader.Uploader
-}
-
-func NewQiniuCDNDeployer(option *DeployerOption) (Deployer, error) {
-	access := &domain.QiniuAccess{}
-	if err := json.Unmarshal([]byte(option.Access), access); err != nil {
-		return nil, xerrors.Wrap(err, "failed to get access")
-	}
-
-	client, err := (&QiniuCDNDeployer{}).createSdkClient(
-		access.AccessKey,
-		access.SecretKey,
-	)
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to create sdk client")
-	}
-
-	uploader, err := uploaderQiniu.New(&uploaderQiniu.QiniuSSLCertUploaderConfig{
-		AccessKey: access.AccessKey,
-		SecretKey: access.SecretKey,
-	})
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to create ssl uploader")
-	}
-
-	return &QiniuCDNDeployer{
-		option:      option,
-		infos:       make([]string, 0),
-		sdkClient:   client,
-		sslUploader: uploader,
-	}, nil
-}
-
-func (d *QiniuCDNDeployer) GetID() string {
-	return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
-}
-
-func (d *QiniuCDNDeployer) GetInfos() []string {
-	return d.infos
-}
-
-func (d *QiniuCDNDeployer) Deploy(ctx context.Context) error {
-	// 上传证书
-	upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
-	if err != nil {
-		return err
-	}
-
-	d.infos = append(d.infos, toStr("已上传证书", upres))
-
-	// 在七牛 CDN 中泛域名表示为 .example.com,需去除前缀星号
-	domain := d.option.DeployConfig.GetConfigAsString("domain")
-	if strings.HasPrefix(domain, "*") {
-		domain = strings.TrimPrefix(domain, "*")
-	}
-
-	// 获取域名信息
-	// REF: https://developer.qiniu.com/fusion/4246/the-domain-name
-	getDomainInfoResp, err := d.sdkClient.GetDomainInfo(domain)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to execute sdk request 'cdn.GetDomainInfo'")
-	}
-
-	d.infos = append(d.infos, toStr("已获取域名信息", getDomainInfoResp))
-
-	// 判断域名是否已启用 HTTPS。如果已启用,修改域名证书;否则,启用 HTTPS
-	// REF: https://developer.qiniu.com/fusion/4246/the-domain-name
-	if getDomainInfoResp.Https != nil && getDomainInfoResp.Https.CertID != "" {
-		modifyDomainHttpsConfResp, err := d.sdkClient.ModifyDomainHttpsConf(domain, upres.CertId, getDomainInfoResp.Https.ForceHttps, getDomainInfoResp.Https.Http2Enable)
-		if err != nil {
-			return xerrors.Wrap(err, "failed to execute sdk request 'cdn.ModifyDomainHttpsConf'")
-		}
-
-		d.infos = append(d.infos, toStr("已修改域名证书", modifyDomainHttpsConfResp))
-	} else {
-		enableDomainHttpsResp, err := d.sdkClient.EnableDomainHttps(domain, upres.CertId, true, true)
-		if err != nil {
-			return xerrors.Wrap(err, "failed to execute sdk request 'cdn.EnableDomainHttps'")
-		}
-
-		d.infos = append(d.infos, toStr("已将域名升级为 HTTPS", enableDomainHttpsResp))
-	}
-
-	return nil
-}
-
-func (u *QiniuCDNDeployer) createSdkClient(accessKey, secretKey string) (*qiniuEx.Client, error) {
-	credential := auth.New(accessKey, secretKey)
-	client := qiniuEx.NewClient(credential)
-	return client, nil
-}
diff --git a/internal/deployer/ssh.go b/internal/deployer/ssh.go
deleted file mode 100644
index 96f8bdd2..00000000
--- a/internal/deployer/ssh.go
+++ /dev/null
@@ -1,209 +0,0 @@
-package deployer
-
-import (
-	"bytes"
-	"context"
-	"encoding/json"
-	"errors"
-	"fmt"
-	"os"
-	"path/filepath"
-
-	xerrors "github.com/pkg/errors"
-	"github.com/pkg/sftp"
-	"golang.org/x/crypto/ssh"
-
-	"github.com/usual2970/certimate/internal/domain"
-	"github.com/usual2970/certimate/internal/pkg/utils/x509"
-)
-
-type SSHDeployer struct {
-	option *DeployerOption
-	infos  []string
-}
-
-func NewSSHDeployer(option *DeployerOption) (Deployer, error) {
-	return &SSHDeployer{
-		option: option,
-		infos:  make([]string, 0),
-	}, nil
-}
-
-func (d *SSHDeployer) GetID() string {
-	return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
-}
-
-func (d *SSHDeployer) GetInfos() []string {
-	return d.infos
-}
-
-func (d *SSHDeployer) Deploy(ctx context.Context) error {
-	access := &domain.SSHAccess{}
-	if err := json.Unmarshal([]byte(d.option.Access), access); err != nil {
-		return err
-	}
-
-	// 连接
-	client, err := d.createSshClient(access)
-	if err != nil {
-		return err
-	}
-	defer client.Close()
-
-	d.infos = append(d.infos, toStr("SSH 连接成功", nil))
-
-	// 执行前置命令
-	preCommand := d.option.DeployConfig.GetConfigAsString("preCommand")
-	if preCommand != "" {
-		stdout, stderr, err := d.sshExecCommand(client, preCommand)
-		if err != nil {
-			return xerrors.Wrapf(err, "failed to run pre-command: stdout: %s, stderr: %s", stdout, stderr)
-		}
-
-		d.infos = append(d.infos, toStr("SSH 执行前置命令成功", stdout))
-	}
-
-	// 上传证书和私钥文件
-	switch d.option.DeployConfig.GetConfigOrDefaultAsString("format", certFormatPEM) {
-	case certFormatPEM:
-		if err := d.writeSftpFileString(client, d.option.DeployConfig.GetConfigAsString("certPath"), d.option.Certificate.Certificate); err != nil {
-			return err
-		}
-
-		d.infos = append(d.infos, toStr("SSH 上传证书成功", nil))
-
-		if err := d.writeSftpFileString(client, d.option.DeployConfig.GetConfigAsString("keyPath"), d.option.Certificate.PrivateKey); err != nil {
-			return err
-		}
-
-		d.infos = append(d.infos, toStr("SSH 上传私钥成功", nil))
-
-	case certFormatPFX:
-		pfxData, err := x509.TransformCertificateFromPEMToPFX(
-			d.option.Certificate.Certificate,
-			d.option.Certificate.PrivateKey,
-			d.option.DeployConfig.GetConfigAsString("pfxPassword"),
-		)
-		if err != nil {
-			return err
-		}
-
-		if err := d.writeSftpFile(client, d.option.DeployConfig.GetConfigAsString("certPath"), pfxData); err != nil {
-			return err
-		}
-
-		d.infos = append(d.infos, toStr("SSH 上传证书成功", nil))
-
-	case certFormatJKS:
-		jksData, err := x509.TransformCertificateFromPEMToJKS(
-			d.option.Certificate.Certificate,
-			d.option.Certificate.PrivateKey,
-			d.option.DeployConfig.GetConfigAsString("jksAlias"),
-			d.option.DeployConfig.GetConfigAsString("jksKeypass"),
-			d.option.DeployConfig.GetConfigAsString("jksStorepass"),
-		)
-		if err != nil {
-			return err
-		}
-
-		if err := d.writeSftpFile(client, d.option.DeployConfig.GetConfigAsString("certPath"), jksData); err != nil {
-			return err
-		}
-
-		d.infos = append(d.infos, toStr("SSH 上传证书成功", nil))
-
-	default:
-		return errors.New("unsupported format")
-	}
-
-	// 执行命令
-	command := d.option.DeployConfig.GetConfigAsString("command")
-	if command != "" {
-		stdout, stderr, err := d.sshExecCommand(client, command)
-		if err != nil {
-			return xerrors.Wrapf(err, "failed to run command, stdout: %s, stderr: %s", stdout, stderr)
-		}
-
-		d.infos = append(d.infos, toStr("SSH 执行命令成功", stdout))
-	}
-
-	return nil
-}
-
-func (d *SSHDeployer) createSshClient(access *domain.SSHAccess) (*ssh.Client, error) {
-	var authMethod ssh.AuthMethod
-
-	if access.Key != "" {
-		var signer ssh.Signer
-		var err error
-
-		if access.KeyPassphrase != "" {
-			signer, err = ssh.ParsePrivateKeyWithPassphrase([]byte(access.Key), []byte(access.KeyPassphrase))
-		} else {
-			signer, err = ssh.ParsePrivateKey([]byte(access.Key))
-		}
-
-		if err != nil {
-			return nil, err
-		}
-		authMethod = ssh.PublicKeys(signer)
-	} else {
-		authMethod = ssh.Password(access.Password)
-	}
-
-	return ssh.Dial("tcp", fmt.Sprintf("%s:%s", access.Host, access.Port), &ssh.ClientConfig{
-		User: access.Username,
-		Auth: []ssh.AuthMethod{
-			authMethod,
-		},
-		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
-	})
-}
-
-func (d *SSHDeployer) sshExecCommand(sshCli *ssh.Client, command string) (string, string, error) {
-	session, err := sshCli.NewSession()
-	if err != nil {
-		return "", "", xerrors.Wrap(err, "failed to create ssh session")
-	}
-
-	defer session.Close()
-	var stdoutBuf bytes.Buffer
-	session.Stdout = &stdoutBuf
-	var stderrBuf bytes.Buffer
-	session.Stderr = &stderrBuf
-	err = session.Run(command)
-	if err != nil {
-		return "", "", xerrors.Wrap(err, "failed to execute ssh script")
-	}
-
-	return stdoutBuf.String(), stderrBuf.String(), nil
-}
-
-func (d *SSHDeployer) writeSftpFileString(sshCli *ssh.Client, path string, content string) error {
-	return d.writeSftpFile(sshCli, path, []byte(content))
-}
-
-func (d *SSHDeployer) writeSftpFile(sshCli *ssh.Client, path string, data []byte) error {
-	sftpCli, err := sftp.NewClient(sshCli)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to create sftp client")
-	}
-	defer sftpCli.Close()
-
-	if err := sftpCli.MkdirAll(filepath.Dir(path)); err != nil {
-		return xerrors.Wrap(err, "failed to create remote directory")
-	}
-
-	file, err := sftpCli.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to open remote file")
-	}
-	defer file.Close()
-
-	_, err = file.Write(data)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to write to remote file")
-	}
-
-	return nil
-}
diff --git a/internal/deployer/ssh_test.go b/internal/deployer/ssh_test.go
deleted file mode 100644
index c9b1e85a..00000000
--- a/internal/deployer/ssh_test.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package deployer
-
-import (
-	"os"
-	"path"
-	"testing"
-)
-
-func TestPath(t *testing.T) {
-	dir := path.Dir("./a/b/c")
-	os.MkdirAll(dir, 0o755)
-}
diff --git a/internal/deployer/tencent_cdn.go b/internal/deployer/tencent_cdn.go
deleted file mode 100644
index f5230910..00000000
--- a/internal/deployer/tencent_cdn.go
+++ /dev/null
@@ -1,194 +0,0 @@
-package deployer
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-	"strings"
-
-	xerrors "github.com/pkg/errors"
-	tcCdn "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn/v20180606"
-	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
-	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
-	tcSsl "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl/v20191205"
-	"golang.org/x/exp/slices"
-
-	"github.com/usual2970/certimate/internal/domain"
-	"github.com/usual2970/certimate/internal/pkg/core/uploader"
-	uploaderTcSsl "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/tencentcloud-ssl"
-)
-
-type TencentCDNDeployer struct {
-	option *DeployerOption
-	infos  []string
-
-	sdkClients  *tencentCDNDeployerSdkClients
-	sslUploader uploader.Uploader
-}
-
-type tencentCDNDeployerSdkClients struct {
-	ssl *tcSsl.Client
-	cdn *tcCdn.Client
-}
-
-func NewTencentCDNDeployer(option *DeployerOption) (Deployer, error) {
-	access := &domain.TencentAccess{}
-	if err := json.Unmarshal([]byte(option.Access), access); err != nil {
-		return nil, xerrors.Wrap(err, "failed to get access")
-	}
-
-	clients, err := (&TencentCDNDeployer{}).createSdkClients(
-		access.SecretId,
-		access.SecretKey,
-	)
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to create sdk clients")
-	}
-
-	uploader, err := uploaderTcSsl.New(&uploaderTcSsl.TencentCloudSSLUploaderConfig{
-		SecretId:  access.SecretId,
-		SecretKey: access.SecretKey,
-	})
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to create ssl uploader")
-	}
-
-	return &TencentCDNDeployer{
-		option:      option,
-		infos:       make([]string, 0),
-		sdkClients:  clients,
-		sslUploader: uploader,
-	}, nil
-}
-
-func (d *TencentCDNDeployer) GetID() string {
-	return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
-}
-
-func (d *TencentCDNDeployer) GetInfos() []string {
-	return d.infos
-}
-
-func (d *TencentCDNDeployer) Deploy(ctx context.Context) error {
-	// 上传证书到 SSL
-	upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
-	if err != nil {
-		return err
-	}
-
-	d.infos = append(d.infos, toStr("已上传证书", upres))
-
-	// 获取待部署的 CDN 实例
-	// 如果是泛域名,根据证书匹配 CDN 实例
-	tcInstanceIds := make([]string, 0)
-	domain := d.option.DeployConfig.GetConfigAsString("domain")
-	if strings.HasPrefix(domain, "*") {
-		domains, err := d.getDomainsByCertificateId(upres.CertId)
-		if err != nil {
-			return err
-		}
-
-		tcInstanceIds = domains
-	} else {
-		tcInstanceIds = append(tcInstanceIds, domain)
-	}
-
-	// 跳过已部署的 CDN 实例
-	if len(tcInstanceIds) > 0 {
-		deployedDomains, err := d.getDeployedDomainsByCertificateId(upres.CertId)
-		if err != nil {
-			return err
-		}
-
-		temp := make([]string, 0)
-		for _, tcInstanceId := range tcInstanceIds {
-			if !slices.Contains(deployedDomains, tcInstanceId) {
-				temp = append(temp, tcInstanceId)
-			}
-		}
-		tcInstanceIds = temp
-	}
-	if len(tcInstanceIds) == 0 {
-		d.infos = append(d.infos, "已部署过或没有要部署的 CDN 实例")
-		return nil
-	}
-
-	// 证书部署到 CDN 实例
-	// REF: https://cloud.tencent.com/document/product/400/91667
-	deployCertificateInstanceReq := tcSsl.NewDeployCertificateInstanceRequest()
-	deployCertificateInstanceReq.CertificateId = common.StringPtr(upres.CertId)
-	deployCertificateInstanceReq.ResourceType = common.StringPtr("cdn")
-	deployCertificateInstanceReq.Status = common.Int64Ptr(1)
-	deployCertificateInstanceReq.InstanceIdList = common.StringPtrs(tcInstanceIds)
-	deployCertificateInstanceResp, err := d.sdkClients.ssl.DeployCertificateInstance(deployCertificateInstanceReq)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to execute sdk request 'ssl.DeployCertificateInstance'")
-	}
-
-	d.infos = append(d.infos, toStr("已部署证书到云资源实例", deployCertificateInstanceResp.Response))
-
-	return nil
-}
-
-func (d *TencentCDNDeployer) createSdkClients(secretId, secretKey string) (*tencentCDNDeployerSdkClients, error) {
-	credential := common.NewCredential(secretId, secretKey)
-
-	sslClient, err := tcSsl.NewClient(credential, "", profile.NewClientProfile())
-	if err != nil {
-		return nil, err
-	}
-
-	cdnClient, err := tcCdn.NewClient(credential, "", profile.NewClientProfile())
-	if err != nil {
-		return nil, err
-	}
-
-	return &tencentCDNDeployerSdkClients{
-		ssl: sslClient,
-		cdn: cdnClient,
-	}, nil
-}
-
-func (d *TencentCDNDeployer) getDomainsByCertificateId(tcCertId string) ([]string, error) {
-	// 获取证书中的可用域名
-	// REF: https://cloud.tencent.com/document/product/228/42491
-	describeCertDomainsReq := tcCdn.NewDescribeCertDomainsRequest()
-	describeCertDomainsReq.CertId = common.StringPtr(tcCertId)
-	describeCertDomainsReq.Product = common.StringPtr("cdn")
-	describeCertDomainsResp, err := d.sdkClients.cdn.DescribeCertDomains(describeCertDomainsReq)
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.DescribeCertDomains'")
-	}
-
-	domains := make([]string, 0)
-	if describeCertDomainsResp.Response.Domains == nil {
-		for _, domain := range describeCertDomainsResp.Response.Domains {
-			domains = append(domains, *domain)
-		}
-	}
-
-	return domains, nil
-}
-
-func (d *TencentCDNDeployer) getDeployedDomainsByCertificateId(tcCertId string) ([]string, error) {
-	// 根据证书查询关联 CDN 域名
-	// REF: https://cloud.tencent.com/document/product/400/62674
-	describeDeployedResourcesReq := tcSsl.NewDescribeDeployedResourcesRequest()
-	describeDeployedResourcesReq.CertificateIds = common.StringPtrs([]string{tcCertId})
-	describeDeployedResourcesReq.ResourceType = common.StringPtr("cdn")
-	describeDeployedResourcesResp, err := d.sdkClients.ssl.DescribeDeployedResources(describeDeployedResourcesReq)
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.DescribeDeployedResources'")
-	}
-
-	domains := make([]string, 0)
-	if describeDeployedResourcesResp.Response.DeployedResources != nil {
-		for _, deployedResource := range describeDeployedResourcesResp.Response.DeployedResources {
-			for _, resource := range deployedResource.Resources {
-				domains = append(domains, *resource)
-			}
-		}
-	}
-
-	return domains, nil
-}
diff --git a/internal/deployer/tencent_clb.go b/internal/deployer/tencent_clb.go
deleted file mode 100644
index c3feefc7..00000000
--- a/internal/deployer/tencent_clb.go
+++ /dev/null
@@ -1,328 +0,0 @@
-package deployer
-
-import (
-	"context"
-	"encoding/json"
-	"errors"
-	"fmt"
-
-	xerrors "github.com/pkg/errors"
-	tcClb "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb/v20180317"
-	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
-	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
-	tcSsl "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl/v20191205"
-
-	"github.com/usual2970/certimate/internal/domain"
-	"github.com/usual2970/certimate/internal/pkg/core/uploader"
-	uploaderTcSsl "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/tencentcloud-ssl"
-)
-
-type TencentCLBDeployer struct {
-	option *DeployerOption
-	infos  []string
-
-	sdkClients  *tencentCLBDeployerSdkClients
-	sslUploader uploader.Uploader
-}
-
-type tencentCLBDeployerSdkClients struct {
-	ssl *tcSsl.Client
-	clb *tcClb.Client
-}
-
-func NewTencentCLBDeployer(option *DeployerOption) (Deployer, error) {
-	access := &domain.TencentAccess{}
-	if err := json.Unmarshal([]byte(option.Access), access); err != nil {
-		return nil, xerrors.Wrap(err, "failed to get access")
-	}
-
-	clients, err := (&TencentCLBDeployer{}).createSdkClients(
-		access.SecretId,
-		access.SecretKey,
-		option.DeployConfig.GetConfigAsString("region"),
-	)
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to create sdk clients")
-	}
-
-	uploader, err := uploaderTcSsl.New(&uploaderTcSsl.TencentCloudSSLUploaderConfig{
-		SecretId:  access.SecretId,
-		SecretKey: access.SecretKey,
-	})
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to create ssl uploader")
-	}
-
-	return &TencentCLBDeployer{
-		option:      option,
-		infos:       make([]string, 0),
-		sdkClients:  clients,
-		sslUploader: uploader,
-	}, nil
-}
-
-func (d *TencentCLBDeployer) GetID() string {
-	return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
-}
-
-func (d *TencentCLBDeployer) GetInfos() []string {
-	return d.infos
-}
-
-func (d *TencentCLBDeployer) Deploy(ctx context.Context) error {
-	switch d.option.DeployConfig.GetConfigAsString("resourceType") {
-	case "ssl-deploy":
-		// 通过 SSL 服务部署到云资源实例
-		err := d.deployToInstanceUseSsl(ctx)
-		if err != nil {
-			return err
-		}
-	case "loadbalancer":
-		// 部署到指定负载均衡器
-		if err := d.deployToLoadbalancer(ctx); err != nil {
-			return err
-		}
-	case "listener":
-		// 部署到指定监听器
-		if err := d.deployToListener(ctx); err != nil {
-			return err
-		}
-	case "ruledomain":
-		// 部署到指定七层监听转发规则域名
-		if err := d.deployToRuleDomain(ctx); err != nil {
-			return err
-		}
-	default:
-		return errors.New("unsupported resource type")
-	}
-
-	return nil
-}
-
-func (d *TencentCLBDeployer) createSdkClients(secretId, secretKey, region string) (*tencentCLBDeployerSdkClients, error) {
-	credential := common.NewCredential(secretId, secretKey)
-
-	sslClient, err := tcSsl.NewClient(credential, region, profile.NewClientProfile())
-	if err != nil {
-		return nil, err
-	}
-
-	clbClient, err := tcClb.NewClient(credential, region, profile.NewClientProfile())
-	if err != nil {
-		return nil, err
-	}
-
-	return &tencentCLBDeployerSdkClients{
-		ssl: sslClient,
-		clb: clbClient,
-	}, nil
-}
-
-func (d *TencentCLBDeployer) deployToInstanceUseSsl(ctx context.Context) error {
-	tcLoadbalancerId := d.option.DeployConfig.GetConfigAsString("loadbalancerId")
-	tcListenerId := d.option.DeployConfig.GetConfigAsString("listenerId")
-	tcDomain := d.option.DeployConfig.GetConfigAsString("domain")
-	if tcLoadbalancerId == "" {
-		return errors.New("`loadbalancerId` is required")
-	}
-	if tcListenerId == "" {
-		return errors.New("`listenerId` is required")
-	}
-
-	// 上传证书到 SSL
-	upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
-	if err != nil {
-		return err
-	}
-
-	d.infos = append(d.infos, toStr("已上传证书", upres))
-
-	// 证书部署到 CLB 实例
-	// REF: https://cloud.tencent.com/document/product/400/91667
-	deployCertificateInstanceReq := tcSsl.NewDeployCertificateInstanceRequest()
-	deployCertificateInstanceReq.CertificateId = common.StringPtr(upres.CertId)
-	deployCertificateInstanceReq.ResourceType = common.StringPtr("clb")
-	deployCertificateInstanceReq.Status = common.Int64Ptr(1)
-	if tcDomain == "" {
-		// 未开启 SNI,只需指定到监听器
-		deployCertificateInstanceReq.InstanceIdList = common.StringPtrs([]string{fmt.Sprintf("%s|%s", tcLoadbalancerId, tcListenerId)})
-	} else {
-		// 开启 SNI,需指定到域名(支持泛域名)
-		deployCertificateInstanceReq.InstanceIdList = common.StringPtrs([]string{fmt.Sprintf("%s|%s|%s", tcLoadbalancerId, tcListenerId, tcDomain)})
-	}
-	deployCertificateInstanceResp, err := d.sdkClients.ssl.DeployCertificateInstance(deployCertificateInstanceReq)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to execute sdk request 'ssl.DeployCertificateInstance'")
-	}
-
-	d.infos = append(d.infos, toStr("已部署证书到云资源实例", deployCertificateInstanceResp.Response))
-
-	return nil
-}
-
-func (d *TencentCLBDeployer) deployToLoadbalancer(ctx context.Context) error {
-	tcLoadbalancerId := d.option.DeployConfig.GetConfigAsString("loadbalancerId")
-	tcListenerIds := make([]string, 0)
-	if tcLoadbalancerId == "" {
-		return errors.New("`loadbalancerId` is required")
-	}
-
-	// 查询负载均衡器详细信息
-	// REF: https://cloud.tencent.com/document/api/214/46916
-	describeLoadBalancersDetailReq := tcClb.NewDescribeLoadBalancersDetailRequest()
-	describeLoadBalancersDetailResp, err := d.sdkClients.clb.DescribeLoadBalancersDetail(describeLoadBalancersDetailReq)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to execute sdk request 'clb.DescribeLoadBalancersDetail'")
-	}
-
-	d.infos = append(d.infos, toStr("已查询到负载均衡详细信息", describeLoadBalancersDetailResp))
-
-	// 查询监听器列表
-	// REF: https://cloud.tencent.com/document/api/214/30686
-	describeListenersReq := tcClb.NewDescribeListenersRequest()
-	describeListenersReq.LoadBalancerId = common.StringPtr(tcLoadbalancerId)
-	describeListenersResp, err := d.sdkClients.clb.DescribeListeners(describeListenersReq)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to execute sdk request 'clb.DescribeListeners'")
-	} else {
-		if describeListenersResp.Response.Listeners != nil {
-			for _, listener := range describeListenersResp.Response.Listeners {
-				if listener.Protocol == nil || (*listener.Protocol != "HTTPS" && *listener.Protocol != "TCP_SSL" && *listener.Protocol != "QUIC") {
-					continue
-				}
-
-				tcListenerIds = append(tcListenerIds, *listener.ListenerId)
-			}
-		}
-	}
-
-	d.infos = append(d.infos, toStr("已查询到负载均衡器下的监听器", tcListenerIds))
-
-	// 上传证书到 SCM
-	upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
-	if err != nil {
-		return err
-	}
-
-	d.infos = append(d.infos, toStr("已上传证书", upres))
-
-	// 批量更新监听器证书
-	var errs []error
-	for _, tcListenerId := range tcListenerIds {
-		if err := d.modifyListenerCertificate(ctx, tcLoadbalancerId, tcListenerId, upres.CertId); err != nil {
-			errs = append(errs, err)
-		}
-	}
-	if len(errs) > 0 {
-		return errors.Join(errs...)
-	}
-
-	return nil
-}
-
-func (d *TencentCLBDeployer) deployToListener(ctx context.Context) error {
-	tcLoadbalancerId := d.option.DeployConfig.GetConfigAsString("loadbalancerId")
-	tcListenerId := d.option.DeployConfig.GetConfigAsString("listenerId")
-	if tcLoadbalancerId == "" {
-		return errors.New("`loadbalancerId` is required")
-	}
-	if tcListenerId == "" {
-		return errors.New("`listenerId` is required")
-	}
-
-	// 上传证书到 SSL
-	upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
-	if err != nil {
-		return err
-	}
-
-	d.infos = append(d.infos, toStr("已上传证书", upres))
-
-	// 更新监听器证书
-	if err := d.modifyListenerCertificate(ctx, tcLoadbalancerId, tcListenerId, upres.CertId); err != nil {
-		return err
-	}
-
-	return nil
-}
-
-func (d *TencentCLBDeployer) deployToRuleDomain(ctx context.Context) error {
-	tcLoadbalancerId := d.option.DeployConfig.GetConfigAsString("loadbalancerId")
-	tcListenerId := d.option.DeployConfig.GetConfigAsString("listenerId")
-	tcDomain := d.option.DeployConfig.GetConfigAsString("domain")
-	if tcLoadbalancerId == "" {
-		return errors.New("`loadbalancerId` is required")
-	}
-	if tcListenerId == "" {
-		return errors.New("`listenerId` is required")
-	}
-	if tcDomain == "" {
-		return errors.New("`domain` is required")
-	}
-
-	// 上传证书到 SSL
-	upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
-	if err != nil {
-		return err
-	}
-
-	d.infos = append(d.infos, toStr("已上传证书", upres))
-
-	// 修改负载均衡七层监听器转发规则的域名级别属性
-	// REF: https://cloud.tencent.com/document/api/214/38092
-	modifyDomainAttributesReq := tcClb.NewModifyDomainAttributesRequest()
-	modifyDomainAttributesReq.LoadBalancerId = common.StringPtr(tcLoadbalancerId)
-	modifyDomainAttributesReq.ListenerId = common.StringPtr(tcListenerId)
-	modifyDomainAttributesReq.Domain = common.StringPtr(tcDomain)
-	modifyDomainAttributesReq.Certificate = &tcClb.CertificateInput{
-		SSLMode: common.StringPtr("UNIDIRECTIONAL"),
-		CertId:  common.StringPtr(upres.CertId),
-	}
-	modifyDomainAttributesResp, err := d.sdkClients.clb.ModifyDomainAttributes(modifyDomainAttributesReq)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to execute sdk request 'clb.ModifyDomainAttributes'")
-	}
-
-	d.infos = append(d.infos, toStr("已修改七层监听器转发规则的域名级别属性", modifyDomainAttributesResp.Response))
-
-	return nil
-}
-
-func (d *TencentCLBDeployer) modifyListenerCertificate(ctx context.Context, tcLoadbalancerId, tcListenerId, tcCertId string) error {
-	// 查询监听器列表
-	// REF: https://cloud.tencent.com/document/api/214/30686
-	describeListenersReq := tcClb.NewDescribeListenersRequest()
-	describeListenersReq.LoadBalancerId = common.StringPtr(tcLoadbalancerId)
-	describeListenersReq.ListenerIds = common.StringPtrs([]string{tcListenerId})
-	describeListenersResp, err := d.sdkClients.clb.DescribeListeners(describeListenersReq)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to execute sdk request 'clb.DescribeListeners'")
-	}
-	if len(describeListenersResp.Response.Listeners) == 0 {
-		d.infos = append(d.infos, toStr("未找到监听器", nil))
-		return errors.New("listener not found")
-	}
-
-	d.infos = append(d.infos, toStr("已查询到监听器属性", describeListenersResp.Response))
-
-	// 修改监听器属性
-	// REF: https://cloud.tencent.com/document/product/214/30681
-	modifyListenerReq := tcClb.NewModifyListenerRequest()
-	modifyListenerReq.LoadBalancerId = common.StringPtr(tcLoadbalancerId)
-	modifyListenerReq.ListenerId = common.StringPtr(tcListenerId)
-	modifyListenerReq.Certificate = &tcClb.CertificateInput{CertId: common.StringPtr(tcCertId)}
-	if describeListenersResp.Response.Listeners[0].Certificate != nil && describeListenersResp.Response.Listeners[0].Certificate.SSLMode != nil {
-		modifyListenerReq.Certificate.SSLMode = describeListenersResp.Response.Listeners[0].Certificate.SSLMode
-		modifyListenerReq.Certificate.CertCaId = describeListenersResp.Response.Listeners[0].Certificate.CertCaId
-	} else {
-		modifyListenerReq.Certificate.SSLMode = common.StringPtr("UNIDIRECTIONAL")
-	}
-	modifyListenerResp, err := d.sdkClients.clb.ModifyListener(modifyListenerReq)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to execute sdk request 'clb.ModifyListener'")
-	}
-
-	d.infos = append(d.infos, toStr("已修改监听器属性", modifyListenerResp.Response))
-
-	return nil
-}
diff --git a/internal/deployer/tencent_cos.go b/internal/deployer/tencent_cos.go
deleted file mode 100644
index bfcd7b7d..00000000
--- a/internal/deployer/tencent_cos.go
+++ /dev/null
@@ -1,107 +0,0 @@
-package deployer
-
-import (
-	"context"
-	"encoding/json"
-	"errors"
-	"fmt"
-
-	xerrors "github.com/pkg/errors"
-	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
-	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
-	tcSsl "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl/v20191205"
-
-	"github.com/usual2970/certimate/internal/domain"
-	"github.com/usual2970/certimate/internal/pkg/core/uploader"
-	uploaderTcSsl "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/tencentcloud-ssl"
-)
-
-type TencentCOSDeployer struct {
-	option *DeployerOption
-	infos  []string
-
-	sdkClient   *tcSsl.Client
-	sslUploader uploader.Uploader
-}
-
-func NewTencentCOSDeployer(option *DeployerOption) (Deployer, error) {
-	access := &domain.TencentAccess{}
-	if err := json.Unmarshal([]byte(option.Access), access); err != nil {
-		return nil, xerrors.Wrap(err, "failed to get access")
-	}
-
-	client, err := (&TencentCOSDeployer{}).createSdkClient(
-		access.SecretId,
-		access.SecretKey,
-		option.DeployConfig.GetConfigAsString("region"),
-	)
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to create sdk clients")
-	}
-
-	uploader, err := uploaderTcSsl.New(&uploaderTcSsl.TencentCloudSSLUploaderConfig{
-		SecretId:  access.SecretId,
-		SecretKey: access.SecretKey,
-	})
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to create ssl uploader")
-	}
-
-	return &TencentCOSDeployer{
-		option:      option,
-		infos:       make([]string, 0),
-		sdkClient:   client,
-		sslUploader: uploader,
-	}, nil
-}
-
-func (d *TencentCOSDeployer) GetID() string {
-	return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
-}
-
-func (d *TencentCOSDeployer) GetInfos() []string {
-	return d.infos
-}
-
-func (d *TencentCOSDeployer) Deploy(ctx context.Context) error {
-	tcRegion := d.option.DeployConfig.GetConfigAsString("region")
-	tcBucket := d.option.DeployConfig.GetConfigAsString("bucket")
-	tcDomain := d.option.DeployConfig.GetConfigAsString("domain")
-	if tcBucket == "" {
-		return errors.New("`bucket` is required")
-	}
-
-	// 上传证书到 SSL
-	upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
-	if err != nil {
-		return err
-	}
-
-	d.infos = append(d.infos, toStr("已上传证书", upres))
-
-	// 证书部署到 COS 实例
-	// REF: https://cloud.tencent.com/document/product/400/91667
-	deployCertificateInstanceReq := tcSsl.NewDeployCertificateInstanceRequest()
-	deployCertificateInstanceReq.CertificateId = common.StringPtr(upres.CertId)
-	deployCertificateInstanceReq.ResourceType = common.StringPtr("cos")
-	deployCertificateInstanceReq.Status = common.Int64Ptr(1)
-	deployCertificateInstanceReq.InstanceIdList = common.StringPtrs([]string{fmt.Sprintf("%s#%s#%s", tcRegion, tcBucket, tcDomain)})
-	deployCertificateInstanceResp, err := d.sdkClient.DeployCertificateInstance(deployCertificateInstanceReq)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to execute sdk request 'ssl.DeployCertificateInstance'")
-	}
-
-	d.infos = append(d.infos, toStr("已部署证书到云资源实例", deployCertificateInstanceResp.Response))
-
-	return nil
-}
-
-func (d *TencentCOSDeployer) createSdkClient(secretId, secretKey, region string) (*tcSsl.Client, error) {
-	credential := common.NewCredential(secretId, secretKey)
-	client, err := tcSsl.NewClient(credential, region, profile.NewClientProfile())
-	if err != nil {
-		return nil, err
-	}
-
-	return client, nil
-}
diff --git a/internal/deployer/tencent_ecdn.go b/internal/deployer/tencent_ecdn.go
deleted file mode 100644
index a61c5680..00000000
--- a/internal/deployer/tencent_ecdn.go
+++ /dev/null
@@ -1,154 +0,0 @@
-package deployer
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-	"strings"
-
-	xerrors "github.com/pkg/errors"
-	tcCdn "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn/v20180606"
-	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
-	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
-	tcSsl "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl/v20191205"
-
-	"github.com/usual2970/certimate/internal/domain"
-	"github.com/usual2970/certimate/internal/pkg/core/uploader"
-	uploaderTcSsl "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/tencentcloud-ssl"
-)
-
-type TencentECDNDeployer struct {
-	option *DeployerOption
-	infos  []string
-
-	sdkClients  *tencentECDNDeployerSdkClients
-	sslUploader uploader.Uploader
-}
-
-type tencentECDNDeployerSdkClients struct {
-	ssl *tcSsl.Client
-	cdn *tcCdn.Client
-}
-
-func NewTencentECDNDeployer(option *DeployerOption) (Deployer, error) {
-	access := &domain.TencentAccess{}
-	if err := json.Unmarshal([]byte(option.Access), access); err != nil {
-		return nil, xerrors.Wrap(err, "failed to get access")
-	}
-
-	clients, err := (&TencentECDNDeployer{}).createSdkClients(
-		access.SecretId,
-		access.SecretKey,
-	)
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to create sdk clients")
-	}
-
-	uploader, err := uploaderTcSsl.New(&uploaderTcSsl.TencentCloudSSLUploaderConfig{
-		SecretId:  access.SecretId,
-		SecretKey: access.SecretKey,
-	})
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to create ssl uploader")
-	}
-
-	return &TencentECDNDeployer{
-		option:      option,
-		infos:       make([]string, 0),
-		sdkClients:  clients,
-		sslUploader: uploader,
-	}, nil
-}
-
-func (d *TencentECDNDeployer) GetID() string {
-	return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
-}
-
-func (d *TencentECDNDeployer) GetInfos() []string {
-	return d.infos
-}
-
-func (d *TencentECDNDeployer) Deploy(ctx context.Context) error {
-	// 上传证书到 SSL
-	upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
-	if err != nil {
-		return err
-	}
-
-	d.infos = append(d.infos, toStr("已上传证书", upres))
-
-	// 获取待部署的 ECDN 实例
-	// 如果是泛域名,根据证书匹配 ECDN 实例
-	aliInstanceIds := make([]string, 0)
-	domain := d.option.DeployConfig.GetConfigAsString("domain")
-	if strings.HasPrefix(domain, "*") {
-		domains, err := d.getDomainsByCertificateId(upres.CertId)
-		if err != nil {
-			return err
-		}
-
-		aliInstanceIds = domains
-	} else {
-		aliInstanceIds = append(aliInstanceIds, domain)
-	}
-	if len(aliInstanceIds) == 0 {
-		d.infos = append(d.infos, "没有要部署的 ECDN 实例")
-		return nil
-	}
-
-	// 证书部署到 ECDN 实例
-	// REF: https://cloud.tencent.com/document/product/400/91667
-	deployCertificateInstanceReq := tcSsl.NewDeployCertificateInstanceRequest()
-	deployCertificateInstanceReq.CertificateId = common.StringPtr(upres.CertId)
-	deployCertificateInstanceReq.ResourceType = common.StringPtr("ecdn")
-	deployCertificateInstanceReq.Status = common.Int64Ptr(1)
-	deployCertificateInstanceReq.InstanceIdList = common.StringPtrs(aliInstanceIds)
-	deployCertificateInstanceResp, err := d.sdkClients.ssl.DeployCertificateInstance(deployCertificateInstanceReq)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to execute sdk request 'ssl.DeployCertificateInstance'")
-	}
-
-	d.infos = append(d.infos, toStr("已部署证书到云资源实例", deployCertificateInstanceResp.Response))
-
-	return nil
-}
-
-func (d *TencentECDNDeployer) createSdkClients(secretId, secretKey string) (*tencentECDNDeployerSdkClients, error) {
-	credential := common.NewCredential(secretId, secretKey)
-
-	sslClient, err := tcSsl.NewClient(credential, "", profile.NewClientProfile())
-	if err != nil {
-		return nil, err
-	}
-
-	cdnClient, err := tcCdn.NewClient(credential, "", profile.NewClientProfile())
-	if err != nil {
-		return nil, err
-	}
-
-	return &tencentECDNDeployerSdkClients{
-		ssl: sslClient,
-		cdn: cdnClient,
-	}, nil
-}
-
-func (d *TencentECDNDeployer) getDomainsByCertificateId(tcCertId string) ([]string, error) {
-	// 获取证书中的可用域名
-	// REF: https://cloud.tencent.com/document/product/228/42491
-	describeCertDomainsReq := tcCdn.NewDescribeCertDomainsRequest()
-	describeCertDomainsReq.CertId = common.StringPtr(tcCertId)
-	describeCertDomainsReq.Product = common.StringPtr("ecdn")
-	describeCertDomainsResp, err := d.sdkClients.cdn.DescribeCertDomains(describeCertDomainsReq)
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.DescribeCertDomains'")
-	}
-
-	domains := make([]string, 0)
-	if describeCertDomainsResp.Response.Domains == nil {
-		for _, domain := range describeCertDomainsResp.Response.Domains {
-			domains = append(domains, *domain)
-		}
-	}
-
-	return domains, nil
-}
diff --git a/internal/deployer/tencent_teo.go b/internal/deployer/tencent_teo.go
deleted file mode 100644
index 583089dd..00000000
--- a/internal/deployer/tencent_teo.go
+++ /dev/null
@@ -1,119 +0,0 @@
-package deployer
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-	"strings"
-
-	xerrors "github.com/pkg/errors"
-	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
-	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
-	tcSsl "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl/v20191205"
-	tcTeo "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo/v20220901"
-
-	"github.com/usual2970/certimate/internal/domain"
-	"github.com/usual2970/certimate/internal/pkg/core/uploader"
-	uploaderTcSsl "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/tencentcloud-ssl"
-)
-
-type TencentTEODeployer struct {
-	option *DeployerOption
-	infos  []string
-
-	sdkClients  *tencentTEODeployerSdkClients
-	sslUploader uploader.Uploader
-}
-
-type tencentTEODeployerSdkClients struct {
-	ssl *tcSsl.Client
-	teo *tcTeo.Client
-}
-
-func NewTencentTEODeployer(option *DeployerOption) (Deployer, error) {
-	access := &domain.TencentAccess{}
-	if err := json.Unmarshal([]byte(option.Access), access); err != nil {
-		return nil, xerrors.Wrap(err, "failed to get access")
-	}
-
-	clients, err := (&TencentTEODeployer{}).createSdkClients(
-		access.SecretId,
-		access.SecretKey,
-	)
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to create sdk clients")
-	}
-
-	uploader, err := uploaderTcSsl.New(&uploaderTcSsl.TencentCloudSSLUploaderConfig{
-		SecretId:  access.SecretId,
-		SecretKey: access.SecretKey,
-	})
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to create ssl uploader")
-	}
-
-	return &TencentTEODeployer{
-		option:      option,
-		infos:       make([]string, 0),
-		sdkClients:  clients,
-		sslUploader: uploader,
-	}, nil
-}
-
-func (d *TencentTEODeployer) GetID() string {
-	return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
-}
-
-func (d *TencentTEODeployer) GetInfos() []string {
-	return d.infos
-}
-
-func (d *TencentTEODeployer) Deploy(ctx context.Context) error {
-	tcZoneId := d.option.DeployConfig.GetConfigAsString("zoneId")
-	if tcZoneId == "" {
-		return xerrors.New("`zoneId` is required")
-	}
-
-	// 上传证书到 SSL
-	upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
-	if err != nil {
-		return err
-	}
-
-	d.infos = append(d.infos, toStr("已上传证书", upres))
-
-	// 配置域名证书
-	// REF: https://cloud.tencent.com/document/product/1552/80764
-	modifyHostsCertificateReq := tcTeo.NewModifyHostsCertificateRequest()
-	modifyHostsCertificateReq.ZoneId = common.StringPtr(tcZoneId)
-	modifyHostsCertificateReq.Mode = common.StringPtr("sslcert")
-	modifyHostsCertificateReq.Hosts = common.StringPtrs(strings.Split(strings.ReplaceAll(d.option.Domain, "\r\n", "\n"), "\n"))
-	modifyHostsCertificateReq.ServerCertInfo = []*tcTeo.ServerCertInfo{{CertId: common.StringPtr(upres.CertId)}}
-	modifyHostsCertificateResp, err := d.sdkClients.teo.ModifyHostsCertificate(modifyHostsCertificateReq)
-	if err != nil {
-		return xerrors.Wrap(err, "failed to execute sdk request 'teo.ModifyHostsCertificate'")
-	}
-
-	d.infos = append(d.infos, toStr("已配置域名证书", modifyHostsCertificateResp.Response))
-
-	return nil
-}
-
-func (d *TencentTEODeployer) createSdkClients(secretId, secretKey string) (*tencentTEODeployerSdkClients, error) {
-	credential := common.NewCredential(secretId, secretKey)
-
-	sslClient, err := tcSsl.NewClient(credential, "", profile.NewClientProfile())
-	if err != nil {
-		return nil, err
-	}
-
-	teoClient, err := tcTeo.NewClient(credential, "", profile.NewClientProfile())
-	if err != nil {
-		return nil, err
-	}
-
-	return &tencentTEODeployerSdkClients{
-		ssl: sslClient,
-		teo: teoClient,
-	}, nil
-}
diff --git a/internal/deployer/volcengine_cdn.go b/internal/deployer/volcengine_cdn.go
deleted file mode 100644
index 6ba8a23d..00000000
--- a/internal/deployer/volcengine_cdn.go
+++ /dev/null
@@ -1,116 +0,0 @@
-package deployer
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-	"strings"
-
-	volcenginecdn "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/volcengine-cdn"
-
-	xerrors "github.com/pkg/errors"
-	"github.com/usual2970/certimate/internal/domain"
-	"github.com/usual2970/certimate/internal/pkg/core/uploader"
-	"github.com/volcengine/volc-sdk-golang/service/cdn"
-)
-
-type VolcengineCDNDeployer struct {
-	option      *DeployerOption
-	infos       []string
-	sdkClient   *cdn.CDN
-	sslUploader uploader.Uploader
-}
-
-func NewVolcengineCDNDeployer(option *DeployerOption) (Deployer, error) {
-	access := &domain.VolcEngineAccess{}
-	if err := json.Unmarshal([]byte(option.Access), access); err != nil {
-		return nil, xerrors.Wrap(err, "failed to get access")
-	}
-	client := cdn.NewInstance()
-	client.Client.SetAccessKey(access.AccessKeyId)
-	client.Client.SetSecretKey(access.SecretAccessKey)
-	uploader, err := volcenginecdn.New(&volcenginecdn.VolcEngineCDNUploaderConfig{
-		AccessKeyId:     access.AccessKeyId,
-		AccessKeySecret: access.SecretAccessKey,
-	})
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to create ssl uploader")
-	}
-	return &VolcengineCDNDeployer{
-		option:      option,
-		infos:       make([]string, 0),
-		sdkClient:   client,
-		sslUploader: uploader,
-	}, nil
-}
-
-func (d *VolcengineCDNDeployer) GetID() string {
-	return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
-}
-
-func (d *VolcengineCDNDeployer) GetInfos() []string {
-	return d.infos
-}
-
-func (d *VolcengineCDNDeployer) Deploy(ctx context.Context) error {
-	apiCtx := context.Background()
-	// 上传证书
-	upres, err := d.sslUploader.Upload(apiCtx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
-	if err != nil {
-		return err
-	}
-
-	d.infos = append(d.infos, toStr("已上传证书", upres))
-
-	domains := make([]string, 0)
-	configDomain := d.option.DeployConfig.GetConfigAsString("domain")
-	if strings.HasPrefix(configDomain, "*.") {
-		// 获取证书可以部署的域名
-		// REF: https://www.volcengine.com/docs/6454/125711
-		describeCertConfigReq := &cdn.DescribeCertConfigRequest{
-			CertId: upres.CertId,
-		}
-		describeCertConfigResp, err := d.sdkClient.DescribeCertConfig(describeCertConfigReq)
-		if err != nil {
-			return xerrors.Wrap(err, "failed to execute sdk request 'cdn.DescribeCertConfig'")
-		}
-		for i := range describeCertConfigResp.Result.CertNotConfig {
-			// 当前未启用 HTTPS 的加速域名列表。
-			domains = append(domains, describeCertConfigResp.Result.CertNotConfig[i].Domain)
-		}
-		for i := range describeCertConfigResp.Result.OtherCertConfig {
-			// 已启用了 HTTPS 的加速域名列表。这些加速域名关联的证书不是您指定的证书。
-			domains = append(domains, describeCertConfigResp.Result.OtherCertConfig[i].Domain)
-		}
-		for i := range describeCertConfigResp.Result.SpecifiedCertConfig {
-			// 已启用了 HTTPS 的加速域名列表。这些加速域名关联了您指定的证书。
-			d.infos = append(d.infos, fmt.Sprintf("%s域名已配置该证书", describeCertConfigResp.Result.SpecifiedCertConfig[i].Domain))
-		}
-		if len(domains) == 0 {
-			if len(describeCertConfigResp.Result.SpecifiedCertConfig) > 0 {
-				// 所有匹配的域名都配置了该证书,跳过部署
-				return nil
-			} else {
-				return xerrors.Errorf("未查询到匹配的域名: %s", configDomain)
-			}
-		}
-	} else {
-		domains = append(domains, configDomain)
-	}
-	// 部署证书
-	// REF: https://www.volcengine.com/docs/6454/125712
-	for i := range domains {
-		batchDeployCertReq := &cdn.BatchDeployCertRequest{
-			CertId: upres.CertId,
-			Domain: domains[i],
-		}
-		batchDeployCertResp, err := d.sdkClient.BatchDeployCert(batchDeployCertReq)
-		if err != nil {
-			return xerrors.Wrap(err, "failed to execute sdk request 'cdn.BatchDeployCert'")
-		} else {
-			d.infos = append(d.infos, toStr(fmt.Sprintf("%s域名的证书已修改", domains[i]), batchDeployCertResp))
-		}
-	}
-
-	return nil
-}
diff --git a/internal/deployer/volcengine_live.go b/internal/deployer/volcengine_live.go
deleted file mode 100644
index 1795d79f..00000000
--- a/internal/deployer/volcengine_live.go
+++ /dev/null
@@ -1,148 +0,0 @@
-package deployer
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-	"regexp"
-	"strings"
-
-	xerrors "github.com/pkg/errors"
-	"github.com/usual2970/certimate/internal/domain"
-	"github.com/usual2970/certimate/internal/pkg/core/uploader"
-	volcenginelive "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/volcengine-live"
-	"github.com/usual2970/certimate/internal/pkg/utils/cast"
-	"github.com/volcengine/volc-sdk-golang/base"
-	live "github.com/volcengine/volc-sdk-golang/service/live/v20230101"
-)
-
-type VolcengineLiveDeployer struct {
-	option      *DeployerOption
-	infos       []string
-	sdkClient   *live.Live
-	sslUploader uploader.Uploader
-}
-
-func NewVolcengineLiveDeployer(option *DeployerOption) (Deployer, error) {
-	access := &domain.VolcEngineAccess{}
-	if err := json.Unmarshal([]byte(option.Access), access); err != nil {
-		return nil, xerrors.Wrap(err, "failed to get access")
-	}
-	client := live.NewInstance()
-	client.SetCredential(base.Credentials{
-		AccessKeyID:     access.AccessKeyId,
-		SecretAccessKey: access.SecretAccessKey,
-	})
-	uploader, err := volcenginelive.New(&volcenginelive.VolcEngineLiveUploaderConfig{
-		AccessKeyId:     access.AccessKeyId,
-		AccessKeySecret: access.SecretAccessKey,
-	})
-	if err != nil {
-		return nil, xerrors.Wrap(err, "failed to create ssl uploader")
-	}
-	return &VolcengineLiveDeployer{
-		option:      option,
-		infos:       make([]string, 0),
-		sdkClient:   client,
-		sslUploader: uploader,
-	}, nil
-}
-
-func (d *VolcengineLiveDeployer) GetID() string {
-	return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
-}
-
-func (d *VolcengineLiveDeployer) GetInfos() []string {
-	return d.infos
-}
-
-func (d *VolcengineLiveDeployer) Deploy(ctx context.Context) error {
-	apiCtx := context.Background()
-	// 上传证书
-	upres, err := d.sslUploader.Upload(apiCtx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
-	if err != nil {
-		return err
-	}
-
-	d.infos = append(d.infos, toStr("已上传证书", upres))
-
-	domains := make([]string, 0)
-	configDomain := d.option.DeployConfig.GetConfigAsString("domain")
-	if strings.HasPrefix(configDomain, "*.") {
-		// 如果是泛域名,获取所有的域名并匹配
-		matchDomains, err := d.getDomainsByWildcardDomain(apiCtx, configDomain)
-		if err != nil {
-			d.infos = append(d.infos, toStr("获取域名列表失败", upres))
-			return xerrors.Wrap(err, "failed to execute sdk request 'live.ListDomainDetail'")
-		}
-		if len(matchDomains) == 0 {
-			return xerrors.Errorf("未查询到匹配的域名: %s", configDomain)
-		}
-		domains = matchDomains
-	} else {
-		domains = append(domains, configDomain)
-	}
-
-	// 部署证书
-	// REF: https://www.volcengine.com/docs/6469/1186278#%E7%BB%91%E5%AE%9A%E8%AF%81%E4%B9%A6d
-	for i := range domains {
-		bindCertReq := &live.BindCertBody{
-			ChainID: upres.CertId,
-			Domain:  domains[i],
-			HTTPS:   cast.BoolPtr(true),
-		}
-		bindCertResp, err := d.sdkClient.BindCert(apiCtx, bindCertReq)
-		if err != nil {
-			return xerrors.Wrap(err, "failed to execute sdk request 'live.BindCert'")
-		} else {
-			d.infos = append(d.infos, toStr(fmt.Sprintf("%s域名的证书已修改", domains[i]), bindCertResp))
-		}
-	}
-
-	return nil
-}
-
-func (d *VolcengineLiveDeployer) getDomainsByWildcardDomain(ctx context.Context, wildcardDomain string) ([]string, error) {
-	pageNum := int32(1)
-	searchTotal := 0
-	domains := make([]string, 0)
-	for {
-		listDomainDetailReq := &live.ListDomainDetailBody{
-			PageNum:  pageNum,
-			PageSize: 1000,
-		}
-		// 查询域名列表
-		// REF: https://www.volcengine.com/docs/6469/1186277#%E6%9F%A5%E8%AF%A2%E5%9F%9F%E5%90%8D%E5%88%97%E8%A1%A8
-		listDomainDetailResp, err := d.sdkClient.ListDomainDetail(ctx, listDomainDetailReq)
-		if err != nil {
-			return domains, err
-		}
-		if listDomainDetailResp.Result.DomainList != nil {
-			for _, item := range listDomainDetailResp.Result.DomainList {
-				if matchWildcardDomain(item.Domain, wildcardDomain) {
-					domains = append(domains, item.Domain)
-				}
-			}
-		}
-		searchTotal += len(listDomainDetailResp.Result.DomainList)
-		if int(listDomainDetailResp.Result.Total) > searchTotal {
-			pageNum++
-		} else {
-			break
-		}
-	}
-
-	return domains, nil
-}
-
-func matchWildcardDomain(domain, wildcardDomain string) bool {
-	if strings.HasPrefix(wildcardDomain, "*.") {
-		if "*."+domain == wildcardDomain {
-			return true
-		}
-		regexPattern := "^([a-zA-Z0-9_-]+)\\." + regexp.QuoteMeta(wildcardDomain[2:]) + "$"
-		regex := regexp.MustCompile(regexPattern)
-		return regex.MatchString(domain)
-	}
-	return domain == wildcardDomain
-}
diff --git a/internal/deployer/webhook.go b/internal/deployer/webhook.go
deleted file mode 100644
index 35c37235..00000000
--- a/internal/deployer/webhook.go
+++ /dev/null
@@ -1,66 +0,0 @@
-package deployer
-
-import (
-	"bytes"
-	"context"
-	"encoding/json"
-	"fmt"
-	"net/http"
-
-	xerrors "github.com/pkg/errors"
-
-	"github.com/usual2970/certimate/internal/domain"
-	xhttp "github.com/usual2970/certimate/internal/utils/http"
-)
-
-type WebhookDeployer struct {
-	option *DeployerOption
-	infos  []string
-}
-
-func NewWebhookDeployer(option *DeployerOption) (Deployer, error) {
-	return &WebhookDeployer{
-		option: option,
-		infos:  make([]string, 0),
-	}, nil
-}
-
-func (d *WebhookDeployer) GetID() string {
-	return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
-}
-
-func (d *WebhookDeployer) GetInfos() []string {
-	return d.infos
-}
-
-type webhookData struct {
-	Domain      string            `json:"domain"`
-	Certificate string            `json:"certificate"`
-	PrivateKey  string            `json:"privateKey"`
-	Variables   map[string]string `json:"variables"`
-}
-
-func (d *WebhookDeployer) Deploy(ctx context.Context) error {
-	access := &domain.WebhookAccess{}
-	if err := json.Unmarshal([]byte(d.option.Access), access); err != nil {
-		return xerrors.Wrap(err, "failed to get access")
-	}
-
-	data := &webhookData{
-		Domain:      d.option.Domain,
-		Certificate: d.option.Certificate.Certificate,
-		PrivateKey:  d.option.Certificate.PrivateKey,
-		Variables:   d.option.DeployConfig.GetConfigAsVariables(),
-	}
-	body, _ := json.Marshal(data)
-	resp, err := xhttp.Req(access.Url, http.MethodPost, bytes.NewReader(body), map[string]string{
-		"Content-Type": "application/json",
-	})
-	if err != nil {
-		return xerrors.Wrap(err, "failed to send webhook request")
-	}
-
-	d.infos = append(d.infos, toStr("Webhook Response", string(resp)))
-
-	return nil
-}