perf: 部署插件支持ucloud alb

This commit is contained in:
xiaojunnuo
2026-01-27 00:32:23 +08:00
parent 998de0f9a0
commit 640950d4c8
4 changed files with 308 additions and 33 deletions

View File

@@ -1,5 +1,5 @@
import { AccessInput, BaseAccess, IsAccess } from '@certd/pipeline';
import { isDev } from '../../utils/env.js';
import { AccessInput, BaseAccess, IsAccess, Pager, PageRes, PageSearch } from '@certd/pipeline';
import { DomainRecord } from '@certd/plugin-lib';
/**
* 这个注解将注册一个授权配置
@@ -8,42 +8,125 @@ import { isDev } from '../../utils/env.js';
@IsAccess({
name: 'demo',
title: '授权插件示例',
icon: 'clarity:plugin-line',
desc: '',
icon: 'clarity:plugin-line', //插件图标
desc: '这是一个示例授权插件,用于演示如何实现一个授权插件',
})
export class DemoAccess extends BaseAccess {
/**
* 授权属性配置
*/
@AccessInput({
title: '授权方式',
value: 'apiKey', //默认值
component: {
name: "a-select", //基于antdv的输入组件
vModel: "value", // v-model绑定的属性名
options: [ //组件参数
{ label: "API密钥推荐", value: "apiKey" },
{ label: "账号密码", value: "account" },
],
placeholder: 'demoKeyId',
},
required: true,
})
apiType = '';
/**
* 授权属性配置
*/
@AccessInput({
title: '密钥Id',
component: {
name:"a-input",
allowClear: true,
placeholder: 'demoKeyId',
},
required: true,
})
demoKeyId = '';
/**
* 授权属性配置
*/
@AccessInput({
//标题
title: '密钥串',
component: {
//input组件的placeholder
placeholder: 'demoKeySecret',
},
//是否必填
required: true,
//改属性是否需要加密
encrypt: true,
title: '密钥',//标题
required: true, //text组件可以省略
encrypt: true, //该属性是否需要加密
})
//属性名称
demoKeySecret = '';
}
if (isDev()) {
//你的实现 要去掉这个if不然生产环境将不会显示
new DemoAccess();
@AccessInput({
title: "测试",
component: {
name: "api-test",
action: "TestRequest"
},
helper: "点击测试接口是否正常"
})
testRequest = true;
/**
* 会通过上面的testRequest参数在ui界面上生成测试按钮供用户测试接口调用是否正常
*/
async onTestRequest() {
await this.GetDomainList({});
return "ok"
}
/**
* 获api接口示例 取域名列表,
*/
async GetDomainList(req: PageSearch): Promise<PageRes<DomainRecord>> {
//输出日志必须使用ctx.logger
this.ctx.logger.info(`获取域名列表req:${JSON.stringify(req)}`);
const pager = new Pager(req);
const resp = await this.doRequest({
action: "ListDomains",
data: {
domain: req.searchKey,
offset: pager.getOffset(),
limit: pager.pageSize,
}
});
const total = resp?.TotalCount || 0;
let list = resp?.DomainList?.map((item) => {
item.domain = item.Domain;
item.id = item.DomainId;
return item;
})
return {
total,
list
};
}
// 还可以继续编写API
/**
* 通用api调用方法, 具体如何构造请求体需参考对应应用的API文档
*/
async doRequest(req: { action: string, data?: any }) {
/**
this.ctx中包含很多有用的工具类
type AccessContext = {
http: HttpClient;
logger: ILogger;
utils: typeof utils;
accessService: IAccessService;
}
*/
const res = await this.ctx.http.request({
url: "https://api.demo.cn/api/",
method: "POST",
data: {
Action: req.action,
Body: req.data
}
});
if (res.Code !== 0) {
//异常处理
throw new Error(res.Message || "请求失败");
}
return res.Resp;
}
}

View File

