perf: 添加 FlexCDN 更新证书插件

- 新增 FlexCDNRefreshCert 插件类,实现更新证书功能
- 添加 FlexCDNAccess 授权类和 FlexCDNClient 客户端类
- 实现获取证书列表和更新证书的 API 调用
- 提供插件配置界面和执行逻辑
pull/409/head
xiaojunnuo 2025-05-16 00:04:52 +08:00
parent 3e2101aa5b
commit bf040d4c42
5 changed files with 323 additions and 0 deletions

View File

@ -0,0 +1,110 @@
import { IsAccess, AccessInput, BaseAccess } from "@certd/pipeline";
import { HttpClient } from "@certd/basic";
import { FlexCDNClient } from "./client.js";
/**
*/
@IsAccess({
name: "flexcdn",
title: "FlexCDN授权",
desc: "",
icon: "svg:icon-lucky"
})
export class FlexCDNAccess extends BaseAccess {
@AccessInput({
title: "接口地址",
component: {
placeholder: "http://xxxxxxx:8080",
name: "a-input",
vModel: "value"
},
required: true
})
endpoint!: string;
@AccessInput({
title: "用户类型",
component: {
placeholder: "请选择",
name: "a-select",
vModel: "value",
options: [
{
value: "user",
label: "普通用户"
},
{
value: "admin",
label: "管理员"
}
]
},
required: true
})
type!: string;
@AccessInput({
title: "accessKeyId",
component: {
placeholder: "accessKeyId",
component: {
name: "a-input",
vModel: "value"
}
},
encrypt: false,
required: true
})
accessKeyId!: string;
@AccessInput({
title: "accessKey",
component: {
placeholder: "accessKey",
component: {
name: "a-input",
vModel: "value"
}
},
encrypt: true,
required: true
})
accessKey!: string;
@AccessInput({
title: "忽略证书校验",
component: {
name: "a-switch",
vModel: "checked"
},
encrypt: false,
required: true
})
skipSslVerify!: boolean;
@AccessInput({
title: "测试",
component: {
name: "api-test",
action: "TestRequest"
},
helper: "点击测试接口看是否正常"
})
testRequest = true;
async onTestRequest() {
const http: HttpClient = this.ctx.http;
const client = new FlexCDNClient({
logger: this.ctx.logger,
http,
access: this
});
const token = await client.getToken();
if (token) {
return "ok";
}
throw "测试失败,未知错误";
}
}
new FlexCDNAccess();

View File

@ -0,0 +1,81 @@
import { HttpClient, HttpRequestConfig, ILogger } from "@certd/basic";
import { FlexCDNAccess } from "./access.js";
export class FlexCDNClient {
http: HttpClient;
logger: ILogger;
access: FlexCDNAccess;
token: string;
constructor(opts: { logger: ILogger; http: HttpClient; access: FlexCDNAccess }) {
this.logger = opts.logger;
this.http = opts.http;
this.access = opts.access;
}
async getToken() {
/*
2APIAccessToken
/APIAccessTokenService/getAPIAccessToken
POST
{
"type": "admin",
"accessKeyId": "zr9cmR42AEZxRyIV",
"accessKey": "2w5p5NSZZuplUPsfPMzM7dFmTrI7xyja"
}
type - AccessKey userAccessKey admin
accessKeyId accessKey 1AccessKey
{
"code": 200,
"data": {
"token": "IKNSMufZ1vDiXp5rSd9QR01m1174Oum5sah4amWFgbRb7lOKjuk62Spl7hgcazctzGhGG7jPgfmYUPojulC0FK5cLbrj8n7kxW7BtSawH9gWW14IWOzBY6UcpyXQndFu",
"expiresAt": 1609686945
},
"message": "ok"
}
*/
const res = await this.doRequest({
url: "/APIAccessTokenService/getAPIAccessToken",
method: "POST",
data: {
type: this.access.type,
accessKeyId: this.access.accessKeyId,
accessKey: this.access.accessKey,
},
});
this.token = res.token
return this.token
}
async doRequest(req: HttpRequestConfig) {
const headers = {
...req.headers,
}
if(this.token){
headers[ "X-Cloud-Access-Token"] = this.token
}
const res = await this.http.request({
...req,
headers,
baseURL: this.access.endpoint,
logRes:false,
logParams:false,
skipSslVerify: true,
});
if (res.code === 200) {
return res.data;
} else {
throw new Error(res.message);
}
}
}

