From 6a14d801f17e810c6e59a144f8366d7610783eb7 Mon Sep 17 00:00:00 2001 From: Leo Chen Date: Fri, 25 Oct 2024 18:32:45 +0800 Subject: [PATCH 1/5] fix type incompatible error --- ui/src/components/certimate/DeployEdit.tsx | 2 +- ui/src/components/certimate/DeployEditDialog.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/src/components/certimate/DeployEdit.tsx b/ui/src/components/certimate/DeployEdit.tsx index 11903b79..81353a29 100644 --- a/ui/src/components/certimate/DeployEdit.tsx +++ b/ui/src/components/certimate/DeployEdit.tsx @@ -4,7 +4,7 @@ import { DeployConfig } from "@/domain/domain"; type DeployEditContext = { deploy: DeployConfig; - error: Record; + error: Record; setDeploy: (deploy: DeployConfig) => void; setError: (error: Record) => void; }; diff --git a/ui/src/components/certimate/DeployEditDialog.tsx b/ui/src/components/certimate/DeployEditDialog.tsx index 35a7345c..7ef5f291 100644 --- a/ui/src/components/certimate/DeployEditDialog.tsx +++ b/ui/src/components/certimate/DeployEditDialog.tsx @@ -44,7 +44,7 @@ const DeployEditDialog = ({ trigger, deployConfig, onSave }: DeployEditDialogPro type: "", }); - const [error, setError] = useState>({}); + const [error, setError] = useState>({}); const [open, setOpen] = useState(false); From a24a3595fa74b7212cf08d4a3fcf9458b1a8f3fa Mon Sep 17 00:00:00 2001 From: Leo Chen Date: Fri, 25 Oct 2024 18:47:41 +0800 Subject: [PATCH 2/5] feat: add tencent ECDN deploy --- README.md | 2 +- README_EN.md | 32 ++-- internal/deployer/deployer.go | 5 +- internal/deployer/tencent_ecdn.go | 146 ++++++++++++++++++ .../components/certimate/DeployEditDialog.tsx | 1 + ui/src/domain/domain.ts | 1 + ui/src/i18n/locales/en/nls.common.json | 1 + ui/src/i18n/locales/zh/nls.common.json | 1 + 8 files changed, 171 insertions(+), 18 deletions(-) create mode 100644 internal/deployer/tencent_ecdn.go diff --git a/README.md b/README.md index 63d7898b..9add4e40 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ make local.run | 服务商 | 支持申请证书 | 支持部署证书 | 备注 | | :--------: | :----------: | :----------: | ------------------------------------------------------------ | | 阿里云 | √ | √ | 可签发在阿里云注册的域名;可部署到阿里云 OSS、CDN | -| 腾讯云 | √ | √ | 可签发在腾讯云注册的域名;可部署到腾讯云 COS、CDN、CLB | +| 腾讯云 | √ | √ | 可签发在腾讯云注册的域名;可部署到腾讯云 COS、CDN、ECDN、CLB | | 华为云 | √ | √ | 可签发在华为云注册的域名;可部署到华为云 CDN、ELB | | 七牛云 | | √ | 可部署到七牛云 CDN | | AWS | √ | | 可签发在 AWS Route53 托管的域名 | diff --git a/README_EN.md b/README_EN.md index 5bb30b38..f059f992 100644 --- a/README_EN.md +++ b/README_EN.md @@ -70,22 +70,22 @@ password:1234567890 ## List of Supported Providers -| Provider | Registration | Deployment | Remarks | -| :-----------: | :----------: | :--------: | ------------------------------------------------------------------------------------------------ | -| Alibaba Cloud | √ | √ | Supports domains registered on Alibaba Cloud; supports deployment to Alibaba Cloud OSS, CDN | -| Tencent Cloud | √ | √ | Supports domains registered on Tencent Cloud; supports deployment to Tencent Cloud COS, CDN, CLB | -| Huawei Cloud | √ | √ | Supports domains registered on Huawei Cloud; supports deployment to Huawei Cloud CDN, ELB | -| Qiniu Cloud | | √ | Supports deployment to Qiniu Cloud CDN | -| AWS | √ | | Supports domains managed on AWS Route53 | -| CloudFlare | √ | | Supports domains registered on CloudFlare; CloudFlare services come with SSL certificates | -| GoDaddy | √ | | Supports domains registered on GoDaddy | -| Namesilo | √ | | Supports domains registered on Namesilo | -| PowerDNS | √ | | Supports domains managed on PowerDNS | -| HTTP Request | √ | | Supports domains which allow managing DNS by HTTP request | -| Local Deploy | | √ | Supports deployment to local servers | -| SSH | | √ | Supports deployment to SSH servers | -| Webhook | | √ | Supports callback to Webhook | -| Kubernetes | | √ | Supports deployment to Kubernetes Secret | +| Provider | Registration | Deployment | Remarks | +| :-----------: | :----------: | :--------: | --------------------------------------------------------------------------------------------------------------------- | +| Alibaba Cloud | √ | √ | Supports domains registered on Alibaba Cloud; supports deployment to Alibaba Cloud OSS, CDN | +| Tencent Cloud | √ | √ | Supports domains registered on Tencent Cloud; supports deployment to Tencent Cloud COS, CDN, ECDN, CLB | +| Huawei Cloud | √ | √ | Supports domains registered on Huawei Cloud; supports deployment to Huawei Cloud CDN, ELB | +| Qiniu Cloud | | √ | Supports deployment to Qiniu Cloud CDN | +| AWS | √ | | Supports domains managed on AWS Route53 | +| CloudFlare | √ | | Supports domains registered on CloudFlare; CloudFlare services come with SSL certificates | +| GoDaddy | √ | | Supports domains registered on GoDaddy | +| Namesilo | √ | | Supports domains registered on Namesilo | +| PowerDNS | √ | | Supports domains managed on PowerDNS | +| HTTP Request | √ | | Supports domains which allow managing DNS by HTTP request | +| Local Deploy | | √ | Supports deployment to local servers | +| SSH | | √ | Supports deployment to SSH servers | +| Webhook | | √ | Supports callback to Webhook | +| Kubernetes | | √ | Supports deployment to Kubernetes Secret | ## Screenshots diff --git a/internal/deployer/deployer.go b/internal/deployer/deployer.go index 512fd748..4fcbc732 100644 --- a/internal/deployer/deployer.go +++ b/internal/deployer/deployer.go @@ -19,6 +19,7 @@ const ( targetAliyunCDN = "aliyun-cdn" targetAliyunESA = "aliyun-dcdn" targetTencentCDN = "tencent-cdn" + targetTencentECDN = "tencent-ecdn" targetTencentCLB = "tencent-clb" targetTencentCOS = "tencent-cos" targetHuaweiCloudCDN = "huaweicloud-cdn" @@ -107,7 +108,9 @@ func getWithDeployConfig(record *models.Record, cert *applicant.Certificate, dep case targetAliyunESA: return NewAliyunESADeployer(option) case targetTencentCDN: - return NewTencentCDNDeployer(option) + return NewTencentCDNDeployer(option) + case targetTencentECDN: + return NewTencentECDNDeployer(option) case targetTencentCLB: return NewTencentCLBDeployer(option) case targetTencentCOS: diff --git a/internal/deployer/tencent_ecdn.go b/internal/deployer/tencent_ecdn.go new file mode 100644 index 00000000..ea52794e --- /dev/null +++ b/internal/deployer/tencent_ecdn.go @@ -0,0 +1,146 @@ +package deployer + +import ( + "context" + "encoding/base64" + "encoding/json" + "fmt" + "strings" + + cdn "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" + ssl "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl/v20191205" + + "github.com/usual2970/certimate/internal/domain" + "github.com/usual2970/certimate/internal/utils/rand" +) + +type TencentECDNDeployer struct { + option *DeployerOption + credential *common.Credential + infos []string +} + +func NewTencentECDNDeployer(option *DeployerOption) (Deployer, error) { + access := &domain.TencentAccess{} + if err := json.Unmarshal([]byte(option.Access), access); err != nil { + return nil, fmt.Errorf("failed to unmarshal tencent access: %w", err) + } + + credential := common.NewCredential( + access.SecretId, + access.SecretKey, + ) + + return &TencentECDNDeployer{ + option: option, + credential: credential, + infos: make([]string, 0), + }, nil +} + +func (d *TencentECDNDeployer) GetID() string { + return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id) +} + +func (d *TencentECDNDeployer) GetInfo() []string { + return d.infos +} + +func (d *TencentECDNDeployer) Deploy(ctx context.Context) error { + // 上传证书 + certId, err := d.uploadCert() + if err != nil { + return fmt.Errorf("failed to upload certificate: %w", err) + } + d.infos = append(d.infos, toStr("上传证书", certId)) + + if err := d.deploy(certId); err != nil { + return fmt.Errorf("failed to deploy: %w", err) + } + + return nil +} + +func (d *TencentECDNDeployer) uploadCert() (string, error) { + cpf := profile.NewClientProfile() + cpf.HttpProfile.Endpoint = "ssl.tencentcloudapi.com" + + client, _ := ssl.NewClient(d.credential, "", cpf) + + request := ssl.NewUploadCertificateRequest() + + request.CertificatePublicKey = common.StringPtr(d.option.Certificate.Certificate) + request.CertificatePrivateKey = common.StringPtr(d.option.Certificate.PrivateKey) + request.Alias = common.StringPtr(d.option.Domain + "_" + rand.RandStr(6)) + request.Repeatable = common.BoolPtr(false) + + response, err := client.UploadCertificate(request) + if err != nil { + return "", fmt.Errorf("failed to upload certificate: %w", err) + } + + return *response.Response.CertificateId, nil +} + +func (d *TencentECDNDeployer) deploy(certId string) error { + cpf := profile.NewClientProfile() + cpf.HttpProfile.Endpoint = "ssl.tencentcloudapi.com" + // 实例化要请求产品的client对象,clientProfile是可选的 + client, _ := ssl.NewClient(d.credential, "", cpf) + + // 实例化一个请求对象,每个接口都会对应一个request对象 + request := ssl.NewDeployCertificateInstanceRequest() + + request.CertificateId = common.StringPtr(certId) + request.ResourceType = common.StringPtr("ecdn") + request.Status = common.Int64Ptr(1) + + // 如果是泛域名就从cdn列表下获取SSL证书中的可用域名 + domain := getDeployString(d.option.DeployConfig, "domain") + if strings.Contains(domain, "*") { + list, errGetList := d.getDomainList() + if errGetList != nil { + return fmt.Errorf("failed to get certificate domain list: %w", errGetList) + } + if list == nil || len(list) == 0 { + return fmt.Errorf("failed to get certificate domain list: empty list.") + } + request.InstanceIdList = common.StringPtrs(list) + } else { // 否则直接使用传入的域名 + request.InstanceIdList = common.StringPtrs([]string{domain}) + } + + // 返回的resp是一个DeployCertificateInstanceResponse的实例,与请求对象对应 + resp, err := client.DeployCertificateInstance(request) + if err != nil { + return fmt.Errorf("failed to deploy certificate: %w", err) + } + d.infos = append(d.infos, toStr("部署证书", resp.Response)) + return nil +} + +func (d *TencentECDNDeployer) getDomainList() ([]string, error) { + cpf := profile.NewClientProfile() + cpf.HttpProfile.Endpoint = "cdn.tencentcloudapi.com" + client, _ := cdn.NewClient(d.credential, "", cpf) + + request := cdn.NewDescribeCertDomainsRequest() + + cert := base64.StdEncoding.EncodeToString([]byte(d.option.Certificate.Certificate)) + request.Cert = &cert + request.Product = common.StringPtr("ecdn") + + response, err := client.DescribeCertDomains(request) + if err != nil { + return nil, fmt.Errorf("failed to get domain list: %w", err) + } + + domains := make([]string, 0) + for _, domain := range response.Response.Domains { + domains = append(domains, *domain) + } + + return domains, nil +} diff --git a/ui/src/components/certimate/DeployEditDialog.tsx b/ui/src/components/certimate/DeployEditDialog.tsx index 7ef5f291..be74cd7a 100644 --- a/ui/src/components/certimate/DeployEditDialog.tsx +++ b/ui/src/components/certimate/DeployEditDialog.tsx @@ -119,6 +119,7 @@ const DeployEditDialog = ({ trigger, deployConfig, onSave }: DeployEditDialogPro childComponent = ; break; case "tencent-cdn": + case "tencent-ecdn": childComponent = ; break; case "tencent-clb": diff --git a/ui/src/domain/domain.ts b/ui/src/domain/domain.ts index 97bb4ce1..6d3175cf 100644 --- a/ui/src/domain/domain.ts +++ b/ui/src/domain/domain.ts @@ -76,6 +76,7 @@ export const deployTargetsMap: Map = new Map ["aliyun-cdn", "common.provider.aliyun.cdn", "/imgs/providers/aliyun.svg"], ["aliyun-dcdn", "common.provider.aliyun.dcdn", "/imgs/providers/aliyun.svg"], ["tencent-cdn", "common.provider.tencent.cdn", "/imgs/providers/tencent.svg"], + ["tencent-ecdn", "common.provider.tencent.ecdn", "/imgs/providers/tencent.svg"], ["tencent-clb", "common.provider.tencent.clb", "/imgs/providers/tencent.svg"], ["tencent-cos", "common.provider.tencent.cos", "/imgs/providers/tencent.svg"], ["huaweicloud-cdn", "common.provider.huaweicloud.cdn", "/imgs/providers/huaweicloud.svg"], diff --git a/ui/src/i18n/locales/en/nls.common.json b/ui/src/i18n/locales/en/nls.common.json index 872ef19b..b2e3e436 100644 --- a/ui/src/i18n/locales/en/nls.common.json +++ b/ui/src/i18n/locales/en/nls.common.json @@ -58,6 +58,7 @@ "common.provider.aliyun.dcdn": "Alibaba Cloud - DCDN", "common.provider.tencent": "Tencent Cloud", "common.provider.tencent.cdn": "Tencent Cloud - CDN", + "common.provider.tencent.ecdn": "Tencent Cloud - ECDN", "common.provider.tencent.clb": "Tencent Cloud - CLB", "common.provider.tencent.cos": "Tencent Cloud - COS", "common.provider.huaweicloud": "Huawei Cloud", diff --git a/ui/src/i18n/locales/zh/nls.common.json b/ui/src/i18n/locales/zh/nls.common.json index 818807ce..8f82a96d 100644 --- a/ui/src/i18n/locales/zh/nls.common.json +++ b/ui/src/i18n/locales/zh/nls.common.json @@ -58,6 +58,7 @@ "common.provider.tencent": "腾讯云", "common.provider.tencent.cos": "腾讯云 - COS", "common.provider.tencent.cdn": "腾讯云 - CDN", + "common.provider.tencent.ecdn": "腾讯云 - ECDN", "common.provider.tencent.clb": "腾讯云 - CLB", "common.provider.huaweicloud": "华为云", "common.provider.huaweicloud.cdn": "华为云 - CDN", From 3b3376899c7dfa2f36f1f993482d104963dc5ad5 Mon Sep 17 00:00:00 2001 From: Leo Chen Date: Fri, 25 Oct 2024 22:16:27 +0800 Subject: [PATCH 3/5] add feat: tencent TEO deploy support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增腾讯TEO(Edge One)部署方式 --- go.mod | 3 +- go.sum | 4 + internal/deployer/deployer.go | 3 + internal/deployer/tencent_teo.go | 111 +++++++++++++++ .../components/certimate/DeployEditDialog.tsx | 4 + .../certimate/DeployToTencentTEO.tsx | 131 ++++++++++++++++++ ui/src/domain/domain.ts | 1 + ui/src/i18n/locales/en/nls.common.json | 1 + ui/src/i18n/locales/en/nls.domain.json | 4 + ui/src/i18n/locales/zh/nls.common.json | 1 + ui/src/i18n/locales/zh/nls.domain.json | 4 + 11 files changed, 266 insertions(+), 1 deletion(-) create mode 100644 internal/deployer/tencent_teo.go create mode 100644 ui/src/components/certimate/DeployToTencentTEO.tsx diff --git a/go.mod b/go.mod index dff90fd5..d2cf83b2 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/pocketbase/pocketbase v0.22.18 github.com/qiniu/go-sdk/v7 v7.22.0 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1017 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1017 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1030 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.992 golang.org/x/crypto v0.28.0 k8s.io/apimachinery v0.31.1 @@ -54,6 +54,7 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/technoweenie/multipartstreamer v1.0.1 // indirect + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1030 // indirect github.com/x448/float16 v0.8.4 // indirect go.mongodb.org/mongo-driver v1.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/go.sum b/go.sum index f666c84a..17f7c756 100644 --- a/go.sum +++ b/go.sum @@ -451,10 +451,14 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.992/go.mod github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1002/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1017 h1:SXrldOXwgomYuATVAuz5ofpTjB+99qVELgdy5R5kMgI= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1017/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1030 h1:kwiUoCkooUgy7iPyhEEbio7WT21kGJUeZ5JeJfb/dYk= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1030/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1002 h1:QwE0dRkAAbdf+eACnkNULgDn9ZKUJpPWRyXdqJolP5E= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1002/go.mod h1:WdC0FYbqYhJwQ3kbqri6hVP5HAEp+rzX9FToItTAzUg= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.992 h1:A6O89OlCJQUpNxGqC/E5By04UNKBryIt5olQIGOx8mg= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.992/go.mod h1:BcvC7ZPdSlhRggVq4J1ToJlgv8bmODIAuSo0naFZOLo= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1030 h1:tlHbfQlAfL12J/5XF4indKl0cAA3vEn6TDiGZVsr050= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1030/go.mod h1:8dW6JByZKNDAPnjlXxBk9yDc+QGbldpa0tBRfi1kG+U= github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= diff --git a/internal/deployer/deployer.go b/internal/deployer/deployer.go index 4fcbc732..21deb609 100644 --- a/internal/deployer/deployer.go +++ b/internal/deployer/deployer.go @@ -22,6 +22,7 @@ const ( targetTencentECDN = "tencent-ecdn" targetTencentCLB = "tencent-clb" targetTencentCOS = "tencent-cos" + targetTencentTEO = "tencent-teo" targetHuaweiCloudCDN = "huaweicloud-cdn" targetHuaweiCloudELB = "huaweicloud-elb" targetQiniuCdn = "qiniu-cdn" @@ -115,6 +116,8 @@ func getWithDeployConfig(record *models.Record, cert *applicant.Certificate, dep return NewTencentCLBDeployer(option) case targetTencentCOS: return NewTencentCOSDeployer(option) + case targetTencentTEO: + return NewTencentTEODeployer(option) case targetHuaweiCloudCDN: return NewHuaweiCloudCDNDeployer(option) case targetHuaweiCloudELB: diff --git a/internal/deployer/tencent_teo.go b/internal/deployer/tencent_teo.go new file mode 100644 index 00000000..930232ef --- /dev/null +++ b/internal/deployer/tencent_teo.go @@ -0,0 +1,111 @@ +package deployer + +import ( + "context" + "encoding/json" + "fmt" + "strings" + + teo "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo/v20220901" + "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" + "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" + ssl "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl/v20191205" + + "github.com/usual2970/certimate/internal/domain" + "github.com/usual2970/certimate/internal/utils/rand" +) + +type TencentTEODeployer struct { + option *DeployerOption + credential *common.Credential + infos []string +} + +func NewTencentTEODeployer(option *DeployerOption) (Deployer, error) { + access := &domain.TencentAccess{} + if err := json.Unmarshal([]byte(option.Access), access); err != nil { + return nil, fmt.Errorf("failed to unmarshal tencent access: %w", err) + } + + credential := common.NewCredential( + access.SecretId, + access.SecretKey, + ) + + return &TencentTEODeployer{ + option: option, + credential: credential, + infos: make([]string, 0), + }, nil +} + +func (d *TencentTEODeployer) GetID() string { + return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id) +} + +func (d *TencentTEODeployer) GetInfo() []string { + return d.infos +} + +func (d *TencentTEODeployer) Deploy(ctx context.Context) error { + // 上传证书 + certId, err := d.uploadCert() + if err != nil { + return fmt.Errorf("failed to upload certificate: %w", err) + } + d.infos = append(d.infos, toStr("上传证书", certId)) + + if err := d.deploy(certId); err != nil { + return fmt.Errorf("failed to deploy: %w", err) + } + + return nil +} + +func (d *TencentTEODeployer) uploadCert() (string, error) { + cpf := profile.NewClientProfile() + cpf.HttpProfile.Endpoint = "ssl.tencentcloudapi.com" + + client, _ := ssl.NewClient(d.credential, "", cpf) + + request := ssl.NewUploadCertificateRequest() + + request.CertificatePublicKey = common.StringPtr(d.option.Certificate.Certificate) + request.CertificatePrivateKey = common.StringPtr(d.option.Certificate.PrivateKey) + request.Alias = common.StringPtr(d.option.Domain + "_" + rand.RandStr(6)) + request.Repeatable = common.BoolPtr(false) + + response, err := client.UploadCertificate(request) + if err != nil { + return "", fmt.Errorf("failed to upload certificate: %w", err) + } + + return *response.Response.CertificateId, nil +} + +func (d *TencentTEODeployer) deploy(certId string) error { + cpf := profile.NewClientProfile() + cpf.HttpProfile.Endpoint = "teo.tencentcloudapi.com" + // 实例化要请求产品的client对象,clientProfile是可选的 + client, _ := teo.NewClient(d.credential, "", cpf) + + // 实例化一个请求对象,每个接口都会对应一个request对象 + request := teo.NewModifyHostsCertificateRequest() + + request.ZoneId = common.StringPtr(getDeployString(d.option.DeployConfig, "zoneId")) + request.Mode = common.StringPtr("sslcert") + request.ServerCertInfo = []*teo.ServerCertInfo{&teo.ServerCertInfo{ + CertId: common.StringPtr(certId), + }} + + domains := strings.Split(strings.ReplaceAll(d.option.Domain, "\r\n", "\n"),"\n") + request.Hosts = common.StringPtrs(domains) + + // 返回的resp是一个DeployCertificateInstanceResponse的实例,与请求对象对应 + resp, err := client.ModifyHostsCertificate(request) + if err != nil { + return fmt.Errorf("failed to deploy certificate: %w", err) + } + d.infos = append(d.infos, toStr("部署证书", resp.Response)) + return nil +} diff --git a/ui/src/components/certimate/DeployEditDialog.tsx b/ui/src/components/certimate/DeployEditDialog.tsx index be74cd7a..3d33b870 100644 --- a/ui/src/components/certimate/DeployEditDialog.tsx +++ b/ui/src/components/certimate/DeployEditDialog.tsx @@ -14,6 +14,7 @@ import DeployToAliyunCDN from "./DeployToAliyunCDN"; import DeployToTencentCDN from "./DeployToTencentCDN"; import DeployToTencentCLB from "./DeployToTencentCLB"; import DeployToTencentCOS from "./DeployToTencentCOS"; +import DeployToTencentTEO from "./DeployToTencentTEO"; import DeployToHuaweiCloudCDN from "./DeployToHuaweiCloudCDN"; import DeployToHuaweiCloudELB from "./DeployToHuaweiCloudELB"; import DeployToQiniuCDN from "./DeployToQiniuCDN"; @@ -128,6 +129,9 @@ const DeployEditDialog = ({ trigger, deployConfig, onSave }: DeployEditDialogPro case "tencent-cos": childComponent = ; break; + case "tencent-teo": + childComponent = ; + break; case "huaweicloud-cdn": childComponent = ; break; diff --git a/ui/src/components/certimate/DeployToTencentTEO.tsx b/ui/src/components/certimate/DeployToTencentTEO.tsx new file mode 100644 index 00000000..cdd64280 --- /dev/null +++ b/ui/src/components/certimate/DeployToTencentTEO.tsx @@ -0,0 +1,131 @@ +import { useEffect } from "react"; +import { useTranslation } from "react-i18next"; +import { z } from "zod"; +import { produce } from "immer"; + +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Textarea } from "@/components/ui/textarea"; +import { useDeployEditContext } from "./DeployEdit"; + +const DeployToTencentTEO = () => { + const { t } = useTranslation(); + + const { deploy: data, setDeploy, error, setError } = useDeployEditContext(); + + useEffect(() => { + setError({}); + }, []); + + useEffect(() => { + const resp = domainSchema.safeParse(data.config?.domain); + if (!resp.success) { + setError({ + ...error, + domain: JSON.parse(resp.error.message)[0].message, + }); + } else { + setError({ + ...error, + domain: "", + }); + } + }, [data]); + + useEffect(() => { + const resp = ZoneIdSchema.safeParse(data.config?.zoneId); + if (!resp.success) { + setError({ + ...error, + zoneId: JSON.parse(resp.error.message)[0].message, + }); + } else { + setError({ + ...error, + zoneId: "", + }); + } + }, [data]); + + const domainSchema = z.string().regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, { + message: t("common.errmsg.domain_invalid"), + }); + + const ZoneIdSchema = z.string().regex(/^zone-[0-9a-zA-Z]{9}$/, { + message: t("common.errmsg.domain_invalid"), + }); + + return ( +
+
+ + { + const temp = e.target.value; + + const resp = ZoneIdSchema.safeParse(temp); + if (!resp.success) { + setError({ + ...error, + zoneId: JSON.parse(resp.error.message)[0].message, + }); + } else { + setError({ + ...error, + zoneId: "", + }); + } + + const newData = produce(data, (draft) => { + if (!draft.config) { + draft.config = {}; + } + draft.config.zoneId = temp; + }); + setDeploy(newData); + }} + /> +
{error?.zoneId}
+
+ +
+ +