Merge remote-tracking branch 'origin/v2-dev' into v2-dev

This commit is contained in:
xiaojunnuo
2025-04-03 11:40:56 +08:00
325 changed files with 422580 additions and 118 deletions
@@ -7,7 +7,7 @@ import { cloneDeep } from 'lodash-es';
export class BuiltInPluginService {
getList() {
const collection = pluginRegistry.storage;
const list = [];
let list = [];
for (const key in collection) {
const Plugin = collection[key];
if (Plugin?.define?.deprecated) {
@@ -15,11 +15,21 @@ export class BuiltInPluginService {
}
list.push({ ...Plugin.define, key });
}
list = list.sort((a, b) => {
return (a.order ?? 10 )- (b.order ?? 10);
});
return list;
}
getGroups() {
return cloneDeep(pluginGroups);
const groups:any = cloneDeep(pluginGroups);
for (const key in groups) {
const group = groups[key];
group.plugins = group.plugins.sort((a, b) => {
return (a.order ?? 10 )- (b.order ?? 10);
});
}
return groups;
}
getByType(type: string) {
@@ -17,3 +17,4 @@ export * from './plugin-aws/index.js';
export * from './plugin-dnsla/index.js';
export * from './plugin-upyun/index.js';
export * from './plugin-volcengine/index.js'
export * from './plugin-jdcloud/index.js'
@@ -34,7 +34,7 @@ export class CopyCertToLocalPlugin extends AbstractTaskPlugin {
component: {
name: 'a-select',
options: [
{ value: 'pem', label: 'pem,用于Nginx等大部分应用' },
{ value: 'pem', label: 'pem(crt),用于Nginx等大部分应用' },
{ value: 'pfx', label: 'pfx,一般用于IIS' },
{ value: 'der', label: 'der,一般用于Apache' },
{ value: 'jks', label: 'jks,一般用于JAVA应用' },
@@ -9,6 +9,7 @@ import { CertApplyPluginNames} from '@certd/plugin-cert';
icon: 'line-md:uploading-loop',
group: pluginGroups.host.key,
desc: 'SFTP上传证书到主机,然后SSH执行部署脚本命令',
order: 1,
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
@@ -33,7 +34,7 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin {
component: {
name: 'a-select',
options: [
{ value: 'pem', label: 'pemNginx等大部分应用' },
{ value: 'pem', label: 'pemcrtNginx等大部分应用' },
{ value: 'pfx', label: 'pfx,一般用于IIS' },
{ value: 'der', label: 'der,一般用于Apache' },
{ value: 'jks', label: 'jks,一般用于JAVA应用' },
@@ -0,0 +1,36 @@
import {AccessInput, BaseAccess, IsAccess} from '@certd/pipeline';
/**
* 这个注解将注册一个授权配置
* 在certd的后台管理系统中,用户可以选择添加此类型的授权
*/
@IsAccess({
name: 'jdcloud',
title: '京东云',
desc: '',
icon: 'svg:icon-jdcloud',
})
export class JDCloudAccess extends BaseAccess {
@AccessInput({
title: 'AccessKeyID',
component: {
placeholder: 'AccessKeyID',
},
helper:"[获取密钥](https://uc.jdcloud.com/account/accesskey)",
required: true,
})
accessKeyId = '';
@AccessInput({
title: 'SecretAccessKey',
component: {
placeholder: 'SecretAccessKey',
},
required: true,
encrypt: true,
})
secretAccessKey = '';
}
new JDCloudAccess();
@@ -0,0 +1,97 @@
import { AbstractDnsProvider, CreateRecordOptions, IsDnsProvider, RemoveRecordOptions } from "@certd/plugin-cert";
import { Autowire } from "@certd/pipeline";
import { JDCloudAccess } from "./access.js";
@IsDnsProvider({
name: "jdcloud",
title: "京东云",
desc: "京东云DNS解析提供商",
accessType: "jdcloud",
icon: "svg:icon-jdcloud"
})
export class JDCloudDnsProvider extends AbstractDnsProvider {
@Autowire()
access!: JDCloudAccess;
async onInstance() {
}
async createRecord(options: CreateRecordOptions): Promise<any> {
const { fullRecord, hostRecord, value, type, domain } = options;
this.logger.info("添加域名解析:", fullRecord, value, domain);
const service = await this.getJDDomainService();
const domainRes = await service.describeDomains({
domainName: domain,
pageNumber: 1,
pageSize: 10
})
if (!domainRes.result?.dataList?.length) {
throw new Error(`域名${domain}在此京东云账号中不存在`)
}
const domainId = domainRes.result.dataList[0].id
this.logger.info("域名ID", domainId)
/**
* hostRecord String True 主机记录
* hostValue String True 解析记录的值
* jcloudRes Boolean False 是否是京东云资源
* mxPriority Integer False 优先级,只存在于MX, SRV解析记录类型
* port Integer False 端口,只存在于SRV解析记录类型
* ttl Integer True 解析记录的生存时间,单位:秒
* type String True 解析的类型,请参考解析记录类型详解
* weight Integer False 解析记录的权重,目前支持权重的有:A/AAAA/CNAME/JNAMEA/AAAA权重范围:0-100、CNAME/JNAME权重范围:1-100。
* viewValue Integer True 解析线路的ID,请调用describeViewTree接口获取基础解
*/
try{
const res = await service.createResourceRecord({
domainId: domainId,
req:{
hostRecord: hostRecord,
hostValue: value,
type: type,
ttl: 100,
}
})
return {
recordId: res.result.dataList[0].id,
domainId: domainId
};
}catch (e) {
this.logger.error(e)
throw e
}
}
async removeRecord(options: RemoveRecordOptions<any>): Promise<any> {
const record = options.recordRes;
const service = await this.getJDDomainService();
await service.deleteResourceRecord({
domainId: record.domainId,
resourceRecordId: record.recordId
})
}
private async getJDDomainService() {
const {JDDomainService} = await import("@certd/jdcloud")
const service = new JDDomainService({
credentials: {
accessKeyId: this.access.accessKeyId,
secretAccessKey: this.access.secretAccessKey
},
regionId: "cn-north-1" //地域信息,某个api调用可以单独传参regionId,如果不传则会使用此配置中的regionId
});
return service;
}
}
new JDCloudDnsProvider();
@@ -0,0 +1,3 @@
export * from './access.js';
export * from './dns-provider.js';
export * from './plugins/index.js';
@@ -0,0 +1,3 @@
export * from './plugin-deploy-to-cdn.js'
export * from './plugin-update-cert.js'
export * from './plugin-upload-cert.js'
@@ -0,0 +1,175 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib";
import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
import { optionsUtils } from "@certd/basic/dist/utils/util.options.js";
import { JDCloudAccess } from "../access.js";
@IsTaskPlugin({
name: "JDCloudDeployToCDN",
title: "京东云-部署证书至CDN",
icon: "svg:icon-jdcloud",
group: pluginGroups.jdcloud.key,
desc: "京东云内容分发网络",
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed
}
}
})
export class JDCloudDeployToCDN extends AbstractTaskPlugin {
@TaskInput({
title: "域名证书",
helper: "请选择前置任务输出的域名证书",
component: {
name: "output-selector",
from: [...CertApplyPluginNames, "JDCloudUploadCert"]
},
required: true
})
cert!: CertInfo | number;
@TaskInput(createCertDomainGetterInputDefine({ props: { required: false } }))
certDomains!: string[];
@TaskInput({
title: "Access授权",
helper: "京东云AccessKeyId、AccessKeySecret",
component: {
name: "access-selector",
type: "jdcloud"
},
required: true
})
accessId!: string;
@TaskInput(
createRemoteSelectInputDefine({
title: "CDN加速域名",
helper: "你在京东云上配置的CDN加速域名,比如:certd.docmirror.cn",
action: JDCloudDeployToCDN.prototype.onGetDomainList.name,
watches: ["certDomains", "accessId"],
required: true
})
)
domainName!: string | string[];
async onInstance() {
}
async execute(): Promise<void> {
this.logger.info("开始部署证书到京东云CDN");
const access = await this.accessService.getById<JDCloudAccess>(this.accessId);
const service = await this.getClient(access);
let certId = this.cert;
const certName = this.appendTimeSuffix("certd");
if (typeof certId === "object") {
const certInfo = this.cert as CertInfo;
this.logger.info(`开始上传证书`);
const sslService = await this.getSslClient(access);
const res = await sslService.uploadCert({
// certName String True 证书名称
// keyFile String True 私钥
// certFile String True 证书
// aliasName String False 证书别名
certName: certName,
keyFile: certInfo.key,
certFile: certInfo.crt,
aliasName: certName
});
certId = res.result.certId;
}
// const certInfo = this.cert as CertInfo;
for (const domain of this.domainName) {
this.logger.info(`开始部署域名${domain}证书`);
const res = await service.setHttpType({
/**
* @param {string} opts.domain - 用户域名
* @param {} [opts.httpType] - http类型,只能为http或者https,默认为http.当设为https时,需要调用“设置通讯协议”接口上传证书和私钥 optional
* @param {} [opts.certificate] - 用户证书,当Type为https时必须设置 optional
* @param {} [opts.rsaKey] - 证书私钥 optional
* @param {} [opts.jumpType] - 有三种类型:default、http、https optional
* @param {} [opts.certFrom] - 证书来源有两种类型:default,ssl optional
* @param {} [opts.sslCertId] - ssl证书id optional
* @param {} [opts.syncToSsl] - 是否同步到ssl,boolean值,取值true或者false optional
* @param {} [opts.certName] - syncToSsl是true时,certName是必填项 optional
*/
domain,
httpType: "https",
// certificate: certInfo.crt,
// rsaKey: certInfo.key,
jumpType: "default",
certFrom: "ssl",
sslCertId: certId, // 不用certId 方式,会报证书已存在错误,目前还没找到怎么查询重复证书
syncToSsl: false,
certName: certName
});
this.logger.info(`部署域名${domain}证书成功:${JSON.stringify(res)}`);
await this.ctx.utils.sleep(2000);
}
this.logger.info("部署完成");
}
async getClient(access: JDCloudAccess) {
const { JDCdnService } = await import("@certd/jdcloud");
const service = new JDCdnService({
credentials: {
accessKeyId: access.accessKeyId,
secretAccessKey: access.secretAccessKey
},
regionId: "cn-north-1" //地域信息,某个api调用可以单独传参regionId,如果不传则会使用此配置中的regionId
});
return service;
}
async getSslClient(access: JDCloudAccess) {
const { JDSslService } = await import("@certd/jdcloud");
const service = new JDSslService({
credentials: {
accessKeyId: access.accessKeyId,
secretAccessKey: access.secretAccessKey
},
regionId: "cn-north-1" //地域信息,某个api调用可以单独传参regionId,如果不传则会使用此配置中的regionId
});
return service;
}
async onGetDomainList(data: any) {
if (!this.accessId) {
throw new Error("请选择Access授权");
}
const access = await this.accessService.getById<JDCloudAccess>(this.accessId);
const service = await this.getClient(access);
/**
* pageNumber Integer False 1 pageNumber,默认值1
* pageSize
*/
const res = await service.getDomainList({
pageNumber: 1,
pageSize: 50
});
// @ts-ignore
const list = res?.result?.domains;
if (!list || list.length === 0) {
throw new Error("找不到加速域名,您可以手动输入");
}
const options = list.map((item: any) => {
return {
value: item.domain,
label: item.domain,
domain: item.domain
};
});
return optionsUtils.buildGroupOptions(options, this.certDomains);
}
}
new JDCloudDeployToCDN();
@@ -0,0 +1,148 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib";
import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
import { optionsUtils } from "@certd/basic/dist/utils/util.options.js";
import { JDCloudAccess } from "../access.js";
@IsTaskPlugin({
name: 'JDCloudUpdateCert',
title: '京东云-更新已有证书',
icon: 'svg:icon-jdcloud',
group: pluginGroups.jdcloud.key,
desc: '更新SSL数字证书中的证书',
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
})
export class JDCloudUpdateCert extends AbstractTaskPlugin {
@TaskInput({
title: '域名证书',
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: [...CertApplyPluginNames, 'JDCloudUploadCert'],
},
required: true,
})
cert!: CertInfo | string;
@TaskInput(createCertDomainGetterInputDefine({ props: { required: false } }))
certDomains!: string[];
@TaskInput({
title: 'Access授权',
helper: '京东云AccessKeyId、AccessKeySecret',
component: {
name: 'access-selector',
type: 'jdcloud',
},
required: true,
})
accessId!: string;
@TaskInput(
createRemoteSelectInputDefine({
title: '要更新的证书id',
helper: '您在京东云上已有的证书Id',
action: JDCloudUpdateCert.prototype.onGetCertList.name,
watches: ['certDomains', 'accessId'],
required: true,
})
)
certIds!: string[];
async onInstance() {}
async execute(): Promise<void> {
this.logger.info('开始部署证书到京东云CDN');
const access = await this.accessService.getById<JDCloudAccess>(this.accessId);
const service = await this.getClient(access)
// let certId = this.cert
// const certName = this.appendTimeSuffix("certd");
// if (typeof certId !== 'string') {
// const certInfo = this.cert as CertInfo
// this.logger.info(`开始上传证书`)
//
// const res = await service.uploadCert({
// // certName String True 证书名称
// // keyFile String True 私钥
// // certFile String True 证书
// // aliasName String False 证书别名
// certName: certName,
// keyFile: certInfo.key,
// certFile: certInfo.crt,
// aliasName: certName
// })
// certId = res.result.certId
// }
const certInfo = this.cert as CertInfo
for (const certId of this.certIds) {
this.logger.info(`开始更新证书:${certId}`)
const res = await service.updateCert({
/*
@param {string} opts.certId - 证书Id
@param {string} opts.certId - 证书ID
@param {string} opts.keyFile - 私钥
@param {string} opts.certFile - 证书
@param {string} callback - callback
*/
certId,
certFile: certInfo.crt,
keyFile:certInfo.key,
})
this.logger.info(`更新证书${certId}成功:${JSON.stringify(res)}`);
await this.ctx.utils.sleep(2000)
}
}
async getClient(access: JDCloudAccess) {
const {JDSslService} = await import("@certd/jdcloud")
const service = new JDSslService({
credentials: {
accessKeyId: access.accessKeyId,
secretAccessKey: access.secretAccessKey
},
regionId: "cn-north-1" //地域信息,某个api调用可以单独传参regionId,如果不传则会使用此配置中的regionId
});
return service;
}
async onGetCertList(data: any) {
if (!this.accessId) {
throw new Error('请选择Access授权');
}
const access = await this.accessService.getById<JDCloudAccess>(this.accessId);
const service = await this.getClient(access);
/**
* pageNumber Integer False 1 pageNumber,默认值1
* pageSize
*/
const res = await service.describeCerts({
pageNumber: 1,
pageSize: 100,
})
// @ts-ignore
const list = res?.result?.certListDetails
if (!list || list.length === 0) {
throw new Error('找不到证书,您可以手动输入证书id');
}
const options = list.map((item: any) => {
return {
value: item.certId,
label: `${item.certName}<${item.certId}_${item.commonName}>`,
domain: item.commonName, // or item.dnsNames 证书所有域名
};
});
return optionsUtils.buildGroupOptions(options, this.certDomains);
}
}
new JDCloudUpdateCert();
@@ -0,0 +1,92 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput, TaskOutput } from "@certd/pipeline";
import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
import { JDCloudAccess } from "../access.js";
@IsTaskPlugin({
name: "JDCloudUploadCert",
title: "京东云-上传新证书",
icon: "svg:icon-jdcloud",
group: pluginGroups.jdcloud.key,
desc: "上传证书到SSL数字证书中心",
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed
}
}
})
export class JDCloudUploadCert extends AbstractTaskPlugin {
@TaskInput({
title: "域名证书",
helper: "请选择前置任务输出的域名证书",
component: {
name: "output-selector",
from: [...CertApplyPluginNames, "JDCloudUploadCert"]
},
required: true
})
cert!: CertInfo | string;
@TaskInput({
title: "Access授权",
helper: "京东云AccessKeyId、AccessKeySecret",
component: {
name: "access-selector",
type: "jdcloud"
},
required: true
})
accessId!: string;
@TaskInput({
title: "证书名称前缀",
helper: "证书形成,默认为certd",
required: false
})
certName!: string;
@TaskOutput({
title: "上传成功后的京东云CertId"
})
jdcloudCertId!: number;
async onInstance() {
}
async execute(): Promise<void> {
this.logger.info("开始上传证书到京东云数字证书中心");
const access = await this.accessService.getById<JDCloudAccess>(this.accessId);
const service = await this.getClient(access);
const certInfo = this.cert as CertInfo;
const res = await service.uploadCert({
/*
@param {string} opts.certName - 证书名称
@param {string} opts.keyFile - 私钥
@param {string} opts.certFile - 证书
@param {string} [opts.aliasName] - 证书别名 optional
*/
certName: this.appendTimeSuffix(this.certName || "certd"),
certFile: certInfo.crt,
keyFile: certInfo.key
});
this.jdcloudCertId = res.result.certId;
this.logger.info(`上传证书成功:${JSON.stringify(res)}`);
}
async getClient(access: JDCloudAccess) {
const { JDSslService } = await import("@certd/jdcloud");
const service = new JDSslService({
credentials: {
accessKeyId: access.accessKeyId,
secretAccessKey: access.secretAccessKey
},
regionId: "cn-north-1" //地域信息,某个api调用可以单独传参regionId,如果不传则会使用此配置中的regionId
});
return service;
}
}
new JDCloudUploadCert();
@@ -10,7 +10,7 @@ import { CertApplyPluginNames} from '@certd/plugin-cert';
title: 'Proxmox-上传证书到Proxmox',
icon: 'svg:icon-proxmox',
//插件分组
group: pluginGroups.other.key,
group: pluginGroups.panel.key,
needPlus: true,
default: {
//默认值配置照抄即可