mirror of https://github.com/certd/certd
perf: 添加 FlexCDN 更新证书插件
- 新增 FlexCDNRefreshCert 插件类,实现更新证书功能 - 添加 FlexCDNAccess 授权类和 FlexCDNClient 客户端类 - 实现获取证书列表和更新证书的 API 调用 - 提供插件配置界面和执行逻辑pull/409/head
parent
3e2101aa5b
commit
bf040d4c42
|
@ -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();
|
|
@ -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() {
|
||||
|
||||
/*
|
||||
步骤2:调用API获取AccessToken
|
||||
接口地址
|
||||
/APIAccessTokenService/getAPIAccessToken
|
||||
请求方法
|
||||
POST。
|
||||
|
||||
请求参数
|
||||
{
|
||||
"type": "admin",
|
||||
"accessKeyId": "zr9cmR42AEZxRyIV",
|
||||
"accessKey": "2w5p5NSZZuplUPsfPMzM7dFmTrI7xyja"
|
||||
}
|
||||
其中
|
||||
type - 如果是用户(即平台用户)AccessKey,则值为 user;如果是管理员(即系统用户)AccessKey,则值为 admin;
|
||||
accessKeyId 和 accessKey 换成你在步骤1中创建的AccessKey对应的数据。
|
||||
响应结果
|
||||
{
|
||||
"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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
export * from "./plugins/index.js";
|
||||
export * from "./access.js";
|
||||
export * from "./client.js";
|
|
@ -0,0 +1 @@
|
|||
export * from "./plugin-refresh-cert.js";
|
|
@ -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();
|
Loading…
Reference in New Issue