@@ -1,4 +1,4 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { AbstractTaskPlugin, IsTaskPlugin, PageSearch, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { CertInfo, CertReader } from '@certd/plugin-cert';
import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from '@certd/plugin-lib';
import { optionsUtils } from '@certd/basic';
@@ -171,7 +171,7 @@ export class DemoTest extends AbstractTaskPlugin {
}
//此方法演示,如何让前端在添加插件时可以从后端获取选项,这里是后端返回选项的方法
async onGetSiteList() {
async onGetSiteList(req: PageSearch) {
if (!this.accessId) {
throw new Error('请选择Access授权');
}
@@ -179,13 +179,7 @@ export class DemoTest extends AbstractTaskPlugin {
// @ts-ignore
const access = await this.getAccess(this.accessId);
// const siteRes = await this.ctx.http.request({
// url: '你的服务端获取选项的请求地址',
// method: 'GET',
// data: {
// token:access.xxxx
// }, //请求参数
// });
// const siteRes = await access.GetDomainList(req);
//以下是模拟数据
const siteRes = [
{ id: 1, siteName: 'site1.com' },
@@ -204,5 +198,3 @@ export class DemoTest extends AbstractTaskPlugin {
return optionsUtils.buildGroupOptions(options, this.certDomains);
}
}
//实例化一下,注册插件
new DemoTest();

View File

@@ -1,3 +1,4 @@
export * from './plugin-deploy-to-cdn.js';
export * from './plugin-upload-to-ussl.js';
export * from './plugin-deploy-to-waf.js';
export * from './plugin-deploy-to-alb.js';

View File

@@ -0,0 +1,199 @@
import { AbstractTaskPlugin, IsTaskPlugin, PageSearch, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib";
import { UCloudAccess } from "../access.js";
@IsTaskPlugin({
name: "UCloudDeployToALB",
title: "UCloud-部署到ALB",
desc: "将证书部署到UCloud ALB(应用负载均衡)",
icon: "svg:icon-ucloud",
group: pluginGroups.ucloud.key,
needPlus: false,
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed
}
}
})
export class UCloudDeployToALB extends AbstractTaskPlugin {
@TaskInput({
title: "域名证书",
helper: "请选择前置任务输出的域名证书",
component: {
name: "output-selector",
from: [...CertApplyPluginNames, ":UCloudCertId:"]
}
})
cert!: CertInfo | { type: string, id: number, name: string };
@TaskInput(createCertDomainGetterInputDefine({ props: { required: false } }))
certDomains!: string[];
@TaskInput({
title: "UCloud授权",
component: {
name: "access-selector",
type: "ucloud"
},
required: true
})
accessId!: string;
@TaskInput(
createRemoteSelectInputDefine({
title: "负载均衡实例",
helper: "选择ULB负载均衡实例",
action: UCloudDeployToALB.prototype.onGetULBList.name
})
)
ulbId!: string;
@TaskInput(
createRemoteSelectInputDefine({
title: "监听器列表",
helper: "要更新的ALB监听器列表",
action: UCloudDeployToALB.prototype.onGetVServerList.name
})
)
vServerList!: string[];
async onInstance() {
}
async execute(): Promise<void> {
const access = await this.getAccess<UCloudAccess>(this.accessId);
let certType = "ussl"
let certId = 0
if (this.cert && typeof this.cert === 'object' && 'id' in this.cert) {
certId = this.cert.id
} else {
const cert = await access.SslUploadCert({
cert: this.cert as CertInfo
});
certId = cert.id
}
for (const item of this.vServerList) {
this.logger.info(`----------- 开始更新监听器:${item}`);
await this.deployToAlb({
access: access,
ulbId: this.ulbId,
vServerId: item,
certId: certId,
certType: certType
});
this.logger.info(`----------- 更新监听器证书${item}成功`);
}
this.logger.info("部署完成");
}
async deployToAlb(req: { access: any, ulbId: string, vServerId: string, certId: number, certType: string }) {
const { access, ulbId, vServerId, certId, certType } = req
this.logger.info(`----------- 获取监听器${vServerId}配置`);
const vServerRes = await access.invoke({
"Action": "DescribeVServer",
"ProjectId": access.projectId,
"ULBId": ulbId,
"VServerId": vServerId
});
const vServer = vServerRes.VServerSet?.[0];
if (!vServer) {
throw new Error(`没有找到监听器${vServerId}`);
}
this.logger.info(`----------- 更新ALB监听器HTTPS配置${vServerId}`);
const resp = await access.invoke({
"Action": "UpdateVServerAttribute",
"ProjectId": access.projectId,
"ULBId": ulbId,
"VServerId": vServerId,
"SSLMode": "port",
"CertificateId": certId,
"CertificateType": certType
});
this.logger.info(`----------- 部署ALB证书${vServerId}成功,${JSON.stringify(resp)}`);
}
async onGetULBList(req: PageSearch = {}) {
const access = await this.getAccess<UCloudAccess>(this.accessId);
const pageNo = req.pageNo ?? 1;
const pageSize = req.pageSize ?? 100;
const res = await access.invoke({
"Action": "DescribeULB",
"ProjectId": access.projectId,
"Offset": (pageNo - 1) * pageSize,
"Limit": pageSize
});
const total = res.TotalCount || 0;
const list = res.Dataset || [];
if (!list || list.length === 0) {
throw new Error("没有找到ULB实例请先在控制台创建ULB实例");
}
const options = list.map((item: any) => {
return {
label: `${item.Name || item.ULBId}<${item.ULBId}>`,
value: `${item.ULBId}`
};
});
return {
list: options,
total: total,
pageNo: pageNo,
pageSize: pageSize
};
}
async onGetVServerList(req: PageSearch = {}) {
const access = await this.getAccess<UCloudAccess>(this.accessId);
if (!this.ulbId) {
throw new Error("请先选择ULB负载均衡实例");
}
const pageNo = req.pageNo ?? 1;
const pageSize = req.pageSize ?? 100;
const res = await access.invoke({
"Action": "DescribeVServer",
"ProjectId": access.projectId,
"ULBId": this.ulbId,
"Offset": (pageNo - 1) * pageSize,
"Limit": pageSize
});
const total = res.TotalCount || 0;
const list = res.VServerSet || [];
if (!list || list.length === 0) {
throw new Error("没有找到ALB监听器请先在控制台创建ALB实例和监听器");
}
const options = list.map((item: any) => {
return {
label: `${item.VServerName || item.VServerId}<${item.VServerId}>`,
value: `${item.VServerId}`,
domain: item.VServerName || item.VServerId
};
});
return {
list: this.ctx.utils.options.buildGroupOptions(options, this.certDomains),
total: total,
pageNo: pageNo,
pageSize: pageSize
};
}
}
new UCloudDeployToALB();