View File

@ -0,0 +1,3 @@
export * from "./plugins/index.js";
export * from "./access.js";
export * from "./client.js";

View File

@ -0,0 +1 @@
export * from "./plugin-refresh-cert.js";

View File

@ -0,0 +1,128 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib";
import { FlexCDNAccess } from "../access.js";
import { FlexCDNClient } from "../client.js";
@IsTaskPlugin({
//命名规范,插件类型+功能就是目录plugin-demo中的demo大写字母开头驼峰命名
name: "FlexCDNRefreshCert",
title: "FlexCDN-更新证书",
icon: "svg:icon-lucky",
//插件分组
group: pluginGroups.cdn.key,
needPlus: false,
default: {
//默认值配置照抄即可
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
})
//类名规范跟上面插件名称name一致
export class FlexCDNRefreshCert extends AbstractTaskPlugin {
//证书选择,此项必须要有
@TaskInput({
title: "域名证书",
helper: "请选择前置任务输出的域名证书",
component: {
name: "output-selector",
from: [...CertApplyPluginNames],
},
// required: true, // 必填
})
cert!: CertInfo;
@TaskInput(createCertDomainGetterInputDefine({ props: { required: false } }))
certDomains!: string[];
//授权选择框
@TaskInput({
title: "FlexCDN授权",
component: {
name: "access-selector",
type: "FlexCDN", //固定授权类型
},
required: true, //必填
})
accessId!: string;
//
@TaskInput(
createRemoteSelectInputDefine({
title: "证书Id",
helper: "要更新的Flex证书id",
action: FlexCDNRefreshCert.prototype.onGetCertList.name,
})
)
certList!: number[];
//插件实例化时执行的方法
async onInstance() {}
//插件执行方法
async execute(): Promise<void> {
const access: FlexCDNAccess = await this.getAccess<FlexCDNAccess>(this.accessId);
const client = new FlexCDNClient({
http: this.ctx.http,
logger: this.logger,
access,
});
await client.getToken();
for (const item of this.certList) {
this.logger.info(`开始更新证书:${item}`);
const res = await client.doRequest({
url: `/SSLCertService/findEnabledSSLCertConfig`,
data: {
sslCertId: item,
},
});
await client.doRequest({
url: `/SSLCertService/updateSSLCert`,
data: {
...res,
sslCertId: item,
certData: this.cert.crt,
keyData: this.cert.key,
},
});
this.logger.info(`更新证书${item}成功`);
}
this.logger.info("部署完成");
}
async onGetCertList() {
const access: FlexCDNAccess = await this.getAccess<FlexCDNAccess>(this.accessId);
const client = new FlexCDNClient({
http: this.ctx.http,
logger: this.logger,
access,
});
await client.getToken();
const res = await client.doRequest({
url: "/SSLCertService/listSSLCerts",
data: { size: 1000},
method: "POST",
});
const list = res.sslCertsJSON;
if (!list || list.length === 0) {
throw new Error("没有找到证书,请先在控制台上传一次证书且关联网站");
}
const options = list.map((item: any) => {
return {
label: `${item.name}<${item.id}-${item.firstServerName}>`,
value: item.id,
domain: JSON.parse(item.serverNamesJSON),
};
});
return this.ctx.utils.options.buildGroupOptions(options, this.certDomains);
}
}
//实例化一下,注册插件
new FlexCDNRefreshCert();