perf: 支持部署证书到火山dcdn

This commit is contained in:
xiaojunnuo
2025-04-29 18:40:13 +08:00
parent a63d687f1c
commit 5f85219495
5 changed files with 248 additions and 15 deletions
@@ -0,0 +1,204 @@
import {AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput} from "@certd/pipeline";
import {createCertDomainGetterInputDefine, createRemoteSelectInputDefine} from "@certd/plugin-lib";
import {CertApplyPluginNames, CertInfo, CertReader} from "@certd/plugin-cert";
import {VolcengineAccess} from "../access.js";
import {VolcengineClient} from "../ve-client.js";
@IsTaskPlugin({
name: "VolcengineDeployToDCDN",
title: "火山引擎-部署证书至DCDN",
icon: "svg:icon-volcengine",
group: pluginGroups.volcengine.key,
desc: "部署至火山引擎全站加速",
// showRunStrategy: true,
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed
}
}
})
export class VolcengineDeployToDCDN extends AbstractTaskPlugin {
@TaskInput({
title: "域名证书",
helper: "请选择前置任务输出的域名证书",
component: {
name: "output-selector",
from: [...CertApplyPluginNames]
},
required: true
})
cert!: CertInfo;
@TaskInput(createCertDomainGetterInputDefine({props: {required: false}}))
certDomains!: string[];
@TaskInput({
title: "自动匹配",
helper: "是否根据证书自动匹配合适的DCDN域名进行部署",
value: false,
component: {
name: "a-switch",
type: "checked"
},
required: true
})
autoMatch!: boolean;
@TaskInput({
title: "Access授权",
helper: "火山引擎AccessKeyId、AccessKeySecret",
component: {
name: "access-selector",
type: "volcengine"
},
required: true
})
accessId!: string;
@TaskInput(
createRemoteSelectInputDefine({
title: "DCDN域名",
helper: "选择要部署证书的DCDN域名",
action: VolcengineDeployToDCDN.prototype.onGetDomainList.name,
watches: ["certDomains", "accessId"],
required: true,
mergeScript: `
return {
show: ctx.compute(({form})=>{
return !form.autoMatch
})
}
`
})
)
domainList!: string | string[];
async onInstance() {
}
async uploadCert(client: VolcengineClient) {
const certService = await client.getCertCenterService();
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}`);
}
return certId
}
async execute(): Promise<void> {
this.logger.info("开始部署证书到火山引擎DCDN");
const client = await this.getClient();
let certId = await this.uploadCert(client);
const service = await client.getDCDNService();
this.certDomains = new CertReader(this.cert).getAllDomains()
let domainList = this.domainList
if (!this.autoMatch) {
//手动根据域名部署
if (!this.domainList || this.domainList.length === 0) {
throw new Error("域名列表不能为空");
}
} else {
//自动匹配
const options = await this.getDomainOptions(service);
const grouped = this.ctx.utils.options.groupByDomain(options, this.certDomains);
const matched = grouped.matched
domainList = matched.map(item => item.domain)
if (domainList.length === 0) {
this.logger.warn("没有匹配到域名,跳过部署")
this.logger.info("当前证书域名:", this.certDomains)
this.logger.info("当前DCDN域名:", grouped.notMatched.map(item => item.domain))
return
}
}
//域名十个十个的分割
for (let i = 0; i < domainList.length; i += 10) {
const batch = domainList.slice(i, i + 10);
this.logger.info(`开始部署证书到域名:${batch}`);
const res = await service.request({
action: "CreateCertBind",
method: "POST",
body: {
"DomainNames": batch,
"CertSource": "volc",
"CertId": certId
},
version: "2021-04-01"
});
this.logger.info(`部署证书到域名成功:`,JSON.stringify(res));
}
this.logger.info("部署完成");
}
async getClient() {
const access = await this.getAccess<VolcengineAccess>(this.accessId);
return new VolcengineClient({
logger: this.logger,
access,
http: this.http
})
}
async onGetDomainList(data: any) {
if (!this.accessId) {
throw new Error("请选择Access授权");
}
const client = await this.getClient();
const service = await client.getDCDNService();
const options = await this.getDomainOptions(service);
return this.ctx.utils.options.buildGroupOptions(options, this.certDomains);
}
private async getDomainOptions(service: any) {
const res = await service.request({
method: "POST",
action: "DescribeUserDomains",
body: {
"PageSize": 1000
}
})
const list = res.Result?.Domains;
if (!list || list.length === 0) {
throw new Error("找不到DCDN域名,您也可以手动输入域名");
}
const options = list.map((item: any) => {
return {
value: item.Domain,
label: `${item.Domain}<${item.Scope}>`,
domain: item.Domain
};
});
return options;
}
}
new VolcengineDeployToDCDN();
@@ -100,6 +100,19 @@ export class VolcengineClient {
return service;
}
async getDCDNService( opts?: { }) {
const CommonService = await this.getServiceCls();
const service = new CommonService({
serviceName: "dcdn",
defaultVersion: "2023-01-01"
});
service.setAccessKeyId(this.opts.access.accessKeyId);
service.setSecretKey(this.opts.access.secretAccessKey);
service.setRegion("cn-north-1");
return service;
}
async getServiceCls() {
if (this.CommonService) {
return this.CommonService;
@@ -114,11 +127,11 @@ export class VolcengineClient {
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;
this.Generic = async (req: { action: string, body?: any, method?: string, query?: any ,version?:string}) => {
const { action, method, body, query,version } = req;
return await this.fetchOpenAPI({
Action: action,
Version: options.defaultVersion,
Version: version||options.defaultVersion,
method: method as any,
headers: {
"content-type": "application/json"
@@ -129,8 +142,11 @@ export class VolcengineClient {
};
}
async request(req: { action: string, body?: any, method?: string, query?: any }) {
async request(req: { action: string, body?: any, method?: string, query?: any,version?:string }) {
const res = await this.Generic(req);
if (res ==="Not Found"){
throw new Error(`${res} (检查method)`);
}
if (res.errorcode) {
throw new Error(`${res.errorcode}:${res.message}`);
}
@@ -146,4 +162,5 @@ export class VolcengineClient {
}
}