feat: midway注解方式编写插件

pull/9/head^2
xiaojunnuo 2023-01-11 20:39:48 +08:00
parent 52522f27e9
commit dcd1023a39
47 changed files with 484 additions and 714 deletions

View File

@ -6,7 +6,7 @@ export type AccessInputDefine = FormItemProps & {
required?: boolean; required?: boolean;
}; };
export type AccessDefine = Registrable & { export type AccessDefine = Registrable & {
inputs?: { input?: {
[key: string]: AccessInputDefine; [key: string]: AccessInputDefine;
}; };
}; };

View File

@ -20,7 +20,7 @@ export function IsAccess(define: AccessDefine): ClassDecorator {
inputs[property] = input; inputs[property] = input;
} }
} }
_.merge(define, { inputs }); _.merge(define, { input: inputs });
Reflect.defineMetadata(ACCESS_CLASS_KEY, define, target); Reflect.defineMetadata(ACCESS_CLASS_KEY, define, target);
target.define = define; target.define = define;
accessRegistry.register(define.name, { accessRegistry.register(define.name, {
@ -30,9 +30,9 @@ export function IsAccess(define: AccessDefine): ClassDecorator {
}; };
} }
export function IsAccessInput(input?: AccessInputDefine): PropertyDecorator { export function AccessInput(input?: AccessInputDefine): PropertyDecorator {
return (target, propertyKey) => { return (target, propertyKey) => {
target = Decorator.target(target); target = Decorator.target(target, propertyKey);
// const _type = Reflect.getMetadata("design:type", target, propertyKey); // const _type = Reflect.getMetadata("design:type", target, propertyKey);
Reflect.defineMetadata(ACCESS_INPUT_KEY, input, target, propertyKey); Reflect.defineMetadata(ACCESS_INPUT_KEY, input, target, propertyKey);
}; };

View File

@ -9,6 +9,7 @@ import { Logger } from "log4js";
import { request } from "../utils/util.request"; import { request } from "../utils/util.request";
import { IAccessService } from "../access"; import { IAccessService } from "../access";
import { RegistryItem } from "../registry"; import { RegistryItem } from "../registry";
import { Decorator } from "../decorator";
export class Executor { export class Executor {
userId: any; userId: any;
@ -149,21 +150,17 @@ export class Executor {
// @ts-ignore // @ts-ignore
const define: PluginDefine = plugin.define; const define: PluginDefine = plugin.define;
//从outputContext读取输入参数 //从outputContext读取输入参数
_.forEach(define.input, (item, key) => { Decorator.inject(define.input, instance, step.input, (item, key) => {
if (item.component?.name === "pi-output-selector") { if (item.component?.name === "pi-output-selector") {
const contextKey = step.input[key]; const contextKey = step.input[key];
if (contextKey != null) { if (contextKey != null) {
step.input[key] = this.runtime.context[contextKey]; step.input[key] = this.runtime.context[contextKey];
} }
} else {
instance[key] = step.input[key];
} }
}); });
_.forEach(define.autowire, (item, key: string) => { Decorator.inject(define.autowire, instance, context);
instance[key] = context[key]; await instance.onInit();
});
await instance.execute(); await instance.execute();
//输出到output context //输出到output context

View File

@ -0,0 +1,17 @@
import { Decorator } from "./index";
export type AutowireProp = {
name?: string;
type?: any;
};
export const AUTOWIRE_KEY = "pipeline:autowire";
export function Autowire(props?: AutowireProp): PropertyDecorator {
return (target, propertyKey) => {
const _type = Reflect.getMetadata("design:type", target, propertyKey);
target = Decorator.target(target, propertyKey);
props = props || {};
props.type = _type;
Reflect.defineMetadata(AUTOWIRE_KEY, props || {}, target, propertyKey);
};
}

View File

@ -1,28 +1,2 @@
const propertyMap: any = {}; export * from "./utils";
function attachProperty(target: any, propertyKey: string | symbol) { export * from "./common";
let props = propertyMap[target];
if (props == null) {
props = {};
propertyMap[target] = props;
}
props[propertyKey] = true;
}
function getClassProperties(target: any) {
return propertyMap[target] || {};
}
function target(target: any, propertyKey?: string | symbol) {
if (typeof target === "object" && target.constructor) {
target = target.constructor;
}
if (propertyKey != null) {
attachProperty(target, propertyKey);
}
return target;
}
export const Decorator = {
target,
attachProperty,
getClassProperties,
};

View File

@ -0,0 +1,42 @@
import _ from "lodash";
const propertyMap: any = {};
function attachProperty(target: any, propertyKey: string | symbol) {
let props = propertyMap[target];
if (props == null) {
props = {};
propertyMap[target] = props;
}
props[propertyKey] = true;
}
function getClassProperties(target: any) {
return propertyMap[target] || {};
}
function target(target: any, propertyKey?: string | symbol) {
if (typeof target === "object" && target.constructor) {
target = target.constructor;
}
if (propertyKey != null) {
attachProperty(target, propertyKey);
}
return target;
}
function inject(define: any, instance: any, context: any, preHandler?: (item: any, key: string, instance: any, context: any) => void) {
_.forEach(define, (item, key) => {
if (preHandler) {
preHandler(item, key, instance, context);
}
if (context[key] != undefined) {
instance[key] = context[key];
}
});
}
export const Decorator = {
target,
attachProperty,
getClassProperties,
inject,
};

View File

@ -1,22 +0,0 @@
import { AbstractRegistrable } from "../registry";
import { CreateRecordOptions, IDnsProvider, DnsProviderDefine, RemoveRecordOptions } from "./api";
import { AbstractAccess } from "../access";
import { Logger } from "log4js";
import { AxiosInstance } from "axios";
export abstract class AbstractDnsProvider extends AbstractRegistrable<DnsProviderDefine> implements IDnsProvider {
access!: AbstractAccess;
logger!: Logger;
http!: AxiosInstance;
doInit(options: { access: AbstractAccess; logger: Logger; http: AxiosInstance }) {
this.access = options.access;
this.logger = options.logger;
this.http = options.http;
this.onInit();
}
protected abstract onInit(): void;
abstract createRecord(options: CreateRecordOptions): Promise<any>;
abstract removeRecord(options: RemoveRecordOptions): Promise<any>;
}

View File

@ -2,8 +2,8 @@ export * from "./core";
export * from "./d.ts"; export * from "./d.ts";
export * from "./access"; export * from "./access";
export * from "./registry"; export * from "./registry";
export * from "./dns-provider";
export * from "./plugin"; export * from "./plugin";
export * from "./utils"; export * from "./utils";
export * from "./midway"; export * from "./midway";
export * from "./context"; export * from "./context";
export * from "./decorator";

View File

@ -2,6 +2,7 @@ import _ from "lodash";
import { pluginRegistry } from "./registry"; import { pluginRegistry } from "./registry";
import { PluginDefine, TaskInputDefine, TaskOutputDefine } from "./api"; import { PluginDefine, TaskInputDefine, TaskOutputDefine } from "./api";
import { Decorator } from "../decorator"; import { Decorator } from "../decorator";
import { AUTOWIRE_KEY } from "../decorator";
// 提供一个唯一 key // 提供一个唯一 key
export const PLUGIN_CLASS_KEY = "pipeline:plugin"; export const PLUGIN_CLASS_KEY = "pipeline:plugin";
@ -20,7 +21,7 @@ export function IsTaskPlugin(define: PluginDefine): ClassDecorator {
inputs[property] = input; inputs[property] = input;
} }
const autowire = Reflect.getMetadata(PLUGIN_AUTOWIRE_KEY, target, property); const autowire = Reflect.getMetadata(AUTOWIRE_KEY, target, property);
if (autowire) { if (autowire) {
autowires[property] = autowire; autowires[property] = autowire;
} }
@ -59,19 +60,3 @@ export function TaskOutput(output?: TaskOutputDefine): PropertyDecorator {
Reflect.defineMetadata(PLUGIN_OUTPUT_KEY, output, target, propertyKey); Reflect.defineMetadata(PLUGIN_OUTPUT_KEY, output, target, propertyKey);
}; };
} }
export type AutowireProp = {
name?: string;
type?: any;
};
export const PLUGIN_AUTOWIRE_KEY = "pipeline:plugin:autowire";
export function Autowire(props?: AutowireProp): PropertyDecorator {
return (target, propertyKey) => {
const _type = Reflect.getMetadata("design:type", target, propertyKey);
target = Decorator.target(target, propertyKey);
props = props || {};
props.type = _type;
Reflect.defineMetadata(PLUGIN_AUTOWIRE_KEY, props || {}, target, propertyKey);
};
}

View File

@ -20,7 +20,7 @@ export class Registry {
this.storage[key] = value; this.storage[key] = value;
} }
get(name: string) { get(name: string): RegistryItem {
if (!name) { if (!name) {
throw new Error("插件名称不能为空"); throw new Error("插件名称不能为空");
} }

View File

@ -32,4 +32,4 @@ export function buildLogger(write: (text: string) => void) {
}); });
return logger; return logger;
} }
export type LOGGER = Logger; export type ILogger = Logger;

