feat: 【破坏性更新】插件改为metadata加载模式,plugin-cert、plugin-lib包部分代码转移到certd-server中,影响自定义插件,需要修改相关import引用

ssh、aliyun、tencent、qiniu、oss等 access和client需要转移import
This commit is contained in:
xiaojunnuo
2025-12-31 17:01:37 +08:00
parent 9c26598831
commit a3fb24993d
312 changed files with 14321 additions and 597 deletions
@@ -0,0 +1,35 @@
import { IsAccess, AccessInput, BaseAccess } from "@certd/pipeline";
/**
* 这个注解将注册一个授权配置
* 在certd的后台管理系统中,用户可以选择添加此类型的授权
*/
@IsAccess({
name: "yidunrcdn",
title: "易盾rcdn授权",
icon: "material-symbols:shield-outline",
desc: "易盾CDN,每月免费30G[注册即领](https://rhcdn.yiduncdn.com/register?code=8mn536rrzfbf8)",
})
export class YidunRcdnAccess extends BaseAccess {
@AccessInput({
title: "账户",
component: {
placeholder: "手机号",
},
required: true,
encrypt: true,
})
username = "";
@AccessInput({
title: "密码",
component: {
placeholder: "password",
},
required: true,
encrypt: true,
})
password = "";
}
new YidunRcdnAccess();
@@ -0,0 +1,36 @@
import { IsAccess, AccessInput, BaseAccess } from "@certd/pipeline";
/**
* 这个注解将注册一个授权配置
* 在certd的后台管理系统中,用户可以选择添加此类型的授权
*/
@IsAccess({
name: "yfysms",
title: "易发云短信",
icon: "material-symbols:shield-outline",
desc: "sms.yfyidc.cn/",
})
export class YfySmsAccess extends BaseAccess {
@AccessInput({
title: "KeyID",
component: {
placeholder: "api_key",
},
helper: "[获取密钥](http://sms.yfyidc.cn/user/index#)",
required: true,
encrypt: true,
})
keyId = "";
@AccessInput({
title: "KeySecret",
component: {
placeholder: "",
},
required: true,
encrypt: true,
})
keySecret = "";
}
new YfySmsAccess();
@@ -0,0 +1,37 @@
import { IsAccess, AccessInput, BaseAccess } from "@certd/pipeline";
/**
* 这个注解将注册一个授权配置
* 在certd的后台管理系统中,用户可以选择添加此类型的授权
*/
@IsAccess({
name: "yidun",
title: "易盾DCDN授权",
icon: "material-symbols:shield-outline",
desc: "https://user.yiduncdn.com",
})
export class YidunAccess extends BaseAccess {
@AccessInput({
title: "api_key",
component: {
placeholder: "api_key",
},
helper: "http://user.yiduncdn.com/console/index.html#/account/config/api,点击开启后获取",
required: true,
encrypt: true,
})
apiKey = "";
@AccessInput({
title: "api_secret",
component: {
placeholder: "api_secret",
},
helper: "http://user.yiduncdn.com/console/index.html#/account/config/api,点击开启后获取",
required: true,
encrypt: true,
})
apiSecret = "";
}
new YidunAccess();
@@ -0,0 +1,4 @@
export * from "./plugins/index.js";
export * from "./access.js";
export * from "./access-rcdn.js";
export * from "./access-sms.js";
@@ -0,0 +1,2 @@
export * from "./plugin-deploy-to-cdn.js";
export * from "./plugin-deploy-to-rcdn.js";
@@ -0,0 +1,147 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
@IsTaskPlugin({
name: "YidunDeployToCDN",
title: "易盾-部署到易盾DCDN",
icon: "material-symbols:shield-outline",
group: pluginGroups.cdn.key,
desc: "主要是防御,http://user.yiduncdn.com/",
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
needPlus: false,
})
export class YidunDeployToCDNPlugin extends AbstractTaskPlugin {
//测试参数
@TaskInput({
title: "证书ID",
component: {
name: "a-input-number",
vModel: "value",
},
helper: "证书ID,在证书管理页面查看,每条记录都有证书id",
})
certId!: number;
@TaskInput({
title: "网站域名",
component: {
name: "a-input",
vModel: "value",
},
helper: "网站域名和证书ID选填其中一个,填了证书ID,则忽略网站域名",
})
domain!: number;
//证书选择,此项必须要有
@TaskInput({
title: "域名证书",
helper: "请选择前置任务输出的域名证书",
component: {
name: "output-selector",
from: [...CertApplyPluginNames],
},
required: true,
})
cert!: CertInfo;
//授权选择框
@TaskInput({
title: "易盾授权",
helper: "易盾CDN授权",
component: {
name: "access-selector",
type: "yidun",
},
required: true,
})
accessId!: string;
async onInstance() {}
async execute(): Promise<void> {
const { domain, certId, cert } = this;
if (!domain && !certId) {
throw new Error("证书ID和网站域名必须填写一个");
}
if (certId > 0) {
await this.updateByCertId(cert, certId);
} else {
await this.updateByDomain(cert);
}
}
private async updateByCertId(cert: CertInfo, certId: number) {
this.logger.info(`更新证书,证书ID:${certId}`);
const url = `http://user.yiduncdn.com/v1/certs/${certId}`;
await this.doRequest(url, "PUT", {
cert: cert.crt,
key: cert.key,
});
}
async doRequest(url: string, method: string, data: any) {
const access = await this.getAccess(this.accessId);
const { apiKey, apiSecret } = access;
const http = this.ctx.http;
const res: any = await http.request({
url,
method,
headers: {
"api-key": apiKey,
"api-secret": apiSecret,
},
data,
});
if (res.code != 0) {
throw new Error(res.msg);
}
return res;
}
private async updateByDomain(cert: CertInfo) {
//查询站点
const siteUrl = "http://user.yiduncdn.com/v1/sites";
const res = await this.doRequest(siteUrl, "GET", { domain: this.domain });
if (res.data.length === 0) {
throw new Error(`未找到域名相关站点:${this.domain}`);
}
let site = null;
for (const row of res.data) {
if (row.domain === this.domain) {
site = row;
}
}
if (!site) {
throw new Error(`未找到域名匹配的站点:${this.domain}`);
}
if (site.https_listen?.cert) {
//有证书id
const certId = site.https_listen.cert;
await this.updateByCertId(cert, certId);
} else {
//创建证书
this.logger.info(`创建证书,域名:${this.domain}`);
const certUrl = `http://user.yiduncdn.com/v1/certs`;
const name = this.domain + "_" + new Date().getTime();
await this.doRequest(certUrl, "POST", {
name,
type: "custom",
cert: cert.crt,
key: cert.key,
});
const certs: any = await this.doRequest(certUrl, "GET", {
name,
});
const certId = certs.data[0].id;
const siteUrl = "http://user.yiduncdn.com/v1/sites";
await this.doRequest(siteUrl, "PUT", { id: site.id, https_listen: { cert: certId } });
}
}
}
new YidunDeployToCDNPlugin();
@@ -0,0 +1,168 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
import { createRemoteSelectInputDefine } from "@certd/plugin-lib";
import { YidunRcdnAccess } from "../access-rcdn.js";
@IsTaskPlugin({
name: "YidunDeployToRCDN",
title: "易盾-部署到易盾RCDN",
icon: "material-symbols:shield-outline",
group: pluginGroups.cdn.key,
desc: "易盾CDN,每月免费30G[注册即领](https://rhcdn.yiduncdn.com/register?code=8mn536rrzfbf8)",
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
needPlus: false,
})
export class YidunDeployToRCDNPlugin extends AbstractTaskPlugin {
//证书选择,此项必须要有
@TaskInput({
title: "域名证书",
helper: "请选择前置任务输出的域名证书",
component: {
name: "output-selector",
from: [...CertApplyPluginNames],
},
required: true,
})
cert!: CertInfo;
//授权选择框
@TaskInput({
title: "易盾RCDN授权",
helper: "易盾RCDN授权",
component: {
name: "access-selector",
type: "yidunrcdn",
},
required: true,
})
accessId!: string;
@TaskInput(
createRemoteSelectInputDefine({
title: "域名列表",
helper: "选择要部署证书的站点域名",
typeName: "YidunDeployToRCDNPlugin",
action: YidunDeployToRCDNPlugin.prototype.onGetDomainList.name,
})
)
domains!: string[];
async onInstance() {}
async execute(): Promise<void> {
const access = await this.getAccess<YidunRcdnAccess>(this.accessId);
const loginRes = await this.getLoginToken(access);
const curl = "https://rhcdn.yiduncdn.com/CdnDomainHttps/httpsConfiguration";
for (const domain of this.domains) {
// const data = {
// doMainId: domain,
// https: {
// https_status: "off"
// },
// }
// //先关闭https
// const res = await this.doRequest(curl, loginRes, data);
const cert = this.cert;
const update = {
doMainId: domain,
https: {
https_status: "on",
certificate_name: this.appendTimeSuffix("certd"),
certificate_source: "0",
certificate_value: cert.crt,
private_key: cert.key,
},
};
await this.doRequest(curl, loginRes, update);
this.logger.info(`站点${domain}证书更新成功`);
}
}
async getLoginToken(access: YidunRcdnAccess) {
const url = "https://rhcdn.yiduncdn.com/login/loginUser";
const data = {
userAccount: access.username,
userPwd: access.password,
remember: true,
};
const http = this.ctx.http;
const res: any = await http.request({
url,
method: "POST",
data,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
returnOriginRes: true,
});
if (!res.data?.success) {
throw new Error(res.data?.message);
}
const jsessionId = this.ctx.utils.request.getCookie(res, "JSESSIONID");
const token = res.data?.data;
return {
jsessionId,
token,
};
}
async getDomainList(loginRes: any) {
const url = "https://rhcdn.yiduncdn.com/CdnDomain/queryForDatatables";
const data = {
draw: 1,
start: 0,
length: 1000,
search: {
value: "",
regex: false,
},
};
const res = await this.doRequest(url, loginRes, data);
return res.data?.data;
}
private async doRequest(url: string, loginRes: any, data: any) {
const http = this.ctx.http;
const res: any = await http.request({
url,
method: "POST",
headers: {
Cookie: `JSESSIONID=${loginRes.jsessionId};kuocai_cdn_token=${loginRes.token}`,
},
data,
});
if (!res.success) {
throw new Error(res.message);
}
return res;
}
async onGetDomainList(data: any) {
if (!this.accessId) {
throw new Error("请选择Access授权");
}
const access = await this.getAccess<YidunRcdnAccess>(this.accessId);
const loginRes = await this.getLoginToken(access);
const list = await this.getDomainList(loginRes);
if (!list || list.length === 0) {
throw new Error("您账户下还没有站点域名,请先添加域名");
}
return list.map((item: any) => {
return {
label: `${item.domainName}<${item.id}>`,
value: item.id,
};
});
}
}
new YidunDeployToRCDNPlugin();