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