View File

@ -1,4 +1,4 @@
import { IsTaskPlugin, TaskInput, ITaskPlugin, LOGGER, Autowire, TaskOutput } from "../src"; import { IsTaskPlugin, TaskInput, ITaskPlugin, ILogger, Autowire, TaskOutput } from "../src";
@IsTaskPlugin({ @IsTaskPlugin({
name: "EchoPlugin", name: "EchoPlugin",
@ -15,7 +15,7 @@ export class EchoPlugin implements ITaskPlugin {
cert!: any; cert!: any;
@Autowire() @Autowire()
logger!: LOGGER; logger!: ILogger;
@TaskOutput({ @TaskOutput({
title: "cert info", title: "cert info",

View File

@ -15,6 +15,7 @@
"@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/ban-ts-ignore": "off", "@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-empty-function": "off",
// "no-unused-expressions": "off", // "no-unused-expressions": "off",
"max-len": [0, 160, 2, { "ignoreUrls": true }] "max-len": [0, 160, 2, { "ignoreUrls": true }]
} }

View File

@ -1,4 +1,4 @@
import { IsAccess, IsAccessInput } from "@certd/pipeline"; import { IsAccess, AccessInput } from "@certd/pipeline";
@IsAccess({ @IsAccess({
name: "aliyun", name: "aliyun",
@ -6,7 +6,7 @@ import { IsAccess, IsAccessInput } from "@certd/pipeline";
desc: "", desc: "",
}) })
export class AliyunAccess { export class AliyunAccess {
@IsAccessInput({ @AccessInput({
title: "accessKeyId", title: "accessKeyId",
component: { component: {
placeholder: "accessKeyId", placeholder: "accessKeyId",
@ -14,7 +14,7 @@ export class AliyunAccess {
required: true, required: true,
}) })
accessKeyId = ""; accessKeyId = "";
@IsAccessInput({ @AccessInput({
title: "accessKeySecret", title: "accessKeySecret",
component: { component: {
placeholder: "accessKeySecret", placeholder: "accessKeySecret",

View File

@ -1,7 +1,8 @@
import Core from "@alicloud/pop-core"; import Core from "@alicloud/pop-core";
import _ from "lodash"; import _ from "lodash";
import { CreateRecordOptions, IDnsProvider, IsDnsProvider, RemoveRecordOptions } from "@certd/pipeline"; import { CreateRecordOptions, IDnsProvider, IsDnsProvider, RemoveRecordOptions } from "@certd/plugin-cert";
import { Logger } from "log4js"; import { Autowire, ILogger } from "@certd/pipeline";
import { AliyunAccess } from "../access";
@IsDnsProvider({ @IsDnsProvider({
name: "aliyun", name: "aliyun",
@ -11,8 +12,10 @@ import { Logger } from "log4js";
}) })
export class AliyunDnsProvider implements IDnsProvider { export class AliyunDnsProvider implements IDnsProvider {
client: any; client: any;
access: any; @Autowire()
logger!: Logger; access!: AliyunAccess;
@Autowire()
logger!: ILogger;
async onInit() { async onInit() {
const access: any = this.access; const access: any = this.access;
this.client = new Core({ this.client = new Core({

View File

@ -1,101 +1,119 @@
import { AbstractPlugin, IsTask, RunStrategy, TaskInput, TaskOutput, TaskPlugin, utils } from "@certd/pipeline"; import { Autowire, IAccessService, IsTaskPlugin, ITaskPlugin, ILogger, RunStrategy, TaskInput, utils } from "@certd/pipeline";
// @ts-ignore // @ts-ignore
import { ROAClient } from "@alicloud/pop-core"; import { ROAClient } from "@alicloud/pop-core";
import { AliyunAccess } from "../../access"; import { AliyunAccess } from "../../access";
import { K8sClient } from "@certd/plugin-util"; import { K8sClient } from "@certd/plugin-util";
import { appendTimeSuffix } from "../../utils"; import { appendTimeSuffix } from "../../utils";
import { CertInfo } from "@certd/plugin-cert";
@IsTask(() => { @IsTaskPlugin({
return { name: "DeployCertToAliyunAckIngress",
name: "DeployCertToAliyunAckIngress", title: "部署到阿里云AckIngress",
title: "部署到阿里云AckIngress", input: {},
input: { output: {},
clusterId: { default: {
title: "集群id", strategy: {
component: { runStrategy: RunStrategy.SkipWhenSucceed,
placeholder: "集群id",
},
},
secretName: {
title: "保密字典Id",
component: {
placeholder: "保密字典Id",
},
required: true,
},
regionId: {
title: "大区",
value: "cn-shanghai",
component: {
placeholder: "集群所属大区",
},
required: true,
},
namespace: {
title: "命名空间",
value: "default",
component: {
placeholder: "命名空间",
},
required: true,
},
ingressName: {
title: "ingress名称",
value: "",
component: {
placeholder: "ingress名称",
},
required: true,
helper: "可以传入一个数组",
},
ingressClass: {
title: "ingress类型",
value: "nginx",
component: {
placeholder: "暂时只支持nginx类型",
},
required: true,
},
isPrivateIpAddress: {
title: "是否私网ip",
value: false,
component: {
placeholder: "集群连接端点是否是私网ip",
},
helper: "如果您当前certd运行在同一个私网下可以选择是。",
required: true,
},
cert: {
title: "域名证书",
helper: "请选择前置任务输出的域名证书",
component: {
name: "pi-output-selector",
},
required: true,
},
accessId: {
title: "Access授权",
helper: "阿里云授权AccessKeyId、AccessKeySecret",
component: {
name: "pi-access-selector",
type: "aliyun",
},
required: true,
},
}, },
output: {}, },
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
};
}) })
export class DeployCertToAliyunAckIngressPlugin extends AbstractPlugin implements TaskPlugin { export class DeployCertToAliyunAckIngressPlugin implements ITaskPlugin {
async execute(input: TaskInput): Promise<TaskOutput> { @TaskInput({
title: "集群id",
component: {
placeholder: "集群id",
},
})
clusterId!: string;
@TaskInput({
title: "保密字典Id",
component: {
placeholder: "保密字典Id",
},
required: true,
})
secretName!: string | string[];
@TaskInput({
title: "大区",
value: "cn-shanghai",
component: {
placeholder: "集群所属大区",
},
required: true,
})
regionId!: string;
@TaskInput({
title: "命名空间",
value: "default",
component: {
placeholder: "命名空间",
},
required: true,
})
namespace!: string;
@TaskInput({
title: "ingress名称",
value: "",
component: {
placeholder: "ingress名称",
},
required: true,
helper: "可以传入一个数组",
})
ingressName!: string;
@TaskInput({
title: "ingress类型",
value: "nginx",
component: {
placeholder: "暂时只支持nginx类型",
},
required: true,
})
ingressClass!: string;
@TaskInput({
title: "是否私网ip",
value: false,
component: {
placeholder: "集群连接端点是否是私网ip",
},
helper: "如果您当前certd运行在同一个私网下可以选择是。",
required: true,
})
isPrivateIpAddress!: boolean;
@TaskInput({
title: "域名证书",
helper: "请选择前置任务输出的域名证书",
component: {
name: "pi-output-selector",
},
required: true,
})
cert!: CertInfo;
@TaskInput({
title: "Access授权",
helper: "阿里云授权AccessKeyId、AccessKeySecret",
component: {
name: "pi-access-selector",
type: "aliyun",
},
required: true,
})
accessId!: string;
@Autowire()
accessService!: IAccessService;
@Autowire()
logger!: ILogger;
// eslint-disable-next-line @typescript-eslint/no-empty-function
async onInit(): Promise<void> {}
async execute(): Promise<void> {
console.log("开始部署证书到阿里云cdn"); console.log("开始部署证书到阿里云cdn");
const { regionId, ingressClass, clusterId, isPrivateIpAddress, cert } = input; const { regionId, ingressClass, clusterId, isPrivateIpAddress, cert } = this;
const access = (await this.accessService.getById(input.accessId)) as AliyunAccess; const access = (await this.accessService.getById(this.accessId)) as AliyunAccess;
const client = this.getClient(access, regionId); const client = this.getClient(access, regionId);
const kubeConfigStr = await this.getKubeConfig(client, clusterId, isPrivateIpAddress); const kubeConfigStr = await this.getKubeConfig(client, clusterId, isPrivateIpAddress);
@ -106,17 +124,16 @@ export class DeployCertToAliyunAckIngressPlugin extends AbstractPlugin implement
throw new Error("暂未实现"); throw new Error("暂未实现");
// await this.patchQcloudCertSecret({ k8sClient, props, context }) // await this.patchQcloudCertSecret({ k8sClient, props, context })
} else { } else {
await this.patchNginxCertSecret({ cert, k8sClient, input }); await this.patchNginxCertSecret({ cert, k8sClient });
} }
await utils.sleep(3000); // 停留2秒等待secret部署完成 await utils.sleep(3000); // 停留2秒等待secret部署完成
// await this.restartIngress({ k8sClient, props }) // await this.restartIngress({ k8sClient, props })
return {};
} }
async restartIngress(options: { k8sClient: any; input: TaskInput }) { async restartIngress(options: { k8sClient: any }) {
const { k8sClient, input } = options; const { k8sClient } = options;
const { namespace } = input; const { namespace } = this;
const body = { const body = {
metadata: { metadata: {
@ -136,7 +153,7 @@ export class DeployCertToAliyunAckIngressPlugin extends AbstractPlugin implement
return false; return false;
} }
for (const tls of item.spec.tls) { for (const tls of item.spec.tls) {
if (tls.secretName === input.secretName) { if (tls.secretName === this.secretName) {
return true; return true;
} }
} }
@ -151,14 +168,14 @@ export class DeployCertToAliyunAckIngressPlugin extends AbstractPlugin implement
} }
} }
async patchNginxCertSecret(options: { cert: any; k8sClient: any; input: TaskInput }) { async patchNginxCertSecret(options: { cert: any; k8sClient: any }) {
const { cert, k8sClient, input } = options; const { cert, k8sClient } = options;
const crt = cert.crt; const crt = cert.crt;
const key = cert.key; const key = cert.key;
const crtBase64 = Buffer.from(crt).toString("base64"); const crtBase64 = Buffer.from(crt).toString("base64");
const keyBase64 = Buffer.from(key).toString("base64"); const keyBase64 = Buffer.from(key).toString("base64");
const { namespace, secretName } = input; const { namespace, secretName } = this;
const body = { const body = {
data: { data: {
@ -171,7 +188,7 @@ export class DeployCertToAliyunAckIngressPlugin extends AbstractPlugin implement
}, },
}, },
}; };
let secretNames = secretName; let secretNames: any = secretName;
if (typeof secretName === "string") { if (typeof secretName === "string") {
secretNames = [secretName]; secretNames = [secretName];
} }

View File

@ -1,9 +1,8 @@
import { Autowire, IAccessService, IsTaskPlugin, ITaskPlugin, LOGGER, RunStrategy, TaskInput } from "@certd/pipeline"; import { Autowire, IAccessService, ILogger, IsTaskPlugin, ITaskPlugin, RunStrategy, TaskInput } from "@certd/pipeline";
import dayjs from "dayjs"; import dayjs from "dayjs";
import Core from "@alicloud/pop-core"; import Core from "@alicloud/pop-core";
import RPCClient from "@alicloud/pop-core"; import RPCClient from "@alicloud/pop-core";
import { AliyunAccess } from "../../access"; import { AliyunAccess } from "../../access";
import { Inject } from "@midwayjs/core";
@IsTaskPlugin({ @IsTaskPlugin({
name: "DeployCertToAliyunCDN", name: "DeployCertToAliyunCDN",
@ -50,11 +49,11 @@ export class DeployCertToAliyunCDN implements ITaskPlugin {
}) })
accessId!: string; accessId!: string;
@Inject() @Autowire()
accessService!: IAccessService; accessService!: IAccessService;
@Autowire() @Autowire()
logger!: LOGGER; logger!: ILogger;
// eslint-disable-next-line @typescript-eslint/no-empty-function // eslint-disable-next-line @typescript-eslint/no-empty-function
async onInit() {} async onInit() {}
async execute(): Promise<void> { async execute(): Promise<void> {

View File

@ -17,7 +17,6 @@ import { Logger } from "log4js";
export class UploadCertToAliyun implements ITaskPlugin { export class UploadCertToAliyun implements ITaskPlugin {
@TaskInput({ @TaskInput({
title: "证书名称", title: "证书名称",
helper: "证书上传后将以此参数作为名称前缀", helper: "证书上传后将以此参数作为名称前缀",
}) })
name!: string; name!: string;
@ -66,6 +65,9 @@ export class UploadCertToAliyun implements ITaskPlugin {
@Autowire() @Autowire()
logger!: Logger; logger!: Logger;
// eslint-disable-next-line @typescript-eslint/no-empty-function
async onInit() {}
async execute(): Promise<void> { async execute(): Promise<void> {
console.log("开始部署证书到阿里云cdn"); console.log("开始部署证书到阿里云cdn");
const access = (await this.accessService.getById(this.accessId)) as AliyunAccess; const access = (await this.accessService.getById(this.accessId)) as AliyunAccess;

View File

@ -1,199 +0,0 @@
import { AbstractAliyunPlugin } from '../abstract-aliyun.js'
import Core from '@alicloud/pop-core'
import { K8sClient } from '@certd/plugin-common'
const ROAClient = Core.ROAClient
const define = {
name: 'deployCertToAliyunAckIngress',
title: '部署到阿里云AckIngress',
input: {
clusterId: {
title: '集群id',
component: {
placeholder: '集群id'
}
},
secretName: {
title: '保密字典Id',
component: {
placeholder: '保密字典Id'
},
required: true
},
regionId: {
title: '大区',
value: 'cn-shanghai',
component: {
placeholder: '集群所属大区'
},
required: true
},
namespace: {
title: '命名空间',
value: 'default',
component: {
placeholder: '命名空间'
},
required: true
},
ingressName: {
title: 'ingress名称',
value: '',
component: {
placeholder: 'ingress名称'
},
required: true,
helper: '可以传入一个数组'
},
ingressClass: {
title: 'ingress类型',
value: 'nginx',
component: {
placeholder: '暂时只支持nginx类型'
},
required: true
},
isPrivateIpAddress: {
title: '是否私网ip',
value: false,
component: {
placeholder: '集群连接端点是否是私网ip'
},
helper: '如果您当前certd运行在同一个私网下可以选择是。',
required: true
},
accessProvider: {
title: 'Access授权',
type: [String, Object],
helper: 'AccessKey、AccessSecret',
component: {
name: 'access-selector',
type: 'aliyun'
},
required: true
}
},
output: {
}
}
export class DeployCertToAliyunAckIngress extends AbstractAliyunPlugin {
static define () {
return define
}
async execute ({ cert, props, context }) {
const accessProvider = this.getAccessProvider(props.accessProvider)
const client = this.getClient(accessProvider, props.regionId)
const kubeConfigStr = await this.getKubeConfig(client, props.clusterId, props.isPrivateIpAddress)
this.logger.info('kubeconfig已成功获取')
const k8sClient = new K8sClient(kubeConfigStr)
const ingressType = props.ingressClass || 'qcloud'
if (ingressType === 'qcloud') {
throw new Error('暂未实现')
// await this.patchQcloudCertSecret({ k8sClient, props, context })
} else {
await this.patchNginxCertSecret({ cert, k8sClient, props, context })
}
await this.sleep(3000) // 停留2秒等待secret部署完成
// await this.restartIngress({ k8sClient, props })
return true
}
async restartIngress ({ k8sClient, props }) {
const { namespace } = props
const body = {
metadata: {
labels: {
certd: this.appendTimeSuffix('certd')
}
}
}
const ingressList = await k8sClient.getIngressList({ namespace })
console.log('ingressList:', ingressList)
if (!ingressList || !ingressList.body || !ingressList.body.items) {
return
}
const ingressNames = ingressList.body.items.filter(item => {
if (!item.spec.tls) {
return false
}
for (const tls of item.spec.tls) {
if (tls.secretName === props.secretName) {
return true
}
}
return false
}).map(item => {
return item.metadata.name
})
for (const ingress of ingressNames) {
await k8sClient.patchIngress({ namespace, ingressName: ingress, body })
this.logger.info(`ingress已重启:${ingress}`)
}
}
async patchNginxCertSecret ({ cert, k8sClient, props, context }) {
const crt = cert.crt
const key = cert.key
const crtBase64 = Buffer.from(crt).toString('base64')
const keyBase64 = Buffer.from(key).toString('base64')
const { namespace, secretName } = props
const body = {
data: {
'tls.crt': crtBase64,
'tls.key': keyBase64
},
metadata: {
labels: {
certd: this.appendTimeSuffix('certd')
}
}
}
let secretNames = secretName
if (typeof secretName === 'string') {
secretNames = [secretName]
}
for (const secret of secretNames) {
await k8sClient.patchSecret({ namespace, secretName: secret, body })
this.logger.info(`CertSecret已更新:${secret}`)
}
}
getClient (aliyunProvider, regionId) {
return new ROAClient({
accessKeyId: aliyunProvider.accessKeyId,
accessKeySecret: aliyunProvider.accessKeySecret,
endpoint: `https://cs.${regionId}.aliyuncs.com`,
apiVersion: '2015-12-15'
})
}
async getKubeConfig (client, clusterId, isPrivateIpAddress = false) {
const httpMethod = 'GET'
const uriPath = `/k8s/${clusterId}/user_config`
const queries = {
PrivateIpAddress: isPrivateIpAddress
}
const body = '{}'
const headers = {
'Content-Type': 'application/json'
}
const requestOption = {}
try {
const res = await client.request(httpMethod, uriPath, queries, body, headers, requestOption)
return res.config
} catch (e) {
console.error('请求出错:', e)
throw e
}
}
}

View File

@ -1,106 +0,0 @@
import Core from '@alicloud/pop-core'
import { AbstractAliyunPlugin } from '../abstract-aliyun.js'
import { ZoneOptions } from '../../utils/index.js'
const define = {
name: 'uploadCertToAliyun',
title: '上传证书到阿里云',
desc: '',
input: {
name: {
title: '证书名称',
helper: '证书上传后将以此参数作为名称前缀'
},
regionId: {
title: '大区',
value: 'cn-hangzhou',
component: {
name: 'a-select',
vModel: 'value',
options: ZoneOptions
},
required: true
},
accessProvider: {
title: 'Access授权',
helper: 'Access授权',
component: {
name: 'access-selector',
type: 'aliyun'
},
required: true
}
},
output: {
aliyunCertId: {
type: String,
desc: '上传成功后的阿里云CertId'
}
}
}
export class UploadCertToAliyun extends AbstractAliyunPlugin {
static define () {
return define
}
getClient (aliyunProvider) {
return new Core({
accessKeyId: aliyunProvider.accessKeyId,
accessKeySecret: aliyunProvider.accessKeySecret,
endpoint: 'https://cas.aliyuncs.com',
apiVersion: '2018-07-13'
})
}
async execute ({ cert, props, context }) {
const { name, accessProvider } = props
const certName = this.appendTimeSuffix(name || cert.domain)
const params = {
RegionId: props.regionId || 'cn-hangzhou',
Name: certName,
Cert: cert.crt,
Key: cert.key
}
const requestOption = {
method: 'POST'
}
const provider = this.getAccessProvider(accessProvider)
const client = this.getClient(provider)
const ret = await client.request('CreateUserCertificate', params, requestOption)
this.checkRet(ret)
this.logger.info('证书上传成功aliyunCertId=', ret.CertId)
context.aliyunCertId = ret.CertId
}
/**
* 没用现在阿里云证书不允许删除
* @param accessProviders
* @param cert
* @param props
* @param context
* @returns {Promise<void>}
*/
async rollback ({ cert, props, context }) {
const { accessProvider } = props
const { aliyunCertId } = context
this.logger.info('准备删除阿里云证书:', aliyunCertId)
const params = {
RegionId: props.regionId || 'cn-hangzhou',
CertId: aliyunCertId
}
const requestOption = {
method: 'POST'
}
const provider = this.getAccessProvider(accessProvider)
const client = this.getClient(provider)
const ret = await client.request('DeleteUserCertificate', params, requestOption)
this.checkRet(ret)
this.logger.info('证书删除成功:', aliyunCertId)
delete context.aliyunCertId
}
}

View File

@ -15,6 +15,7 @@
"@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/ban-ts-ignore": "off", "@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-empty-function": "off",
// "no-unused-expressions": "off", // "no-unused-expressions": "off",
"max-len": [0, 160, 2, { "ignoreUrls": true }] "max-len": [0, 160, 2, { "ignoreUrls": true }]
} }

View File

@ -1,4 +1,4 @@
// export * from "@certd/plugin-aliyun";
export * from "@certd/plugin-cert"; export * from "@certd/plugin-cert";
export * from "@certd/plugin-aliyun";
export * from "@certd/plugin-tencent"; export * from "@certd/plugin-tencent";
// export * from "@certd/plugin-host"; export * from "@certd/plugin-host";

View File

@ -15,6 +15,7 @@
"@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/ban-ts-ignore": "off", "@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-empty-function": "off",
// "no-unused-expressions": "off", // "no-unused-expressions": "off",
"max-len": [0, 160, 2, { "ignoreUrls": true }] "max-len": [0, 160, 2, { "ignoreUrls": true }]
} }

View File

@ -1,7 +1,10 @@
import { Registrable } from "../registry"; import { Registrable } from "@certd/pipeline";
export type DnsProviderDefine = Registrable & { export type DnsProviderDefine = Registrable & {
accessType: string; accessType: string;
autowire?: {
[key: string]: any;
};
}; };
export type CreateRecordOptions = { export type CreateRecordOptions = {
@ -14,6 +17,7 @@ export type RemoveRecordOptions = CreateRecordOptions & {
}; };
export interface IDnsProvider { export interface IDnsProvider {
onInit(): Promise<void>;
createRecord(options: CreateRecordOptions): Promise<any>; createRecord(options: CreateRecordOptions): Promise<any>;
removeRecord(options: RemoveRecordOptions): Promise<any>; removeRecord(options: RemoveRecordOptions): Promise<any>;
} }

View File

@ -1,7 +1,8 @@
// src/decorator/memoryCache.decorator.ts // src/decorator/memoryCache.decorator.ts
import { dnsProviderRegistry } from "./registry"; import { dnsProviderRegistry } from "./registry";
import { DnsProviderDefine } from "./api"; import { DnsProviderDefine } from "./api";
import { Decorator } from "../decorator"; import { Decorator, AUTOWIRE_KEY } from "@certd/pipeline";
import _ from "lodash";
// 提供一个唯一 key // 提供一个唯一 key
export const DNS_PROVIDER_CLASS_KEY = "pipeline:dns-provider"; export const DNS_PROVIDER_CLASS_KEY = "pipeline:dns-provider";
@ -9,6 +10,16 @@ export const DNS_PROVIDER_CLASS_KEY = "pipeline:dns-provider";
export function IsDnsProvider(define: DnsProviderDefine): ClassDecorator { export function IsDnsProvider(define: DnsProviderDefine): ClassDecorator {
return (target: any) => { return (target: any) => {
target = Decorator.target(target); target = Decorator.target(target);
const autowires: any = {};
const properties = Decorator.getClassProperties(target);
for (const property in properties) {
const autowire = Reflect.getMetadata(AUTOWIRE_KEY, target, property);
if (autowire) {
autowires[property] = autowire;
}
}
_.merge(define, { autowire: autowires });
Reflect.defineMetadata(DNS_PROVIDER_CLASS_KEY, define, target); Reflect.defineMetadata(DNS_PROVIDER_CLASS_KEY, define, target);
target.define = define; target.define = define;

View File

@ -1,4 +1,4 @@
import { Registry } from "../registry"; import { Registry } from "@certd/pipeline";
// @ts-ignore // @ts-ignore
export const dnsProviderRegistry = new Registry(); export const dnsProviderRegistry = new Registry();

View File

@ -1 +1,2 @@
export * from "./plugin"; export * from "./plugin";
export * from "./dns-provider";

View File

@ -1,10 +1,10 @@
// @ts-ignore // @ts-ignore
import * as acme from "@certd/acme-client"; import * as acme from "@certd/acme-client";
import _ from "lodash"; import _ from "lodash";
import { IDnsProvider } from "@certd/pipeline";
import { Challenge } from "@certd/acme-client/types/rfc8555"; import { Challenge } from "@certd/acme-client/types/rfc8555";
import { Logger } from "log4js"; import { Logger } from "log4js";
import { IContext } from "@certd/pipeline/src/core/context"; import { IContext } from "@certd/pipeline/src/core/context";
import { IDnsProvider } from "../../dns-provider";
export class AcmeService { export class AcmeService {
userContext: IContext; userContext: IContext;

View File

@ -1,26 +1,46 @@
import { import { Autowire, HttpClient, IAccessService, IContext, IsTaskPlugin, ITaskPlugin, RunStrategy, TaskInput, TaskOutput } from "@certd/pipeline";
Autowire,
dnsProviderRegistry,
HttpClient,
IAccessService,
IContext,
IsTaskPlugin,
ITaskPlugin,
RunStrategy,
TaskInput,
TaskOutput,
} from "@certd/pipeline";
import forge from "node-forge"; import forge from "node-forge";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { AcmeService } from "./acme"; import { AcmeService } from "./acme";
import _ from "lodash"; import _ from "lodash";
import { Logger } from "log4js"; import { Logger } from "log4js";
import { Decorator } from "@certd/pipeline/src/decorator";
export type CertInfo = { import { DnsProviderDefine, dnsProviderRegistry } from "../../dns-provider";
import fs from "fs";
import os from "os";
export class CertInfo {
crt: string; crt: string;
key: string; key: string;
csr: string; csr: string;
};
detail: any;
expires: number;
constructor(opts: { crt: string; key: string; csr: string }) {
this.crt = opts.crt;
this.key = opts.key;
this.csr = opts.csr;
const { detail, expires } = this.getCrtDetail(this.crt);
this.detail = detail;
this.expires = expires.getTime();
}
getCrtDetail(crt: string) {
const pki = forge.pki;
const detail = pki.certificateFromPem(crt.toString());
const expires = detail.validity.notAfter;
return { detail, expires };
}
saveToFile(type: "crt" | "key", path?: string) {
if (path == null) {
//写入临时目录
path = `${os.tmpdir()}/certd/tmp/${Math.floor(Math.random() * 1000000)}/cert.${type}`;
}
fs.writeFileSync(path, this[type]);
return path;
}
}
@IsTaskPlugin({ @IsTaskPlugin({
name: "CertApply", name: "CertApply",
title: "证书申请", title: "证书申请",
@ -199,11 +219,16 @@ export class CertApplyPlugin implements ITaskPlugin {
); );
this.logger.info("开始申请证书,", email, domains); this.logger.info("开始申请证书,", email, domains);
const dnsProviderClass = dnsProviderRegistry.get(dnsProviderType); const dnsProviderPlugin = dnsProviderRegistry.get(dnsProviderType);
const DnsProviderClass = dnsProviderPlugin.target;
const dnsProviderDefine = dnsProviderPlugin.define as DnsProviderDefine;
const access = await this.accessService.getById(dnsProviderAccessId); const access = await this.accessService.getById(dnsProviderAccessId);
// @ts-ignore // @ts-ignore
const dnsProvider: AbstractDnsProvider = new dnsProviderClass(); const dnsProvider: IDnsProvider = new DnsProviderClass();
dnsProvider.doInit({ access, logger: this.logger, http: this.http }); const context = { access, logger: this.logger, http: this.http };
Decorator.inject(dnsProviderDefine.autowire, dnsProvider, context);
await dnsProvider.onInit();
const cert = await this.acme.order({ const cert = await this.acme.order({
email, email,
@ -239,23 +264,11 @@ export class CertApplyPlugin implements ITaskPlugin {
} }
async readCurrentCert() { async readCurrentCert() {
const cert: CertInfo = await this.pipelineContext.get("cert"); const cert: any = await this.pipelineContext.get("cert");
if (cert == null) { if (cert == null) {
return undefined; return undefined;
} }
const { detail, expires } = this.getCrtDetail(cert.crt); return new CertInfo(cert);
return {
...cert,
detail,
expires: expires.getTime(),
};
}
getCrtDetail(crt: string) {
const pki = forge.pki;
const detail = pki.certificateFromPem(crt.toString());
const expires = detail.validity.notAfter;
return { detail, expires };
} }
/** /**

View File

@ -15,6 +15,7 @@
"@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/ban-ts-ignore": "off", "@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-empty-function": "off",
// "no-unused-expressions": "off", // "no-unused-expressions": "off",
"max-len": [0, 160, 2, { "ignoreUrls": true }] "max-len": [0, 160, 2, { "ignoreUrls": true }]
} }

View File

@ -15,31 +15,31 @@
"ssh2": "^0.8.9" "ssh2": "^0.8.9"
}, },
"devDependencies": { "devDependencies": {
"log4js": "^6.7.1",
"dayjs": "^1.9.7",
"lodash-es": "^4.17.20",
"@types/lodash": "^4.14.186",
"vue-tsc": "^0.38.9",
"@alicloud/cs20151215": "^3.0.3", "@alicloud/cs20151215": "^3.0.3",
"@alicloud/openapi-client": "^0.4.0", "@alicloud/openapi-client": "^0.4.0",
"@alicloud/pop-core": "^1.7.10", "@alicloud/pop-core": "^1.7.10",
"@midwayjs/core": "^3.0.0", "@midwayjs/core": "^3.0.0",
"@midwayjs/decorator": "^3.0.0", "@midwayjs/decorator": "^3.0.0",
"@types/chai": "^4.3.3", "@types/chai": "^4.3.3",
"@types/lodash": "^4.14.186",
"@types/mocha": "^10.0.0", "@types/mocha": "^10.0.0",
"@types/node-forge": "^1.3.0", "@types/node-forge": "^1.3.0",
"@types/ssh2": "^1.11.6",
"@typescript-eslint/eslint-plugin": "^5.38.1", "@typescript-eslint/eslint-plugin": "^5.38.1",
"@typescript-eslint/parser": "^5.38.1", "@typescript-eslint/parser": "^5.38.1",
"chai": "^4.3.6", "chai": "^4.3.6",
"dayjs": "^1.9.7",
"eslint": "^8.24.0", "eslint": "^8.24.0",
"eslint-config-prettier": "^8.5.0", "eslint-config-prettier": "^8.5.0",
"eslint-plugin-import": "^2.26.0", "eslint-plugin-import": "^2.26.0",
"eslint-plugin-node": "^11.1.0", "eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^4.2.1", "eslint-plugin-prettier": "^4.2.1",
"lodash-es": "^4.17.20",
"log4js": "^6.3.0", "log4js": "^6.3.0",
"mocha": "^10.1.0", "mocha": "^10.1.0",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"typescript": "^4.8.4", "typescript": "^4.8.4",
"vite": "^3.1.0" "vite": "^3.1.0",
"vue-tsc": "^0.38.9"
} }
} }

View File

@ -1,48 +1,47 @@
import { AbstractAccess, IsAccess } from "@certd/pipeline"; import { AccessInput, IAccess, IsAccess } from "@certd/pipeline";
@IsAccess({ @IsAccess({
name: "ssh", name: "ssh",
title: "主机登录授权", title: "主机登录授权",
desc: "", desc: "",
input: { input: {},
host: {
title: "主机地址",
component: {
placeholder: "主机域名或IP地址",
},
required: true,
},
port: {
title: "端口",
value: "22",
component: {
placeholder: "22",
},
rules: [{ required: true, message: "此项必填" }],
},
username: {
title: "用户名",
value: "root",
rules: [{ required: true, message: "此项必填" }],
},
password: {
title: "密码",
component: {
name: "a-input-password",
vModel: "value",
},
helper: "登录密码或密钥必填一项",
},
privateKey: {
title: "密钥",
helper: "密钥或密码必填一项",
},
},
}) })
export class SshAccess extends AbstractAccess { export class SshAccess implements IAccess {
host = ""; @AccessInput({
port = 22; title: "主机地址",
username = "root"; component: {
password?: string; placeholder: "主机域名或IP地址",
privateKey?: string; },
required: true,
})
host!: string;
@AccessInput({
title: "端口",
value: "22",
component: {
placeholder: "22",
},
rules: [{ required: true, message: "此项必填" }],
})
port!: string;
@AccessInput({
title: "用户名",
value: "root",
rules: [{ required: true, message: "此项必填" }],
})
username!: string;
@AccessInput({
title: "密码",
component: {
name: "a-input-password",
vModel: "value",
},
helper: "登录密码或密钥必填一项",
})
password!: string;
@AccessInput({
title: "密钥",
helper: "密钥或密码必填一项",
})
privateKey!: string;
} }

View File

@ -1,10 +1,11 @@
// @ts-ignore
import ssh2 from "ssh2"; import ssh2 from "ssh2";
import path from "path"; import path from "path";
import _ from "lodash"; import _ from "lodash";
import { Logger } from "log4js"; import { ILogger } from "@certd/pipeline";
export class SshClient { export class SshClient {
logger: Logger; logger: ILogger;
constructor(logger: Logger) { constructor(logger: ILogger) {
this.logger = logger; this.logger = logger;
} }
/** /**

View File

@ -1,47 +1,56 @@
import { IsTask, TaskInput, TaskOutput, TaskPlugin, AbstractPlugin, RunStrategy } from "@certd/pipeline"; import { Autowire, IAccessService, IsTaskPlugin, ILogger, RunStrategy, TaskInput, ITaskPlugin } from "@certd/pipeline";
import { SshClient } from "../../lib/ssh"; import { SshClient } from "../../lib/ssh";
import { CertInfo } from "@certd/plugin-cert";
@IsTask(() => { @IsTaskPlugin({
return { name: "hostShellExecute",
name: "hostShellExecute", title: "执行远程主机脚本命令",
title: "执行远程主机脚本命令", input: {},
input: { default: {
accessId: { strategy: {
title: "主机登录配置", runStrategy: RunStrategy.SkipWhenSucceed,
helper: "登录",
component: {
name: "pi-access-selector",
type: "ssh",
},
required: true,
},
cert: {
title: "域名证书",
helper: "请选择前置任务输出的域名证书",
component: {
name: "pi-output-selector",
},
required: true,
},
script: {
title: "shell脚本命令",
component: {
name: "a-textarea",
vModel: "value",
},
},
}, },
default: { },
strategy: { output: {},
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
output: {},
};
}) })
export class HostShellExecutePlugin extends AbstractPlugin implements TaskPlugin { export class HostShellExecutePlugin implements ITaskPlugin {
async execute(input: TaskInput): Promise<TaskOutput> { @TaskInput({
const { script, accessId } = input; title: "主机登录配置",
helper: "登录",
component: {
name: "pi-access-selector",
type: "ssh",
},
required: true,
})
accessId!: string;
@TaskInput({
title: "域名证书",
helper: "请选择前置任务输出的域名证书",
component: {
name: "pi-output-selector",
},
required: true,
})
cert!: CertInfo;
@TaskInput({
title: "shell脚本命令",
component: {
name: "a-textarea",
vModel: "value",
},
})
script!: string;
@Autowire()
accessService!: IAccessService;
@Autowire()
logger!: ILogger;
// eslint-disable-next-line @typescript-eslint/no-empty-function
async onInit() {}
async execute(): Promise<void> {
const { script, accessId } = this;
const connectConf = this.accessService.getById(accessId); const connectConf = this.accessService.getById(accessId);
const sshClient = new SshClient(this.logger); const sshClient = new SshClient(this.logger);
const ret = await sshClient.exec({ const ret = await sshClient.exec({
@ -49,6 +58,5 @@ export class HostShellExecutePlugin extends AbstractPlugin implements TaskPlugin
script, script,
}); });
this.logger.info("exec res:", ret); this.logger.info("exec res:", ret);
return {};
} }
} }

View File

@ -1,71 +1,87 @@
import { IsTask, TaskInput, TaskOutput, TaskPlugin, AbstractPlugin, RunStrategy } from "@certd/pipeline"; import { Autowire, IAccessService, IsTaskPlugin, ITaskPlugin, ILogger, RunStrategy, TaskInput, TaskOutput } from "@certd/pipeline";
import { SshClient } from "../../lib/ssh"; import { SshClient } from "../../lib/ssh";
import { CertInfo } from "@certd/plugin-cert";
import * as fs from "fs";
@IsTask(() => { @IsTaskPlugin({
return { name: "uploadCertToHost",
name: "uploadCertToHost", title: "上传证书到主机",
title: "上传证书到主机", default: {
input: { strategy: {
crtPath: { runStrategy: RunStrategy.SkipWhenSucceed,
title: "证书保存路径",
},
keyPath: {
title: "私钥保存路径",
},
cert: {
title: "域名证书",
helper: "请选择前置任务输出的域名证书",
component: {
name: "pi-output-selector",
},
required: true,
},
accessId: {
title: "主机登录配置",
helper: "access授权",
component: {
name: "pi-access-selector",
type: "ssh",
},
rules: [{ required: true, message: "此项必填" }],
},
sudo: {
title: "是否sudo",
component: {
name: "a-checkbox",
vModel: "checked",
},
},
}, },
default: { },
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
output: {
hostCrtPath: {
title: "上传成功后的证书路径",
},
hostKeyPath: {
title: "上传成功后的私钥路径",
},
},
};
}) })
export class UploadCertToHostPlugin extends AbstractPlugin implements TaskPlugin { export class UploadCertToHostPlugin implements ITaskPlugin {
async execute(input: TaskInput): Promise<TaskOutput> { @TaskInput({
const { crtPath, keyPath, cert, accessId, sudo } = input; title: "证书保存路径",
})
crtPath!: string;
@TaskInput({
title: "私钥保存路径",
})
keyPath!: string;
@TaskInput({
title: "域名证书",
helper: "请选择前置任务输出的域名证书",
component: {
name: "pi-output-selector",
},
required: true,
})
cert!: CertInfo;
@TaskInput({
title: "主机登录配置",
helper: "access授权",
component: {
name: "pi-access-selector",
type: "ssh",
},
rules: [{ required: true, message: "此项必填" }],
})
accessId!: string;
@TaskInput({
title: "是否sudo",
component: {
name: "a-checkbox",
vModel: "checked",
},
})
sudo!: boolean;
@Autowire()
accessService!: IAccessService;
@Autowire()
logger!: ILogger;
@TaskOutput({
title: "证书保存路径",
})
hostCrtPath!: string;
@TaskOutput({
title: "私钥保存路径",
})
hostKeyPath!: string;
async onInit() {}
async execute(): Promise<void> {
const { crtPath, keyPath, cert, accessId, sudo } = this;
const connectConf = this.accessService.getById(accessId); const connectConf = this.accessService.getById(accessId);
const sshClient = new SshClient(this.logger); const sshClient = new SshClient(this.logger);
const saveCrtPath = cert.saveToFile("crt");
const saveKeyPath = cert.saveToFile("key");
await sshClient.uploadFiles({ await sshClient.uploadFiles({
connectConf, connectConf,
transports: [ transports: [
{ {
localPath: cert.crtPath, localPath: saveCrtPath,
remotePath: crtPath, remotePath: crtPath,
}, },
{ {
localPath: cert.keyPath, localPath: saveKeyPath,
remotePath: keyPath, remotePath: keyPath,
}, },
], ],
@ -73,9 +89,12 @@ export class UploadCertToHostPlugin extends AbstractPlugin implements TaskPlugin
}); });
this.logger.info("证书上传成功crtPath=", crtPath, ",keyPath=", keyPath); this.logger.info("证书上传成功crtPath=", crtPath, ",keyPath=", keyPath);
return { //删除临时文件
hostCrtPath: crtPath, fs.unlinkSync(saveCrtPath);
hostKeyPath: keyPath, fs.unlinkSync(saveKeyPath);
};
//输出
this.hostCrtPath = crtPath;
this.hostKeyPath = keyPath;
} }
} }

View File

@ -15,6 +15,7 @@
"@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/ban-ts-ignore": "off", "@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-empty-function": "off",
// "no-unused-expressions": "off", // "no-unused-expressions": "off",
"max-len": [0, 160, 2, { "ignoreUrls": true }] "max-len": [0, 160, 2, { "ignoreUrls": true }]
} }

View File

@ -13,6 +13,7 @@
"dependencies": { "dependencies": {
"@certd/pipeline": "^0.3.0", "@certd/pipeline": "^0.3.0",
"@certd/plugin-util": "^0.3.0", "@certd/plugin-util": "^0.3.0",
"@certd/plugin-cert": "^0.3.0",
"tencentcloud-sdk-nodejs": "^4.0.44" "tencentcloud-sdk-nodejs": "^4.0.44"
}, },
"devDependencies": { "devDependencies": {

View File

@ -1,4 +1,4 @@
import { IsAccess, IsAccessInput } from "@certd/pipeline"; import { IsAccess, AccessInput } from "@certd/pipeline";
@IsAccess({ @IsAccess({
name: "dnspod", name: "dnspod",
@ -6,7 +6,7 @@ import { IsAccess, IsAccessInput } from "@certd/pipeline";
desc: "腾讯云的域名解析接口已迁移到dnspod", desc: "腾讯云的域名解析接口已迁移到dnspod",
}) })
export class DnspodAccess { export class DnspodAccess {
@IsAccessInput({ @AccessInput({
title: "token", title: "token",
component: { component: {
placeholder: "开放接口token", placeholder: "开放接口token",
@ -14,7 +14,7 @@ export class DnspodAccess {
rules: [{ required: true, message: "该项必填" }], rules: [{ required: true, message: "该项必填" }],
}) })
token = ""; token = "";
@IsAccessInput({ @AccessInput({
title: "账户id", title: "账户id",
component: { component: {
placeholder: "dnspod接口账户id", placeholder: "dnspod接口账户id",

View File

@ -1,11 +1,11 @@
import { IsAccess, IsAccessInput } from "@certd/pipeline"; import { IsAccess, AccessInput } from "@certd/pipeline";
@IsAccess({ @IsAccess({
name: "tencent", name: "tencent",
title: "腾讯云", title: "腾讯云",
}) })
export class TencentAccess { export class TencentAccess {
@IsAccessInput({ @AccessInput({
title: "secretId", title: "secretId",
component: { component: {
placeholder: "secretId", placeholder: "secretId",
@ -13,7 +13,7 @@ export class TencentAccess {
rules: [{ required: true, message: "该项必填" }], rules: [{ required: true, message: "该项必填" }],
}) })
secretId = ""; secretId = "";
@IsAccessInput({ @AccessInput({
title: "secretKey", title: "secretKey",
component: { component: {
placeholder: "secretKey", placeholder: "secretKey",

View File

@ -1,8 +1,7 @@
import { CreateRecordOptions, HttpClient, IDnsProvider, IsDnsProvider, RemoveRecordOptions } from "@certd/pipeline"; import { Autowire, HttpClient, ILogger } from "@certd/pipeline";
import { CreateRecordOptions, IDnsProvider, IsDnsProvider, RemoveRecordOptions } from "@certd/plugin-cert";
import _ from "lodash"; import _ from "lodash";
import { DnspodAccess } from "../access"; import { DnspodAccess } from "../access";
import { Inject } from "@midwayjs/decorator";
import { ILogger } from "@midwayjs/core";
@IsDnsProvider({ @IsDnsProvider({
name: "dnspod", name: "dnspod",
@ -11,12 +10,12 @@ import { ILogger } from "@midwayjs/core";
accessType: "dnspod", accessType: "dnspod",
}) })
export class DnspodDnsProvider implements IDnsProvider { export class DnspodDnsProvider implements IDnsProvider {
@Inject() @Autowire()
http!: HttpClient; http!: HttpClient;
@Inject() @Autowire()
access!: DnspodAccess; access!: DnspodAccess;
@Inject() @Autowire()
logger!: ILogger; logger!: ILogger;
loginToken: any; loginToken: any;

View File

@ -1,7 +1,7 @@
import { IAccessService, IsTaskPlugin, RunStrategy, TaskInput, ITaskPlugin, LOGGER } from "@certd/pipeline"; import { Autowire, IAccessService, IsTaskPlugin, ITaskPlugin, ILogger, RunStrategy, TaskInput } from "@certd/pipeline";
import tencentcloud from "tencentcloud-sdk-nodejs/index"; import tencentcloud from "tencentcloud-sdk-nodejs/index";
import { TencentAccess } from "../../access"; import { TencentAccess } from "../../access";
import { Inject } from "@midwayjs/decorator"; import { CertInfo } from "@certd/plugin-cert";
@IsTaskPlugin({ @IsTaskPlugin({
name: "DeployCertToTencentCDN", name: "DeployCertToTencentCDN",
@ -22,7 +22,7 @@ export class DeployToCdnPlugin implements ITaskPlugin {
}, },
required: true, required: true,
}) })
cert!: any; cert!: CertInfo;
@TaskInput({ @TaskInput({
title: "Access提供者", title: "Access提供者",
@ -47,11 +47,11 @@ export class DeployToCdnPlugin implements ITaskPlugin {
}) })
domainName!: string; domainName!: string;
@Inject() @Autowire()
accessService!: IAccessService; accessService!: IAccessService;
@Inject() @Autowire()
logger!: LOGGER; logger!: ILogger;
// eslint-disable-next-line @typescript-eslint/no-empty-function // eslint-disable-next-line @typescript-eslint/no-empty-function
async onInit() {} async onInit() {}

View File

@ -1,8 +1,7 @@
import { IAccessService, IsTaskPlugin, ITaskPlugin, LOGGER, RunStrategy, TaskInput, utils } from "@certd/pipeline"; import { Autowire, IAccessService, IsTaskPlugin, ITaskPlugin, ILogger, RunStrategy, TaskInput, utils } from "@certd/pipeline";
import tencentcloud from "tencentcloud-sdk-nodejs/index"; import tencentcloud from "tencentcloud-sdk-nodejs/index";
import { TencentAccess } from "../../access"; import { TencentAccess } from "../../access";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { Inject } from "@midwayjs/decorator";
@IsTaskPlugin({ @IsTaskPlugin({
name: "DeployCertToTencentCLB", name: "DeployCertToTencentCLB",
@ -72,11 +71,11 @@ export class DeployToClbPlugin implements ITaskPlugin {
}) })
accessId!: string; accessId!: string;
@Inject() @Autowire()
accessService!: IAccessService; accessService!: IAccessService;
@Inject() @Autowire()
logger!: LOGGER; logger!: ILogger;
// eslint-disable-next-line @typescript-eslint/no-empty-function // eslint-disable-next-line @typescript-eslint/no-empty-function
async onInit() {} async onInit() {}

View File

@ -1,4 +1,4 @@
import { Autowire, IAccessService, IsTaskPlugin, ITaskPlugin, RunStrategy, TaskInput, TaskOutput, LOGGER } from "@certd/pipeline"; import { Autowire, IAccessService, IsTaskPlugin, ITaskPlugin, RunStrategy, TaskInput, TaskOutput, ILogger } from "@certd/pipeline";
import tencentcloud from "tencentcloud-sdk-nodejs/index"; import tencentcloud from "tencentcloud-sdk-nodejs/index";
import dayjs from "dayjs"; import dayjs from "dayjs";
@ -46,7 +46,7 @@ export class UploadToTencentPlugin implements ITaskPlugin {
accessService!: IAccessService; accessService!: IAccessService;
@Autowire() @Autowire()
logger!: LOGGER; logger!: ILogger;
// eslint-disable-next-line @typescript-eslint/no-empty-function // eslint-disable-next-line @typescript-eslint/no-empty-function
async onInit() {} async onInit() {}

View File

@ -15,6 +15,7 @@
"@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/ban-ts-ignore": "off", "@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-empty-function": "off",
// "no-unused-expressions": "off", // "no-unused-expressions": "off",
"max-len": [0, 160, 2, { "ignoreUrls": true }] "max-len": [0, 160, 2, { "ignoreUrls": true }]
} }

@ -1 +1 @@
Subproject commit b7422ab48ef81f5c98129bacb69d0eefdfe50645 Subproject commit 8c7b3853be27df392dff765d39c6c53de09418bd

@ -1 +1 @@
Subproject commit 5901fb5a440a7cebe3a2b6dfaec1b014e30b3a0c Subproject commit a5681f154468fc5c4aac7e4a0ce090cfffc00e9e