From c9a3e3d9d26f964c7af7b56667936f1414fbf42a Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Sun, 20 Apr 2025 23:53:27 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E6=96=B0=E5=A2=9E=E9=83=A8=E7=BD=B2?= =?UTF-8?q?=E5=88=B0=E7=81=AB=E5=B1=B1=E5=BC=95=E6=93=8EALB/CLB=E3=80=81?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E5=88=B0=E8=AF=81=E4=B9=A6=E4=B8=AD=E5=BF=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin-volcengine/plugins/index.ts | 3 + .../plugins/plugin-deploy-to-alb.ts | 220 ++++++++++++++++ .../plugins/plugin-deploy-to-cdn.ts | 8 +- .../plugins/plugin-deploy-to-clb.ts | 248 ++++++++++++++++++ .../plugins/plugin-upload-to-cert-center.ts | 77 ++++++ .../plugins/plugin-volcengine/ve-client.ts | 122 +++++++++ 6 files changed, 677 insertions(+), 1 deletion(-) create mode 100644 packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/plugin-deploy-to-alb.ts create mode 100644 packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/plugin-deploy-to-clb.ts create mode 100644 packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/plugin-upload-to-cert-center.ts create mode 100644 packages/ui/certd-server/src/plugins/plugin-volcengine/ve-client.ts diff --git a/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/index.ts b/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/index.ts index 74f66650..f1fe8c1e 100644 --- a/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/index.ts @@ -1 +1,4 @@ export * from './plugin-deploy-to-cdn.js' +export * from './plugin-deploy-to-clb.js' +export * from './plugin-upload-to-cert-center.js' +export * from './plugin-deploy-to-alb.js' diff --git a/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/plugin-deploy-to-alb.ts b/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/plugin-deploy-to-alb.ts new file mode 100644 index 00000000..5fdc0136 --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/plugin-deploy-to-alb.ts @@ -0,0 +1,220 @@ +import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline"; +import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib"; +import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert"; +import { VolcengineAccess } from "../access.js"; +import { VolcengineClient } from "../ve-client.js"; + +@IsTaskPlugin({ + name: "VolcengineDeployToALB", + title: "火山引擎-部署证书至ALB", + icon: "svg:icon-volcengine", + group: pluginGroups.volcengine.key, + desc: "部署至火山引擎应用负载均衡", + default: { + strategy: { + runStrategy: RunStrategy.SkipWhenSucceed + } + } +}) +export class VolcengineDeployToALB extends AbstractTaskPlugin { + @TaskInput({ + title: "域名证书", + helper: "请选择前置任务输出的域名证书", + component: { + name: "output-selector", + from: [...CertApplyPluginNames, "VolcengineUploadToCertCenter"] + }, + required: true + }) + cert!: CertInfo | string; + + @TaskInput(createCertDomainGetterInputDefine({ props: { required: false } })) + certDomains!: string[]; + + + @TaskInput({ + title: "Access授权", + helper: "火山引擎AccessKeyId、AccessKeySecret", + component: { + name: "access-selector", + type: "volcengine" + }, + required: true + }) + accessId!: string; + + @TaskInput({ + title: "Region", + helper: "地区选择", + component: { + name: "a-select", + options: [ + /** + * 中国地区 + * 华北2 + * 北京 + * cn-beijing + * 4 + * 可用区A:cn-beijing-a + * 可用区B:cn-beijing-b + * 可用区C:cn-beijing-c + * 可用区D:cn-beijing-d + * 商用 + * 华东2 + * 上海 + * cn-shanghai + * 4 + * 可用区A:cn-shanghai-a + * 可用区B:cn-shanghai-b + * 可用区C:cn-shanghai-c + * 可用区E:cn-shanghai-e + * 商用 + * 华南1 + * 广州 + * cn-guangzhou + * 3 + * 可用区A:cn-guangzhou-a + * 可用区B:cn-guangzhou-b + * 可用区C:cn-guangzhou-c + * 商用 + * 中国香港 + * 香港 + * cn-hongkong + * 2 + * 可用区A:cn-hongkong-a + * 可用区B:cn-hongkong-b + * 商用 + * 其他国家和地区 + * 亚太东南 + * 柔佛 + * ap-southeast-1 + * 2 + * 可用区A:ap-southeast-1a + * 可用区B:ap-southeast-1b + * 商用 + * 雅加达 + * ap-southeast-3 + * 2 + * 可用区A:ap-southeast-3a + * 可用区B:ap-southeast-3b + * 商用 + */ + { label: "北京", value: "cn-beijing" }, + { label: "上海", value: "cn-shanghai" }, + { label: "广州", value: "cn-guangzhou" }, + { label: "香港", value: "cn-hongkong" }, + { label: "柔佛", value: "ap-southeast-1" }, + { label: "雅加达", value: "ap-southeast-3" } + + ] + }, + value: "cn-beijing", + required: true + }) + regionId!: string; + + + @TaskInput( + createRemoteSelectInputDefine({ + title: "监听器列表", + helper: "选择要部署证书的监听器\n需要在监听器中选择证书中心,进行跨服务访问授权", + action: VolcengineDeployToALB.prototype.onGetListenerList.name, + watches: ["certDomains", "accessId", "regionId"], + required: true + }) + ) + listenerList!: string | string[]; + + + async onInstance() { + } + + async execute(): Promise { + this.logger.info("开始部署证书到火山引擎ALB"); + const access = await this.getAccess(this.accessId); + const certService = await this.getCertService(access); + let certId = this.cert; + if (typeof certId !== "string") { + const certInfo = this.cert as CertInfo; + this.logger.info(`开始上传证书`); + certId = await certService.ImportCertificate({ + certName:this.appendTimeSuffix("certd"), + cert:certInfo + }); + this.logger.info(`上传证书成功:${certId}`); + } else { + this.logger.info(`使用已有证书ID:${certId}`); + } + + const service = await this.getAlbService(); + for (const listener of this.listenerList) { + this.logger.info(`开始部署监听器${listener}证书`); + await service.request({ + action: "ModifyListenerAttributes", + query: { + ListenerId: listener, + CertificateSource: "cert_center", + CertCenterCertificateId: certId + } + }); + this.logger.info(`部署监听器${listener}证书成功`); + } + + this.logger.info("部署完成"); + } + + + private async getCertService(access: VolcengineAccess) { + const client = new VolcengineClient({ + logger: this.logger, + access, + http: this.http + }); + + return await client.getCertCenterService(); + } + + private async getAlbService() { + const access = await this.getAccess(this.accessId); + + const client = new VolcengineClient({ + logger: this.logger, + access, + http: this.http + }); + + const service = await client.getAlbService({ + region: this.regionId + }); + return service; + } + + async onGetListenerList(data: any) { + if (!this.accessId) { + throw new Error("请选择Access授权"); + } + const service = await this.getAlbService(); + + const res = await service.request({ + action: "DescribeListeners", + method: "GET", + query: { + PageSize: 100, + Protocol: "HTTPS" + }, + }); + + const list = res.Result.Listeners; + if (!list || list.length === 0) { + throw new Error("找不到HTTPS类型的负载均衡监听器,您也可以手动输入监听器ID"); + } + return list.map((item: any) => { + return { + value: item.ListenerId, + label: `${item.ListenerName}<${item.Description}:${item.ListenerId}>` + }; + }); + } +} + +new VolcengineDeployToALB(); diff --git a/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/plugin-deploy-to-cdn.ts b/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/plugin-deploy-to-cdn.ts index b16b34ff..c16411c0 100644 --- a/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/plugin-deploy-to-cdn.ts +++ b/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/plugin-deploy-to-cdn.ts @@ -23,7 +23,7 @@ export class VolcengineDeployToCDN extends AbstractTaskPlugin { helper: '请选择前置任务输出的域名证书', component: { name: 'output-selector', - from: [...CertApplyPluginNames, 'VolcengineUploadCert'], + from: [...CertApplyPluginNames, 'VolcengineUploadToCertCenter'], }, required: true, }) @@ -82,11 +82,17 @@ export class VolcengineDeployToCDN extends AbstractTaskPlugin { const client = await this.getClient(access) const service = await client.getCdnClient() + if (!this.cert) { + throw new Error('你还未选择证书'); + } let certId = this.cert if (typeof certId !== 'string') { const certInfo = this.cert as CertInfo this.logger.info(`开始上传证书`) certId = await client.uploadCert(certInfo, this.appendTimeSuffix('certd')) + this.logger.info(`上传证书成功:${certId}`); + }else{ + this.logger.info(`使用已有证书ID:${certId}`); } for (const domain of this.domainName) { diff --git a/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/plugin-deploy-to-clb.ts b/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/plugin-deploy-to-clb.ts new file mode 100644 index 00000000..d870803f --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/plugin-deploy-to-clb.ts @@ -0,0 +1,248 @@ +import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline"; +import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib"; +import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert"; +import { VolcengineAccess } from "../access.js"; +import { VolcengineClient } from "../ve-client.js"; + +@IsTaskPlugin({ + name: "VolcengineDeployToCLB", + title: "火山引擎-部署证书至CLB", + icon: "svg:icon-volcengine", + group: pluginGroups.volcengine.key, + desc: "部署至火山引擎负载均衡", + default: { + strategy: { + runStrategy: RunStrategy.SkipWhenSucceed + } + } +}) +export class VolcengineDeployToCLB extends AbstractTaskPlugin { + @TaskInput({ + title: "域名证书", + helper: "请选择前置任务输出的域名证书", + component: { + name: "output-selector", + from: [...CertApplyPluginNames, "VolcengineUploadToCertCenter"] + }, + required: true + }) + cert!: CertInfo | string; + + @TaskInput(createCertDomainGetterInputDefine({ props: { required: false } })) + certDomains!: string[]; + + + @TaskInput({ + title: "Access授权", + helper: "火山引擎AccessKeyId、AccessKeySecret", + component: { + name: "access-selector", + type: "volcengine" + }, + required: true + }) + accessId!: string; + + @TaskInput({ + title: "Region", + helper: "地区选择", + component: { + name: "a-select", + options: [ + /** + * 中国地区 + * 华北2 + * 北京 + * cn-beijing + * 4 + * 可用区A:cn-beijing-a + * 可用区B:cn-beijing-b + * 可用区C:cn-beijing-c + * 可用区D:cn-beijing-d + * 商用 + * 华东2 + * 上海 + * cn-shanghai + * 4 + * 可用区A:cn-shanghai-a + * 可用区B:cn-shanghai-b + * 可用区C:cn-shanghai-c + * 可用区E:cn-shanghai-e + * 商用 + * 华南1 + * 广州 + * cn-guangzhou + * 3 + * 可用区A:cn-guangzhou-a + * 可用区B:cn-guangzhou-b + * 可用区C:cn-guangzhou-c + * 商用 + * 中国香港 + * 香港 + * cn-hongkong + * 2 + * 可用区A:cn-hongkong-a + * 可用区B:cn-hongkong-b + * 商用 + * 其他国家和地区 + * 亚太东南 + * 柔佛 + * ap-southeast-1 + * 2 + * 可用区A:ap-southeast-1a + * 可用区B:ap-southeast-1b + * 商用 + * 雅加达 + * ap-southeast-3 + * 2 + * 可用区A:ap-southeast-3a + * 可用区B:ap-southeast-3b + * 商用 + */ + { label: "北京", value: "cn-beijing" }, + { label: "上海", value: "cn-shanghai" }, + { label: "广州", value: "cn-guangzhou" }, + { label: "深圳", value: "cn-shenzhen" }, + { label: "杭州", value: "cn-hangzhou" }, + { label: "南京", value: "cn-north-1" }, + { label: "青岛", value: "cn-qingdao" }, + { label: "重庆", value: "cn-chengdu" }, + { label: "香港", value: "cn-hongkong" }, + { label: "柔佛", value: "ap-southeast-1" }, + { label: "雅加达", value: "ap-southeast-3" } + + ] + }, + value: "cn-beijing", + required: true + }) + regionId!: string; + + + @TaskInput( + createRemoteSelectInputDefine({ + title: "监听器列表", + helper: "选择要部署证书的监听器\n需要在监听器中选择证书中心,进行跨服务访问授权", + action: VolcengineDeployToCLB.prototype.onGetListenerList.name, + watches: ["certDomains", "accessId", "regionId"], + required: true + }) + ) + listenerList!: string | string[]; + + + async onInstance() { + } + + async execute(): Promise { + this.logger.info("开始部署证书到火山引擎CLB"); + const access = await this.getAccess(this.accessId); + const certService = await this.getCertService(access); + let certId = this.cert; + if (typeof certId !== "string") { + const certInfo = this.cert as CertInfo; + this.logger.info(`开始上传证书`); + certId = await certService.ImportCertificate({ + certName:this.appendTimeSuffix("certd"), + cert:certInfo + }); + this.logger.info(`上传证书成功:${certId}`); + } else { + this.logger.info(`使用已有证书ID:${certId}`); + } + + const service = await this.getClbService(); + for (const listener of this.listenerList) { + this.logger.info(`开始部署监听器${listener}证书`); + await service.request({ + action: "ModifyListenerAttributes", + query: { + ListenerId: listener, + CertificateSource: "cert_center", + CertCenterCertificateId: certId + } + }); + this.logger.info(`部署监听器${listener}证书成功`); + } + + this.logger.info("部署完成"); + } + + + private async getCertService(access: VolcengineAccess) { + const client = new VolcengineClient({ + logger: this.logger, + access, + http: this.http + }); + + return await client.getCertCenterService(); + } + + async onGetClbList(data: any) { + if (!this.accessId) { + throw new Error("请选择Access授权"); + } + const service = await this.getClbService(); + const res = await service.request({ + action: "DescribeLoadBalancers", + method: "GET", + query: { + PageSize: 100 + }, + }); + + const list = res.Result.LoadBalancers; + + return list.map((item: any) => { + return { + value: item.LoadBalancerId, + label: `${item.LoadBalancerName}<${item.Description}>` + }; + }); + } + + private async getClbService() { + const access = await this.getAccess(this.accessId); + + const client = new VolcengineClient({ + logger: this.logger, + access, + http: this.http + }); + + const service = await client.getClbService({ + region: this.regionId + }); + return service; + } + + async onGetListenerList(data: any) { + if (!this.accessId) { + throw new Error("请选择Access授权"); + } + const service = await this.getClbService(); + + const res = await service.request({ + action: "DescribeListeners", + method: "GET", + query: { + PageSize: 100, + Protocol: "HTTPS" + }, + }); + + const list = res.Result.Listeners; + if (!list || list.length === 0) { + throw new Error("找不到HTTPS类型的负载均衡监听器,您也可以手动输入监听器ID"); + } + return list.map((item: any) => { + return { + value: item.ListenerId, + label: `${item.ListenerName}<${item.Description}:${item.ListenerId}>` + }; + }); + } +} + +new VolcengineDeployToCLB(); diff --git a/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/plugin-upload-to-cert-center.ts b/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/plugin-upload-to-cert-center.ts new file mode 100644 index 00000000..b289ae70 --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-volcengine/plugins/plugin-upload-to-cert-center.ts @@ -0,0 +1,77 @@ +import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput, TaskOutput } from "@certd/pipeline"; +import { createCertDomainGetterInputDefine } from "@certd/plugin-lib"; +import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert"; +import { VolcengineAccess } from "../access.js"; +import { VolcengineClient } from "../ve-client.js"; + +@IsTaskPlugin({ + name: 'VolcengineUploadToCertCenter', + title: '火山引擎-上传证书至证书中心', + icon: 'svg:icon-volcengine', + group: pluginGroups.volcengine.key, + desc: '上传证书至火山引擎证书中心', + default: { + strategy: { + runStrategy: RunStrategy.SkipWhenSucceed, + }, + }, +}) +export class VolcengineUploadToCertCenter extends AbstractTaskPlugin { + @TaskInput({ + title: '域名证书', + helper: '请选择前置任务输出的域名证书', + component: { + name: 'output-selector', + from: [...CertApplyPluginNames], + }, + required: true, + }) + cert!: CertInfo; + + @TaskInput(createCertDomainGetterInputDefine({ props: { required: false } })) + certDomains!: string[]; + + + @TaskInput({ + title: 'Access授权', + helper: '火山引擎AccessKeyId、AccessKeySecret', + component: { + name: 'access-selector', + type: 'volcengine', + }, + required: true, + }) + accessId!: string; + + + @TaskOutput({ + title: '上传成功后的火山引擎证书Id', + }) + volcengineCertId?: string; + + async onInstance() {} + async execute(): Promise { + this.logger.info('开始上传证书到证书中心'); + const access = await this.getAccess(this.accessId); + + const client = await this.getClient(access) + const service = await client.getCertCenterService() + const certInfo = this.cert + this.logger.info(`开始上传证书`) + this.volcengineCertId = await service.ImportCertificate({ + certName: this.appendTimeSuffix('certd'), + cert: certInfo + }) + this.logger.info(`上传完成:${this.volcengineCertId}`); + } + + + async getClient(access: VolcengineAccess) { + return new VolcengineClient({ + logger: this.logger, + access, + http:this.http + }) + } +} +new VolcengineUploadToCertCenter(); diff --git a/packages/ui/certd-server/src/plugins/plugin-volcengine/ve-client.ts b/packages/ui/certd-server/src/plugins/plugin-volcengine/ve-client.ts new file mode 100644 index 00000000..445340df --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-volcengine/ve-client.ts @@ -0,0 +1,122 @@ +import { VolcengineAccess } from "./access.js"; +import { HttpClient, ILogger } from "@certd/basic"; + +export type VolcengineOpts = { + access: VolcengineAccess + logger: ILogger + http: HttpClient +} + +export class VolcengineClient { + opts: VolcengineOpts; + CommonService: any; + + constructor(opts: VolcengineOpts) { + this.opts = opts; + } + + async getCertCenterService() { + const CommonService = await this.getServiceCls(); + + const service = new CommonService({ + serviceName: "certificate_service", + defaultVersion: "2024-10-01" + }); + service.setAccessKeyId(this.opts.access.accessKeyId); + service.setSecretKey(this.opts.access.secretAccessKey); + service.setRegion("cn-beijing"); + + service.ImportCertificate = async (body: { certName: string, cert: any }) => { + const { certName, cert } = body; + const res = await service.request({ + action: "ImportCertificate", + method: "POST", + body: { + Tag: certName, + Repeatable: false, + CertificateInfo: { + CertificateChain: cert.crt, + PrivateKey: cert.key + } + } + }); + return res.Result.InstanceId || res.Result.RepeatId + }; + return service; + } + + async getClbService(opts: { region?: string }) { + const CommonService = await this.getServiceCls(); + + const service = new CommonService({ + serviceName: "clb", + defaultVersion: "2020-04-01" + }); + service.setAccessKeyId(this.opts.access.accessKeyId); + service.setSecretKey(this.opts.access.secretAccessKey); + service.setRegion(opts.region); + + return service; + } + + async getAlbService(opts: { region?: string }) { + const CommonService = await this.getServiceCls(); + + const service = new CommonService({ + serviceName: "alb", + defaultVersion: "2020-04-01" + }); + service.setAccessKeyId(this.opts.access.accessKeyId); + service.setSecretKey(this.opts.access.secretAccessKey); + service.setRegion(opts.region); + + return service; + } + + async getServiceCls() { + if (this.CommonService) { + return this.CommonService; + } + const { Service } = await import("@volcengine/openapi"); + + class CommonService extends Service { + Generic: any; + + constructor(options: { + serviceName: string; + defaultVersion: string; + }) { + super(Object.assign({ host: "open.volcengineapi.com" }, options)); + this.Generic = async (req: { action: string, body?: any, method?: string, query?: any }) => { + const { action, method, body, query } = req; + return await this.fetchOpenAPI({ + Action: action, + Version: options.defaultVersion, + method: method as any, + headers: { + "content-type": "application/json" + }, + query: query || {}, + data: body + }); + }; + } + + async request(req: { action: string, body?: any, method?: string, query?: any }) { + const res = await this.Generic(req); + if (res.errorcode) { + throw new Error(`${res.errorcode}:${res.message}`); + } + if (res.ResponseMetadata?.Error) { + throw new Error(res.ResponseMetadata?.Error?.Message); + } + return res; + } + } + + this.CommonService = CommonService; + return CommonService; + } + + +}