mirror of https://github.com/certd/certd
refactor: decorator
parent
c23f6172b5
commit
717d203622
|
@ -12,22 +12,29 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/acme-client": "^0.3.0",
|
"@certd/acme-client": "^0.3.0",
|
||||||
|
"axios": "^0.21.1",
|
||||||
"dayjs": "^1.11.6",
|
"dayjs": "^1.11.6",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"node-forge": "^0.10.0",
|
|
||||||
"log4js": "^6.3.0",
|
"log4js": "^6.3.0",
|
||||||
"axios": "^0.21.1",
|
"node-forge": "^0.10.0",
|
||||||
"qs": "^6.9.4"
|
"qs": "^6.9.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@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/bootstrap": "^3.9.1",
|
||||||
|
"@midwayjs/cache": "^3.9.0",
|
||||||
|
"@midwayjs/cli": "^1.3.21",
|
||||||
"@midwayjs/core": "^3.0.0",
|
"@midwayjs/core": "^3.0.0",
|
||||||
"@midwayjs/decorator": "^3.0.0",
|
"@midwayjs/decorator": "^3.0.0",
|
||||||
|
"@midwayjs/koa": "^3.9.0",
|
||||||
|
"@midwayjs/logger": "^2.17.0",
|
||||||
|
"@midwayjs/typeorm": "^3.9.0",
|
||||||
|
"@midwayjs/validate": "^3.9.0",
|
||||||
"@types/chai": "^4.3.3",
|
"@types/chai": "^4.3.3",
|
||||||
|
"@types/lodash": "^4.14.186",
|
||||||
|
"@types/lodash-es": "^4.17.6",
|
||||||
"@types/mocha": "^10.0.0",
|
"@types/mocha": "^10.0.0",
|
||||||
"@types/node-forge": "^1.3.0",
|
"@types/node-forge": "^1.3.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.38.1",
|
"@typescript-eslint/eslint-plugin": "^5.38.1",
|
||||||
|
@ -41,6 +48,7 @@
|
||||||
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
import { AbstractRegistrable } from "../registry";
|
|
||||||
import { AccessDefine } from "./api";
|
|
||||||
|
|
||||||
export abstract class AbstractAccess extends AbstractRegistrable<AccessDefine> {}
|
|
|
@ -3,22 +3,15 @@ import { accessRegistry } from "./registry";
|
||||||
import { FormItemProps } from "../d.ts";
|
import { FormItemProps } from "../d.ts";
|
||||||
import { AbstractAccess } from "./abstract-access";
|
import { AbstractAccess } from "./abstract-access";
|
||||||
|
|
||||||
export type AccessInput = FormItemProps & {
|
export type AccessInputDefine = FormItemProps & {
|
||||||
title: string;
|
title: string;
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
};
|
};
|
||||||
export type AccessDefine = Registrable & {
|
export type AccessDefine = Registrable & {
|
||||||
input: {
|
inputs?: {
|
||||||
[key: string]: AccessInput;
|
[key: string]: AccessInputDefine;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
export function IsAccess(define: AccessDefine) {
|
|
||||||
return function (target: any) {
|
|
||||||
target.prototype.define = define;
|
|
||||||
accessRegistry.install(target);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IAccessService {
|
export interface IAccessService {
|
||||||
getById(id: any): Promise<AbstractAccess>;
|
getById(id: any): Promise<AbstractAccess>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
// src/decorator/memoryCache.decorator.ts
|
||||||
|
import {
|
||||||
|
attachClassMetadata,
|
||||||
|
attachPropertyDataToClass,
|
||||||
|
getClassMetadata,
|
||||||
|
listModule,
|
||||||
|
listPropertyDataFromClass,
|
||||||
|
saveClassMetadata,
|
||||||
|
saveModule,
|
||||||
|
} from "@midwayjs/decorator";
|
||||||
|
import { AccessDefine, AccessInputDefine } from "./api";
|
||||||
|
import _ from "lodash-es";
|
||||||
|
import { accessRegistry } from "./registry";
|
||||||
|
|
||||||
|
// 提供一个唯一 key
|
||||||
|
export const ACCESS_CLASS_KEY = "decorator:access";
|
||||||
|
|
||||||
|
export function IsAccess(define: AccessDefine): ClassDecorator {
|
||||||
|
console.log("is access define:", define);
|
||||||
|
return (target: any) => {
|
||||||
|
console.log("is access load:", target);
|
||||||
|
// 将装饰的类,绑定到该装饰器,用于后续能获取到 class
|
||||||
|
saveModule(ACCESS_CLASS_KEY, target);
|
||||||
|
// 保存一些元数据信息,任意你希望存的东西
|
||||||
|
saveClassMetadata(
|
||||||
|
ACCESS_CLASS_KEY,
|
||||||
|
{
|
||||||
|
define,
|
||||||
|
},
|
||||||
|
target
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ACCESS_INPUT_KEY = "decorator:access:input";
|
||||||
|
|
||||||
|
export function IsAccessInput(input?: AccessInputDefine): PropertyDecorator {
|
||||||
|
return (target, propertyKey) => {
|
||||||
|
attachPropertyDataToClass(ACCESS_INPUT_KEY, { input }, target, propertyKey, propertyKey as string);
|
||||||
|
|
||||||
|
attachClassMetadata(
|
||||||
|
ACCESS_CLASS_KEY,
|
||||||
|
{
|
||||||
|
inputs: {
|
||||||
|
[propertyKey]: input,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
target
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function registerAccess() {
|
||||||
|
const modules = listModule(ACCESS_CLASS_KEY);
|
||||||
|
for (const mod of modules) {
|
||||||
|
console.log("mod", mod);
|
||||||
|
const define = getClassMetadata(ACCESS_CLASS_KEY, mod);
|
||||||
|
console.log("define", define);
|
||||||
|
const inputs = listPropertyDataFromClass(ACCESS_INPUT_KEY, mod);
|
||||||
|
console.log("inputs", inputs);
|
||||||
|
for (const input of inputs) {
|
||||||
|
define.inputs = {};
|
||||||
|
_.merge(define.inputs, input.inputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
accessRegistry.register(define.name, {
|
||||||
|
define,
|
||||||
|
target: mod,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,3 @@
|
||||||
export * from "./api";
|
export * from "./api";
|
||||||
export * from "./abstract-access";
|
|
||||||
export * from "./registry";
|
export * from "./registry";
|
||||||
|
export * from "./decorator";
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { Registry } from "../registry";
|
import { Registry } from "../registry";
|
||||||
import { AbstractAccess } from "./abstract-access";
|
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
export const accessRegistry = new Registry<typeof AbstractAccess>();
|
export const accessRegistry = new Registry();
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { AxiosInstance } from "axios";
|
||||||
|
import { IContext } from "../core";
|
||||||
|
|
||||||
|
export type HttpClient = AxiosInstance;
|
||||||
|
export type UserContext = IContext;
|
||||||
|
export type PipelineContext = IContext;
|
|
@ -1,13 +1,14 @@
|
||||||
import { ConcurrencyStrategy, Pipeline, ResultType, Runnable, RunStrategy, Stage, Step, Task } from "../d.ts";
|
import { ConcurrencyStrategy, Pipeline, ResultType, Runnable, RunStrategy, Stage, Step, Task } from "../d.ts";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { RunHistory } from "./run-history";
|
import { RunHistory } from "./run-history";
|
||||||
import { pluginRegistry, TaskPlugin } from "../plugin";
|
import { pluginRegistry, ITaskPlugin, PluginDefine } from "../plugin";
|
||||||
import { ContextFactory, IContext } from "./context";
|
import { ContextFactory, IContext } from "./context";
|
||||||
import { IStorage } from "./storage";
|
import { IStorage } from "./storage";
|
||||||
import { logger } from "../utils/util.log";
|
import { logger } from "../utils/util.log";
|
||||||
import { Logger } from "log4js";
|
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 { Registrable, RegistryItem } from "../registry";
|
||||||
export class Executor {
|
export class Executor {
|
||||||
userId: any;
|
userId: any;
|
||||||
pipeline: Pipeline;
|
pipeline: Pipeline;
|
||||||
|
@ -134,40 +135,38 @@ export class Executor {
|
||||||
|
|
||||||
private async runStep(step: Step) {
|
private async runStep(step: Step) {
|
||||||
//执行任务
|
//执行任务
|
||||||
const taskPlugin: TaskPlugin = await this.getPlugin(step.type, this.runtime.loggers[step.id]);
|
const plugin: RegistryItem = pluginRegistry.get(step.type);
|
||||||
|
const context = {
|
||||||
|
logger: this.runtime.loggers[step.id],
|
||||||
|
accessService: this.accessService,
|
||||||
|
pipelineContext: this.pipelineContext,
|
||||||
|
userContext: this.contextFactory.getContext("user", this.userId),
|
||||||
|
http: request,
|
||||||
|
};
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
delete taskPlugin.define;
|
const instance = new plugin.target();
|
||||||
const define = taskPlugin.getDefine();
|
// @ts-ignore
|
||||||
|
const define: PluginDefine = plugin.define;
|
||||||
//从outputContext读取输入参数
|
//从outputContext读取输入参数
|
||||||
_.forEach(define.input, (item, key) => {
|
_.forEach(define.inputs, (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];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const res = await taskPlugin.execute(step.input);
|
|
||||||
|
|
||||||
|
const res = await instance.execute();
|
||||||
|
|
||||||
//输出到output context
|
//输出到output context
|
||||||
_.forEach(define.output, (item, key) => {
|
_.forEach(define.outputs, (item, key) => {
|
||||||
const contextKey = `step.${step.id}.${key}`;
|
const contextKey = `step.${step.id}.${key}`;
|
||||||
this.runtime.context[contextKey] = res[key];
|
this.runtime.context[contextKey] = res[key];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getPlugin(type: string, logger: Logger): Promise<TaskPlugin> {
|
|
||||||
const pluginClass = pluginRegistry.get(type);
|
|
||||||
// @ts-ignore
|
|
||||||
const plugin = new pluginClass();
|
|
||||||
await plugin.doInit({
|
|
||||||
accessService: this.accessService,
|
|
||||||
pipelineContext: this.pipelineContext,
|
|
||||||
userContext: this.contextFactory.getContext("user", this.userId),
|
|
||||||
logger,
|
|
||||||
http: request,
|
|
||||||
});
|
|
||||||
return plugin;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
import { Registrable } from "../registry";
|
import { Registrable } from "../registry";
|
||||||
import { dnsProviderRegistry } from "./registry";
|
|
||||||
import { AbstractDnsProvider } from "./abstract-dns-provider";
|
|
||||||
|
|
||||||
export type DnsProviderDefine = Registrable & {
|
export type DnsProviderDefine = Registrable & {
|
||||||
accessType: string;
|
accessType: string;
|
||||||
|
@ -16,19 +14,6 @@ export type RemoveRecordOptions = CreateRecordOptions & {
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface IDnsProvider {
|
export interface IDnsProvider {
|
||||||
getDefine(): DnsProviderDefine;
|
|
||||||
createRecord(options: CreateRecordOptions): Promise<any>;
|
createRecord(options: CreateRecordOptions): Promise<any>;
|
||||||
|
|
||||||
removeRecord(options: RemoveRecordOptions): Promise<any>;
|
removeRecord(options: RemoveRecordOptions): Promise<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function IsDnsProvider(define: (() => DnsProviderDefine) | DnsProviderDefine) {
|
|
||||||
return function (target: typeof AbstractDnsProvider) {
|
|
||||||
if (define instanceof Function) {
|
|
||||||
target.prototype.define = define();
|
|
||||||
} else {
|
|
||||||
target.prototype.define = define;
|
|
||||||
}
|
|
||||||
dnsProviderRegistry.install(target);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
// src/decorator/memoryCache.decorator.ts
|
||||||
|
import {
|
||||||
|
attachClassMetadata,
|
||||||
|
attachPropertyDataToClass,
|
||||||
|
createCustomPropertyDecorator,
|
||||||
|
getClassMetadata,
|
||||||
|
listModule,
|
||||||
|
listPropertyDataFromClass,
|
||||||
|
Provide,
|
||||||
|
saveClassMetadata,
|
||||||
|
saveModule,
|
||||||
|
Scope,
|
||||||
|
ScopeEnum,
|
||||||
|
} from "@midwayjs/decorator";
|
||||||
|
import _ from "lodash-es";
|
||||||
|
import { dnsProviderRegistry } from "./registry";
|
||||||
|
import { DnsProviderDefine } from "./api";
|
||||||
|
|
||||||
|
// 提供一个唯一 key
|
||||||
|
export const DNS_PROVIDER_CLASS_KEY = "decorator:dnsProvider";
|
||||||
|
|
||||||
|
export function IsDnsProvider(define: DnsProviderDefine): ClassDecorator {
|
||||||
|
console.log("is task plugin define:", define);
|
||||||
|
return (target: any) => {
|
||||||
|
console.log("is task plugin load:", target);
|
||||||
|
// 将装饰的类,绑定到该装饰器,用于后续能获取到 class
|
||||||
|
saveModule(DNS_PROVIDER_CLASS_KEY, target);
|
||||||
|
// 保存一些元数据信息,任意你希望存的东西
|
||||||
|
saveClassMetadata(
|
||||||
|
DNS_PROVIDER_CLASS_KEY,
|
||||||
|
{
|
||||||
|
define,
|
||||||
|
},
|
||||||
|
target
|
||||||
|
);
|
||||||
|
// 指定 IoC 容器创建实例的作用域,这里注册为请求作用域,这样能取到 ctx
|
||||||
|
Scope(ScopeEnum.Prototype)(target);
|
||||||
|
|
||||||
|
// 调用一下 Provide 装饰器,这样用户的 class 可以省略写 @Provide() 装饰器了
|
||||||
|
Provide()(target);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function registerDnsProviders() {
|
||||||
|
const modules = listModule(DNS_PROVIDER_CLASS_KEY);
|
||||||
|
for (const mod of modules) {
|
||||||
|
console.log("mod", mod);
|
||||||
|
const define = getClassMetadata(DNS_PROVIDER_CLASS_KEY, mod);
|
||||||
|
console.log("define", define);
|
||||||
|
dnsProviderRegistry.register(define.name, {
|
||||||
|
define,
|
||||||
|
target: mod,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,3 @@
|
||||||
export * from "./api";
|
export * from "./api";
|
||||||
export * from "./registry";
|
export * from "./registry";
|
||||||
export * from "./abstract-dns-provider";
|
export * from "./decorator";
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { Registry } from "../registry";
|
import { Registry } from "../registry";
|
||||||
import { AbstractDnsProvider } from "./abstract-dns-provider";
|
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
export const dnsProviderRegistry = new Registry<typeof AbstractDnsProvider>();
|
export const dnsProviderRegistry = new Registry();
|
||||||
|
|
|
@ -5,3 +5,6 @@ export * from "./registry";
|
||||||
export * from "./dns-provider";
|
export * from "./dns-provider";
|
||||||
export * from "./plugin";
|
export * from "./plugin";
|
||||||
export * from "./utils";
|
export * from "./utils";
|
||||||
|
export * from "./decorator";
|
||||||
|
export * from "./midway";
|
||||||
|
export * from "./context";
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
import { Config, Configuration, getClassMetadata, Inject, listModule, listPropertyDataFromClass, Logger } from "@midwayjs/decorator";
|
||||||
|
import _ from "lodash-es";
|
||||||
|
// @ts-ignore
|
||||||
|
import { ILogger } from "@midwayjs/logger";
|
||||||
|
import { IMidwayContainer, MidwayDecoratorService } from "@midwayjs/core";
|
||||||
|
import { pluginRegistry } from "../plugin";
|
||||||
|
import { registerPlugins } from "../plugin/decorator";
|
||||||
|
import { registerAccess } from "../access/decorator";
|
||||||
|
import { registerDnsProviders } from "../dns-provider";
|
||||||
|
|
||||||
|
// ... (see below) ...
|
||||||
|
@Configuration({
|
||||||
|
namespace: "pipeline",
|
||||||
|
//importConfigs: [join(__dirname, './config')],
|
||||||
|
})
|
||||||
|
export class PipelineConfiguration {
|
||||||
|
@Config()
|
||||||
|
// @ts-ignore
|
||||||
|
config;
|
||||||
|
@Logger()
|
||||||
|
// @ts-ignore
|
||||||
|
logger: ILogger;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
// @ts-ignore
|
||||||
|
decoratorService: MidwayDecoratorService;
|
||||||
|
|
||||||
|
async onReady(container: IMidwayContainer) {
|
||||||
|
this.logger.info("pipeline install");
|
||||||
|
|
||||||
|
registerPlugins();
|
||||||
|
registerAccess();
|
||||||
|
registerDnsProviders();
|
||||||
|
//this.implPropertyDecorator(container);
|
||||||
|
this.logger.info("pipeline installed");
|
||||||
|
}
|
||||||
|
|
||||||
|
// implPropertyDecorator(container: IMidwayContainer) {
|
||||||
|
// this.logger.info("初始化 property decorator");
|
||||||
|
// // 实现装饰器
|
||||||
|
// this.decoratorService.registerPropertyHandler(CLASS_INPUTS_KEY, (propertyName, meta) => {
|
||||||
|
// return undefined;
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// const autowireWhiteList: any = {
|
||||||
|
// logger: true,
|
||||||
|
// };
|
||||||
|
// this.decoratorService.registerPropertyHandler(CLASS_AUTOWIRE_KEY, (propertyName, meta) => {
|
||||||
|
// // eslint-disable-next-line no-debugger
|
||||||
|
// debugger;
|
||||||
|
// const className = meta.name;
|
||||||
|
// if (autowireWhiteList[className]) {
|
||||||
|
// //在白名单里面,注入
|
||||||
|
// return container.get(className);
|
||||||
|
// }
|
||||||
|
// this.logger.warn(`autowire failed:${className} class is not in white list`);
|
||||||
|
// return undefined;
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
// src/index.ts
|
||||||
|
export { PipelineConfiguration } from "./configuration";
|
||||||
|
// export * from './controller/user';
|
||||||
|
// export * from './controller/api';
|
||||||
|
// export * from './service/user';
|
|
@ -1,13 +1,6 @@
|
||||||
import { Registrable } from "../registry";
|
import { Registrable } from "../registry";
|
||||||
import { pluginRegistry } from "./registry";
|
import { pluginRegistry } from "./registry";
|
||||||
import { FormItemProps } from "../d.ts";
|
import { FormItemProps } from "../d.ts";
|
||||||
export type TaskInput = {
|
|
||||||
[key: string]: any;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TaskOutput = {
|
|
||||||
[key: string]: any;
|
|
||||||
};
|
|
||||||
|
|
||||||
export enum ContextScope {
|
export enum ContextScope {
|
||||||
global,
|
global,
|
||||||
|
@ -28,17 +21,19 @@ export type TaskOutputDefine = {
|
||||||
export type TaskInputDefine = FormItemProps;
|
export type TaskInputDefine = FormItemProps;
|
||||||
|
|
||||||
export type PluginDefine = Registrable & {
|
export type PluginDefine = Registrable & {
|
||||||
input: {
|
default?: any;
|
||||||
|
inputs?: {
|
||||||
[key: string]: TaskInputDefine;
|
[key: string]: TaskInputDefine;
|
||||||
};
|
};
|
||||||
output: {
|
outputs?: {
|
||||||
[key: string]: TaskOutputDefine;
|
[key: string]: TaskOutputDefine;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
autowire?: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface TaskPlugin {
|
export interface ITaskPlugin {
|
||||||
getDefine(): PluginDefine;
|
execute(): Promise<void>;
|
||||||
execute(input: TaskInput): Promise<TaskOutput>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OutputVO = {
|
export type OutputVO = {
|
||||||
|
@ -46,15 +41,3 @@ export type OutputVO = {
|
||||||
title: string;
|
title: string;
|
||||||
value: any;
|
value: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function IsTask(define: (() => PluginDefine) | PluginDefine) {
|
|
||||||
return function (target: any) {
|
|
||||||
if (define instanceof Function) {
|
|
||||||
target.prototype.define = define();
|
|
||||||
} else {
|
|
||||||
target.prototype.define = define;
|
|
||||||
}
|
|
||||||
|
|
||||||
pluginRegistry.install(target);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,141 @@
|
||||||
|
// src/decorator/memoryCache.decorator.ts
|
||||||
|
import {
|
||||||
|
attachClassMetadata,
|
||||||
|
attachPropertyDataToClass,
|
||||||
|
createCustomPropertyDecorator,
|
||||||
|
getClassMetadata,
|
||||||
|
listModule,
|
||||||
|
listPropertyDataFromClass,
|
||||||
|
Provide,
|
||||||
|
saveClassMetadata,
|
||||||
|
saveModule,
|
||||||
|
Scope,
|
||||||
|
ScopeEnum,
|
||||||
|
} from "@midwayjs/decorator";
|
||||||
|
import _ from "lodash-es";
|
||||||
|
import { pluginRegistry } from "./registry";
|
||||||
|
import { PluginDefine, TaskInputDefine, TaskOutputDefine } from "./api";
|
||||||
|
|
||||||
|
// 提供一个唯一 key
|
||||||
|
export const PLUGIN_CLASS_KEY = "decorator:plugin";
|
||||||
|
|
||||||
|
export function IsTaskPlugin(define: PluginDefine): ClassDecorator {
|
||||||
|
console.log("is task plugin define:", define);
|
||||||
|
return (target: any) => {
|
||||||
|
console.log("is task plugin load:", target);
|
||||||
|
// 将装饰的类,绑定到该装饰器,用于后续能获取到 class
|
||||||
|
saveModule(PLUGIN_CLASS_KEY, target);
|
||||||
|
// 保存一些元数据信息,任意你希望存的东西
|
||||||
|
saveClassMetadata(
|
||||||
|
PLUGIN_CLASS_KEY,
|
||||||
|
{
|
||||||
|
define,
|
||||||
|
},
|
||||||
|
target
|
||||||
|
);
|
||||||
|
// 指定 IoC 容器创建实例的作用域,这里注册为请求作用域,这样能取到 ctx
|
||||||
|
Scope(ScopeEnum.Prototype)(target);
|
||||||
|
|
||||||
|
// 调用一下 Provide 装饰器,这样用户的 class 可以省略写 @Provide() 装饰器了
|
||||||
|
Provide()(target);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PLUGIN_INPUT_KEY = "decorator:plugin:input";
|
||||||
|
|
||||||
|
export function TaskInput(input?: TaskInputDefine): PropertyDecorator {
|
||||||
|
return (target, propertyKey) => {
|
||||||
|
attachPropertyDataToClass(PLUGIN_INPUT_KEY, { input }, target, propertyKey, propertyKey as string);
|
||||||
|
|
||||||
|
attachClassMetadata(
|
||||||
|
PLUGIN_CLASS_KEY,
|
||||||
|
{
|
||||||
|
inputs: {
|
||||||
|
[propertyKey]: input,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
target
|
||||||
|
);
|
||||||
|
};
|
||||||
|
//
|
||||||
|
// return createCustomPropertyDecorator(CLASS_PROPS_KEY, {
|
||||||
|
// input,
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 装饰器内部的唯一 id
|
||||||
|
export const PLUGIN_OUTPUT_KEY = "decorator:plugin:output";
|
||||||
|
export function TaskOutput(output?: TaskOutputDefine): PropertyDecorator {
|
||||||
|
return (target, propertyKey) => {
|
||||||
|
attachPropertyDataToClass(PLUGIN_OUTPUT_KEY, { output }, target, propertyKey, propertyKey as string);
|
||||||
|
|
||||||
|
attachClassMetadata(
|
||||||
|
PLUGIN_CLASS_KEY,
|
||||||
|
{
|
||||||
|
outputs: {
|
||||||
|
[propertyKey]: output,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
target
|
||||||
|
);
|
||||||
|
};
|
||||||
|
//
|
||||||
|
// return createCustomPropertyDecorator(CLASS_PROPS_KEY, {
|
||||||
|
// input,
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AutowireProp = {
|
||||||
|
name?: string;
|
||||||
|
};
|
||||||
|
export const PLUGIN_AUTOWIRE_KEY = "decorator:plugin:autowire";
|
||||||
|
|
||||||
|
export function Autowire(props?: AutowireProp): PropertyDecorator {
|
||||||
|
return (target, propertyKey) => {
|
||||||
|
attachPropertyDataToClass(
|
||||||
|
PLUGIN_AUTOWIRE_KEY,
|
||||||
|
{
|
||||||
|
autowire: {
|
||||||
|
[propertyKey]: props,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
target,
|
||||||
|
propertyKey,
|
||||||
|
propertyKey as string
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function registerPlugins() {
|
||||||
|
const modules = listModule(PLUGIN_CLASS_KEY);
|
||||||
|
for (const mod of modules) {
|
||||||
|
console.log("mod", mod);
|
||||||
|
const define: PluginDefine = getClassMetadata(PLUGIN_CLASS_KEY, mod);
|
||||||
|
console.log("define", define);
|
||||||
|
const inputs = listPropertyDataFromClass(PLUGIN_INPUT_KEY, mod);
|
||||||
|
console.log("inputs", inputs);
|
||||||
|
for (const input of inputs) {
|
||||||
|
define.inputs = {};
|
||||||
|
_.merge(define.inputs, input.inputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
const outputs = listPropertyDataFromClass(PLUGIN_OUTPUT_KEY, mod);
|
||||||
|
console.log("outputs", outputs);
|
||||||
|
for (const output of outputs) {
|
||||||
|
define.outputs = {};
|
||||||
|
_.merge(define.outputs, output.outputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
const autowire = listPropertyDataFromClass(PLUGIN_AUTOWIRE_KEY, mod);
|
||||||
|
console.log("autowire", autowire);
|
||||||
|
for (const auto of autowire) {
|
||||||
|
define.autowire = {};
|
||||||
|
_.merge(define.autowire, auto.autowire);
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginRegistry.register(define.name, {
|
||||||
|
define,
|
||||||
|
target: mod,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,3 @@
|
||||||
export * from "./api";
|
export * from "./api";
|
||||||
export * from "./registry";
|
export * from "./registry";
|
||||||
export * from "./abstract-plugin";
|
export * from "./decorator";
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { Registry } from "../registry";
|
import { Registry } from "../registry";
|
||||||
import { AbstractPlugin } from "./abstract-plugin";
|
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
export const pluginRegistry = new Registry<typeof AbstractPlugin>();
|
export const pluginRegistry = new Registry();
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { ILogger } from "@midwayjs/logger";
|
||||||
|
import { ITaskPlugin } from "../api";
|
||||||
|
import { Autowire, IsTaskPlugin, TaskInput } from "../decorator";
|
||||||
|
|
||||||
|
@IsTaskPlugin({
|
||||||
|
name: "EchoPlugin",
|
||||||
|
title: "测试插件",
|
||||||
|
desc: "test",
|
||||||
|
})
|
||||||
|
export class EchoPlugin implements ITaskPlugin {
|
||||||
|
@TaskInput({
|
||||||
|
title: "测试属性",
|
||||||
|
component: {
|
||||||
|
name: "text",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
test?: string;
|
||||||
|
|
||||||
|
@Autowire()
|
||||||
|
// @ts-ignore
|
||||||
|
logger: ILogger;
|
||||||
|
|
||||||
|
async execute(): Promise<void> {
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,36 +4,16 @@ export type Registrable = {
|
||||||
desc?: string;
|
desc?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export abstract class AbstractRegistrable<T extends Registrable> {
|
export type RegistryItem = {
|
||||||
// @ts-ignore
|
define: Registrable;
|
||||||
define: T;
|
target: any;
|
||||||
|
};
|
||||||
getDefine(): T {
|
export class Registry {
|
||||||
return this.define;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export class Registry<T extends typeof AbstractRegistrable> {
|
|
||||||
storage: {
|
storage: {
|
||||||
[key: string]: T;
|
[key: string]: RegistryItem;
|
||||||
} = {};
|
} = {};
|
||||||
|
|
||||||
install(pluginClass: T) {
|
register(key: string, value: RegistryItem) {
|
||||||
if (pluginClass == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// @ts-ignore
|
|
||||||
const plugin = new pluginClass();
|
|
||||||
delete plugin.define;
|
|
||||||
const define = plugin.getDefine();
|
|
||||||
let defineName = define.name;
|
|
||||||
if (defineName == null) {
|
|
||||||
defineName = plugin.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.register(defineName, pluginClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
register(key: string, value: T) {
|
|
||||||
if (!key || value == null) {
|
if (!key || value == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -68,12 +48,10 @@ export class Registry<T extends typeof AbstractRegistrable> {
|
||||||
}
|
}
|
||||||
|
|
||||||
getDefine(key: string) {
|
getDefine(key: string) {
|
||||||
const PluginClass = this.storage[key];
|
const item = this.storage[key];
|
||||||
if (!PluginClass) {
|
if (!item) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// @ts-ignore
|
return item.define;
|
||||||
const plugin = new PluginClass();
|
|
||||||
return plugin.define;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { AbstractPlugin, IsTask, RunStrategy, TaskInput, TaskOutput, TaskPlugin, utils } from "@certd/pipeline";
|
import { AbstractPlugin, IsTask, RunStrategy, TaskInput, TaskOutput, TaskPlugin, utils } from "@certd/pipeline";
|
||||||
import Core from "@alicloud/pop-core";
|
// @ts-ignore
|
||||||
|
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";
|
||||||
|
|
||||||
const ROAClient = Core.ROAClient;
|
|
||||||
@IsTask(() => {
|
@IsTask(() => {
|
||||||
return {
|
return {
|
||||||
name: "DeployCertToAliyunAckIngress",
|
name: "DeployCertToAliyunAckIngress",
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import { AbstractPlugin, IsTask, RunStrategy, TaskInput, TaskOutput, TaskPlugin } from "@certd/pipeline";
|
import { AbstractPlugin, IsTask, RunStrategy, TaskInput, TaskOutput, TaskPlugin } from "@certd/pipeline";
|
||||||
import dayjs from "dayjs";
|
|
||||||
import Core from "@alicloud/pop-core";
|
import Core from "@alicloud/pop-core";
|
||||||
import RPCClient from "@alicloud/pop-core";
|
|
||||||
import { AliyunAccess } from "../../access";
|
import { AliyunAccess } from "../../access";
|
||||||
import { appendTimeSuffix, checkRet, ZoneOptions } from "../../utils";
|
import { appendTimeSuffix, checkRet, ZoneOptions } from "../../utils";
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,25 @@
|
||||||
import { AbstractAccess, IsAccess } from "@certd/pipeline";
|
import { IsAccess, IsAccessInput } from "@certd/pipeline";
|
||||||
|
|
||||||
@IsAccess({
|
@IsAccess({
|
||||||
name: "dnspod",
|
name: "dnspod",
|
||||||
title: "dnspod",
|
title: "dnspod",
|
||||||
desc: "腾讯云的域名解析接口已迁移到dnspod",
|
desc: "腾讯云的域名解析接口已迁移到dnspod",
|
||||||
input: {
|
|
||||||
id: {
|
|
||||||
title: "账户id",
|
|
||||||
component: {
|
|
||||||
placeholder: "dnspod接口账户id",
|
|
||||||
},
|
|
||||||
rules: [{ required: true, message: "该项必填" }],
|
|
||||||
},
|
|
||||||
token: {
|
|
||||||
title: "token",
|
|
||||||
component: {
|
|
||||||
placeholder: "开放接口token",
|
|
||||||
},
|
|
||||||
rules: [{ required: true, message: "该项必填" }],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
export class DnspodAccess extends AbstractAccess {
|
export class DnspodAccess {
|
||||||
|
@IsAccessInput({
|
||||||
|
title: "token",
|
||||||
|
component: {
|
||||||
|
placeholder: "开放接口token",
|
||||||
|
},
|
||||||
|
rules: [{ required: true, message: "该项必填" }],
|
||||||
|
})
|
||||||
token = "";
|
token = "";
|
||||||
|
@IsAccessInput({
|
||||||
|
title: "账户id",
|
||||||
|
component: {
|
||||||
|
placeholder: "dnspod接口账户id",
|
||||||
|
},
|
||||||
|
rules: [{ required: true, message: "该项必填" }],
|
||||||
|
})
|
||||||
id = "";
|
id = "";
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,24 @@
|
||||||
import { AbstractAccess, IsAccess } from "@certd/pipeline";
|
import { IsAccess, IsAccessInput } from "@certd/pipeline";
|
||||||
|
|
||||||
@IsAccess({
|
@IsAccess({
|
||||||
name: "tencent",
|
name: "tencent",
|
||||||
title: "腾讯云",
|
title: "腾讯云",
|
||||||
input: {
|
|
||||||
secretId: {
|
|
||||||
title: "secretId",
|
|
||||||
component: {
|
|
||||||
placeholder: "secretId",
|
|
||||||
},
|
|
||||||
rules: [{ required: true, message: "该项必填" }],
|
|
||||||
},
|
|
||||||
secretKey: {
|
|
||||||
title: "secretKey",
|
|
||||||
component: {
|
|
||||||
placeholder: "secretKey",
|
|
||||||
},
|
|
||||||
rules: [{ required: true, message: "该项必填" }],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
export class TencentAccess extends AbstractAccess {
|
export class TencentAccess {
|
||||||
|
@IsAccessInput({
|
||||||
|
title: "secretId",
|
||||||
|
component: {
|
||||||
|
placeholder: "secretId",
|
||||||
|
},
|
||||||
|
rules: [{ required: true, message: "该项必填" }],
|
||||||
|
})
|
||||||
secretId = "";
|
secretId = "";
|
||||||
|
@IsAccessInput({
|
||||||
|
title: "secretKey",
|
||||||
|
component: {
|
||||||
|
placeholder: "secretKey",
|
||||||
|
},
|
||||||
|
rules: [{ required: true, message: "该项必填" }],
|
||||||
|
})
|
||||||
secretKey = "";
|
secretKey = "";
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import { AbstractDnsProvider, CreateRecordOptions, IDnsProvider, IsDnsProvider, RemoveRecordOptions } from "@certd/pipeline";
|
import { CreateRecordOptions, HttpClient, IDnsProvider, IsDnsProvider, RemoveRecordOptions } from "@certd/pipeline";
|
||||||
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",
|
||||||
|
@ -8,11 +10,17 @@ import { DnspodAccess } from "../access";
|
||||||
desc: "腾讯云的域名解析接口已迁移到dnspod",
|
desc: "腾讯云的域名解析接口已迁移到dnspod",
|
||||||
accessType: "dnspod",
|
accessType: "dnspod",
|
||||||
})
|
})
|
||||||
export class DnspodDnsProvider extends AbstractDnsProvider implements IDnsProvider {
|
export class DnspodDnsProvider implements IDnsProvider {
|
||||||
|
@Inject()
|
||||||
|
http!: HttpClient;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
access!: DnspodAccess;
|
||||||
|
@Inject()
|
||||||
|
logger!: ILogger;
|
||||||
|
|
||||||
loginToken: any;
|
loginToken: any;
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
async onInit() {
|
async onInit() {
|
||||||
const access: DnspodAccess = this.access as DnspodAccess;
|
const access: DnspodAccess = this.access as DnspodAccess;
|
||||||
this.loginToken = access.id + "," + access.token;
|
this.loginToken = access.id + "," + access.token;
|
||||||
|
|
|
@ -1,56 +1,64 @@
|
||||||
import { AbstractPlugin, IsTask, RunStrategy, TaskInput, TaskOutput, TaskPlugin } from "@certd/pipeline";
|
import { IAccessService, IsTaskPlugin, RunStrategy, TaskInput, ITaskPlugin } 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 { ILogger } from "@midwayjs/core";
|
||||||
|
|
||||||
@IsTask(() => {
|
@IsTaskPlugin({
|
||||||
return {
|
name: "DeployCertToTencentCDN",
|
||||||
name: "DeployCertToTencentCDN",
|
title: "部署到腾讯云CDN",
|
||||||
title: "部署到腾讯云CDN",
|
default: {
|
||||||
input: {
|
strategy: {
|
||||||
domainName: {
|
runStrategy: RunStrategy.SkipWhenSucceed,
|
||||||
title: "cdn加速域名",
|
|
||||||
rules: [{ required: true, message: "该项必填" }],
|
|
||||||
},
|
|
||||||
certName: {
|
|
||||||
title: "证书名称",
|
|
||||||
helper: "证书上传后将以此参数作为名称前缀",
|
|
||||||
},
|
|
||||||
cert: {
|
|
||||||
title: "域名证书",
|
|
||||||
helper: "请选择前置任务输出的域名证书",
|
|
||||||
component: {
|
|
||||||
name: "pi-output-selector",
|
|
||||||
from: "CertApply",
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
accessId: {
|
|
||||||
title: "Access提供者",
|
|
||||||
helper: "access 授权",
|
|
||||||
component: {
|
|
||||||
name: "pi-access-selector",
|
|
||||||
type: "tencent",
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
default: {
|
},
|
||||||
strategy: {
|
|
||||||
runStrategy: RunStrategy.SkipWhenSucceed,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
output: {},
|
|
||||||
};
|
|
||||||
})
|
})
|
||||||
export class DeployToCdnPlugin extends AbstractPlugin implements TaskPlugin {
|
export class DeployToCdnPlugin implements ITaskPlugin {
|
||||||
async execute(input: TaskInput): Promise<TaskOutput> {
|
@TaskInput({
|
||||||
const { cert, accessId } = input;
|
title: "域名证书",
|
||||||
const accessProvider: TencentAccess = (await this.accessService.getById(accessId)) as TencentAccess;
|
helper: "请选择前置任务输出的域名证书",
|
||||||
const client = this.getClient(accessProvider);
|
component: {
|
||||||
const params = this.buildParams(input, cert);
|
name: "pi-output-selector",
|
||||||
await this.doRequest(client, params);
|
from: "CertApply",
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
cert!: any;
|
||||||
|
|
||||||
return {};
|
@TaskInput({
|
||||||
|
title: "Access提供者",
|
||||||
|
helper: "access 授权",
|
||||||
|
component: {
|
||||||
|
name: "pi-access-selector",
|
||||||
|
type: "tencent",
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
accessId!: string;
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: "证书名称",
|
||||||
|
helper: "证书上传后将以此参数作为名称前缀",
|
||||||
|
})
|
||||||
|
certName!: string;
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: "cdn加速域名",
|
||||||
|
rules: [{ required: true, message: "该项必填" }],
|
||||||
|
})
|
||||||
|
domainName!: string;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
accessService!: IAccessService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
logger!: ILogger;
|
||||||
|
|
||||||
|
async execute(): Promise<void> {
|
||||||
|
const accessProvider: TencentAccess = (await this.accessService.getById(this.accessId)) as TencentAccess;
|
||||||
|
const client = this.getClient(accessProvider);
|
||||||
|
const params = this.buildParams();
|
||||||
|
await this.doRequest(client, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
getClient(accessProvider: TencentAccess) {
|
getClient(accessProvider: TencentAccess) {
|
||||||
|
@ -72,17 +80,16 @@ export class DeployToCdnPlugin extends AbstractPlugin implements TaskPlugin {
|
||||||
return new CdnClient(clientConfig);
|
return new CdnClient(clientConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
buildParams(props: TaskInput, cert: any) {
|
buildParams() {
|
||||||
const { domainName } = props;
|
|
||||||
return {
|
return {
|
||||||
Https: {
|
Https: {
|
||||||
Switch: "on",
|
Switch: "on",
|
||||||
CertInfo: {
|
CertInfo: {
|
||||||
Certificate: cert.crt,
|
Certificate: this.cert.crt,
|
||||||
PrivateKey: cert.key,
|
PrivateKey: this.cert.key,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Domain: domainName,
|
Domain: this.domainName,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,99 +1,115 @@
|
||||||
import { AbstractPlugin, IsTask, RunStrategy, TaskInput, TaskOutput, TaskPlugin, utils } from "@certd/pipeline";
|
import { IAccessService, IsTaskPlugin, ITaskPlugin, 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";
|
||||||
|
import { ILogger } from "@midwayjs/core";
|
||||||
|
|
||||||
@IsTask(() => {
|
@IsTaskPlugin({
|
||||||
return {
|
name: "DeployCertToTencentCLB",
|
||||||
name: "DeployCertToTencentCLB",
|
title: "部署到腾讯云CLB",
|
||||||
title: "部署到腾讯云CLB",
|
desc: "暂时只支持单向认证证书,暂时只支持通用负载均衡",
|
||||||
desc: "暂时只支持单向认证证书,暂时只支持通用负载均衡",
|
default: {
|
||||||
input: {
|
strategy: {
|
||||||
region: {
|
runStrategy: RunStrategy.SkipWhenSucceed,
|
||||||
title: "大区",
|
|
||||||
value: "ap-guangzhou",
|
|
||||||
component: {
|
|
||||||
name: "a-select",
|
|
||||||
options: [{ value: "ap-guangzhou" }],
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
domain: {
|
|
||||||
title: "域名",
|
|
||||||
required: true,
|
|
||||||
helper: "要更新的支持https的负载均衡的域名",
|
|
||||||
},
|
|
||||||
loadBalancerId: {
|
|
||||||
title: "负载均衡ID",
|
|
||||||
helper: "如果没有配置,则根据域名匹配负载均衡下的监听器(根据域名匹配时暂时只支持前100个)",
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
listenerId: {
|
|
||||||
title: "监听器ID",
|
|
||||||
helper: "如果没有配置,则根据域名或负载均衡id匹配监听器",
|
|
||||||
},
|
|
||||||
certName: {
|
|
||||||
title: "证书名称前缀",
|
|
||||||
},
|
|
||||||
accessId: {
|
|
||||||
title: "Access提供者",
|
|
||||||
helper: "access授权",
|
|
||||||
component: {
|
|
||||||
name: "pi-access-selector",
|
|
||||||
type: "tencent",
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
cert: {
|
|
||||||
title: "域名证书",
|
|
||||||
helper: "请选择前置任务输出的域名证书",
|
|
||||||
component: {
|
|
||||||
name: "pi-output-selector",
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
default: {
|
},
|
||||||
strategy: {
|
|
||||||
runStrategy: RunStrategy.SkipWhenSucceed,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
output: {},
|
|
||||||
};
|
|
||||||
})
|
})
|
||||||
export class DeployToClbPlugin extends AbstractPlugin implements TaskPlugin {
|
export class DeployToClbPlugin implements ITaskPlugin {
|
||||||
async execute(input: TaskInput): Promise<TaskOutput> {
|
@TaskInput({
|
||||||
const { accessId, region, domain } = input;
|
title: "大区",
|
||||||
const accessProvider = (await this.accessService.getById(accessId)) as TencentAccess;
|
value: "ap-guangzhou",
|
||||||
const client = this.getClient(accessProvider, region);
|
component: {
|
||||||
|
name: "a-select",
|
||||||
|
options: [{ value: "ap-guangzhou" }],
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
region!: string;
|
||||||
|
|
||||||
const lastCertId = await this.getCertIdFromProps(client, input);
|
@TaskInput({
|
||||||
if (!domain) {
|
title: "证书名称前缀",
|
||||||
await this.updateListener(client, input);
|
})
|
||||||
|
certName!: string;
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: "负载均衡ID",
|
||||||
|
helper: "如果没有配置,则根据域名匹配负载均衡下的监听器(根据域名匹配时暂时只支持前100个)",
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
loadBalancerId!: string;
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: "监听器ID",
|
||||||
|
helper: "如果没有配置,则根据域名或负载均衡id匹配监听器",
|
||||||
|
})
|
||||||
|
listenerId!: string;
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: "域名",
|
||||||
|
required: true,
|
||||||
|
helper: "要更新的支持https的负载均衡的域名",
|
||||||
|
})
|
||||||
|
domain!: string;
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: "域名证书",
|
||||||
|
helper: "请选择前置任务输出的域名证书",
|
||||||
|
component: {
|
||||||
|
name: "pi-output-selector",
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
cert!: any;
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: "Access提供者",
|
||||||
|
helper: "access授权",
|
||||||
|
component: {
|
||||||
|
name: "pi-access-selector",
|
||||||
|
type: "tencent",
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
accessId!: string;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
accessService!: IAccessService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
logger!: ILogger;
|
||||||
|
|
||||||
|
async execute(): Promise<void> {
|
||||||
|
const accessProvider = (await this.accessService.getById(this.accessId)) as TencentAccess;
|
||||||
|
const client = this.getClient(accessProvider, this.region);
|
||||||
|
|
||||||
|
const lastCertId = await this.getCertIdFromProps(client);
|
||||||
|
if (!this.domain) {
|
||||||
|
await this.updateListener(client);
|
||||||
} else {
|
} else {
|
||||||
await this.updateByDomainAttr(client, input);
|
await this.updateByDomainAttr(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await utils.sleep(2000);
|
await utils.sleep(2000);
|
||||||
let newCertId = await this.getCertIdFromProps(client, input);
|
let newCertId = await this.getCertIdFromProps(client);
|
||||||
if ((lastCertId && newCertId === lastCertId) || (!lastCertId && !newCertId)) {
|
if ((lastCertId && newCertId === lastCertId) || (!lastCertId && !newCertId)) {
|
||||||
await utils.sleep(2000);
|
await utils.sleep(2000);
|
||||||
newCertId = await this.getCertIdFromProps(client, input);
|
newCertId = await this.getCertIdFromProps(client);
|
||||||
}
|
}
|
||||||
if (newCertId === lastCertId) {
|
if (newCertId === lastCertId) {
|
||||||
return {};
|
return;
|
||||||
}
|
}
|
||||||
this.logger.info("腾讯云证书ID:", newCertId);
|
this.logger.info("腾讯云证书ID:", newCertId);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logger.warn("查询腾讯云证书失败", e);
|
this.logger.warn("查询腾讯云证书失败", e);
|
||||||
}
|
}
|
||||||
return {};
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getCertIdFromProps(client: any, input: TaskInput) {
|
async getCertIdFromProps(client: any) {
|
||||||
const listenerRet = await this.getListenerList(client, input.loadBalancerId, [input.listenerId]);
|
const listenerRet = await this.getListenerList(client, this.loadBalancerId, [this.listenerId]);
|
||||||
return this.getCertIdFromListener(listenerRet[0], input.domain);
|
return this.getCertIdFromListener(listenerRet[0], this.domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
getCertIdFromListener(listener: any, domain: string) {
|
getCertIdFromListener(listener: any, domain: string) {
|
||||||
|
@ -115,28 +131,28 @@ export class DeployToClbPlugin extends AbstractPlugin implements TaskPlugin {
|
||||||
return certId;
|
return certId;
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateListener(client: any, props: TaskInput) {
|
async updateListener(client: any) {
|
||||||
const params = this.buildProps(props);
|
const params = this.buildProps();
|
||||||
const ret = await client.ModifyListener(params);
|
const ret = await client.ModifyListener(params);
|
||||||
this.checkRet(ret);
|
this.checkRet(ret);
|
||||||
this.logger.info("设置腾讯云CLB证书成功:", ret.RequestId, "->loadBalancerId:", props.loadBalancerId, "listenerId", props.listenerId);
|
this.logger.info("设置腾讯云CLB证书成功:", ret.RequestId, "->loadBalancerId:", this.loadBalancerId, "listenerId", this.listenerId);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateByDomainAttr(client: any, props: TaskInput) {
|
async updateByDomainAttr(client: any) {
|
||||||
const params: any = this.buildProps(props);
|
const params: any = this.buildProps();
|
||||||
params.Domain = props.domain;
|
params.Domain = this.domain;
|
||||||
const ret = await client.ModifyDomainAttributes(params);
|
const ret = await client.ModifyDomainAttributes(params);
|
||||||
this.checkRet(ret);
|
this.checkRet(ret);
|
||||||
this.logger.info(
|
this.logger.info(
|
||||||
"设置腾讯云CLB证书(sni)成功:",
|
"设置腾讯云CLB证书(sni)成功:",
|
||||||
ret.RequestId,
|
ret.RequestId,
|
||||||
"->loadBalancerId:",
|
"->loadBalancerId:",
|
||||||
props.loadBalancerId,
|
this.loadBalancerId,
|
||||||
"listenerId",
|
"listenerId",
|
||||||
props.listenerId,
|
this.listenerId,
|
||||||
"domain:",
|
"domain:",
|
||||||
props.domain
|
this.domain
|
||||||
);
|
);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -146,26 +162,25 @@ export class DeployToClbPlugin extends AbstractPlugin implements TaskPlugin {
|
||||||
}
|
}
|
||||||
return name + "-" + dayjs().format("YYYYMMDD-HHmmss");
|
return name + "-" + dayjs().format("YYYYMMDD-HHmmss");
|
||||||
}
|
}
|
||||||
buildProps(props: TaskInput) {
|
buildProps() {
|
||||||
const { certName, cert } = props;
|
|
||||||
return {
|
return {
|
||||||
Certificate: {
|
Certificate: {
|
||||||
SSLMode: "UNIDIRECTIONAL", // 单向认证
|
SSLMode: "UNIDIRECTIONAL", // 单向认证
|
||||||
CertName: this.appendTimeSuffix(certName || cert.domain),
|
CertName: this.appendTimeSuffix(this.certName || this.cert.domain),
|
||||||
CertKey: cert.key,
|
CertKey: this.cert.key,
|
||||||
CertContent: cert.crt,
|
CertContent: this.cert.crt,
|
||||||
},
|
},
|
||||||
LoadBalancerId: props.loadBalancerId,
|
LoadBalancerId: this.loadBalancerId,
|
||||||
ListenerId: props.listenerId,
|
ListenerId: this.listenerId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async getCLBList(client: any, props: TaskInput) {
|
async getCLBList(client: any) {
|
||||||
const params = {
|
const params = {
|
||||||
Limit: 100, // 最大暂时只支持100个,暂时没做翻页
|
Limit: 100, // 最大暂时只支持100个,暂时没做翻页
|
||||||
OrderBy: "CreateTime",
|
OrderBy: "CreateTime",
|
||||||
OrderType: 0,
|
OrderType: 0,
|
||||||
...props.DescribeLoadBalancers,
|
// ...this.DescribeLoadBalancers,
|
||||||
};
|
};
|
||||||
const ret = await client.DescribeLoadBalancers(params);
|
const ret = await client.DescribeLoadBalancers(params);
|
||||||
this.checkRet(ret);
|
this.checkRet(ret);
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit a59e66a11d667dc99b425b14cd6fde7468a8725b
|
Subproject commit 70f3fd5359ac27b6dbd452f30fbf6630d4c9fa26
|
Loading…
Reference in New Issue