diff --git a/packages/core/pipeline/package.json b/packages/core/pipeline/package.json index 7aa06a3f6..092475746 100644 --- a/packages/core/pipeline/package.json +++ b/packages/core/pipeline/package.json @@ -12,22 +12,29 @@ }, "dependencies": { "@certd/acme-client": "^0.3.0", + "axios": "^0.21.1", "dayjs": "^1.11.6", "lodash": "^4.17.21", - "node-forge": "^0.10.0", "log4js": "^6.3.0", - "axios": "^0.21.1", + "node-forge": "^0.10.0", "qs": "^6.9.4" }, "devDependencies": { - "@types/lodash": "^4.14.186", - "vue-tsc": "^0.38.9", "@alicloud/cs20151215": "^3.0.3", "@alicloud/openapi-client": "^0.4.0", "@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/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/lodash": "^4.14.186", + "@types/lodash-es": "^4.17.6", "@types/mocha": "^10.0.0", "@types/node-forge": "^1.3.0", "@typescript-eslint/eslint-plugin": "^5.38.1", @@ -41,6 +48,7 @@ "mocha": "^10.1.0", "ts-node": "^10.9.1", "typescript": "^4.8.4", - "vite": "^3.1.0" + "vite": "^3.1.0", + "vue-tsc": "^0.38.9" } } diff --git a/packages/core/pipeline/src/access/abstract-access.ts b/packages/core/pipeline/src/access/abstract-access.ts deleted file mode 100644 index 60816e313..000000000 --- a/packages/core/pipeline/src/access/abstract-access.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { AbstractRegistrable } from "../registry"; -import { AccessDefine } from "./api"; - -export abstract class AbstractAccess extends AbstractRegistrable {} diff --git a/packages/core/pipeline/src/access/api.ts b/packages/core/pipeline/src/access/api.ts index 48356c0bf..d5d9aaddc 100644 --- a/packages/core/pipeline/src/access/api.ts +++ b/packages/core/pipeline/src/access/api.ts @@ -3,22 +3,15 @@ import { accessRegistry } from "./registry"; import { FormItemProps } from "../d.ts"; import { AbstractAccess } from "./abstract-access"; -export type AccessInput = FormItemProps & { +export type AccessInputDefine = FormItemProps & { title: string; required?: boolean; }; export type AccessDefine = Registrable & { - input: { - [key: string]: AccessInput; + inputs?: { + [key: string]: AccessInputDefine; }; }; -export function IsAccess(define: AccessDefine) { - return function (target: any) { - target.prototype.define = define; - accessRegistry.install(target); - }; -} - export interface IAccessService { getById(id: any): Promise; } diff --git a/packages/core/pipeline/src/access/decorator.ts b/packages/core/pipeline/src/access/decorator.ts new file mode 100644 index 000000000..d4c94e4c4 --- /dev/null +++ b/packages/core/pipeline/src/access/decorator.ts @@ -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, + }); + } +} diff --git a/packages/core/pipeline/src/access/index.ts b/packages/core/pipeline/src/access/index.ts index b51a95f11..c97ebdb45 100644 --- a/packages/core/pipeline/src/access/index.ts +++ b/packages/core/pipeline/src/access/index.ts @@ -1,3 +1,3 @@ export * from "./api"; -export * from "./abstract-access"; export * from "./registry"; +export * from "./decorator"; diff --git a/packages/core/pipeline/src/access/registry.ts b/packages/core/pipeline/src/access/registry.ts index 2f4ad7b10..19e4fc7f7 100644 --- a/packages/core/pipeline/src/access/registry.ts +++ b/packages/core/pipeline/src/access/registry.ts @@ -1,5 +1,4 @@ import { Registry } from "../registry"; -import { AbstractAccess } from "./abstract-access"; // @ts-ignore -export const accessRegistry = new Registry(); +export const accessRegistry = new Registry(); diff --git a/packages/core/pipeline/src/context/index.ts b/packages/core/pipeline/src/context/index.ts new file mode 100644 index 000000000..0c865d211 --- /dev/null +++ b/packages/core/pipeline/src/context/index.ts @@ -0,0 +1,6 @@ +import { AxiosInstance } from "axios"; +import { IContext } from "../core"; + +export type HttpClient = AxiosInstance; +export type UserContext = IContext; +export type PipelineContext = IContext; diff --git a/packages/core/pipeline/src/core/executor.ts b/packages/core/pipeline/src/core/executor.ts index cf86fa1a1..3f4863cff 100644 --- a/packages/core/pipeline/src/core/executor.ts +++ b/packages/core/pipeline/src/core/executor.ts @@ -1,13 +1,14 @@ import { ConcurrencyStrategy, Pipeline, ResultType, Runnable, RunStrategy, Stage, Step, Task } from "../d.ts"; import _ from "lodash"; import { RunHistory } from "./run-history"; -import { pluginRegistry, TaskPlugin } from "../plugin"; +import { pluginRegistry, ITaskPlugin, PluginDefine } from "../plugin"; import { ContextFactory, IContext } from "./context"; import { IStorage } from "./storage"; import { logger } from "../utils/util.log"; import { Logger } from "log4js"; import { request } from "../utils/util.request"; import { IAccessService } from "../access"; +import { Registrable, RegistryItem } from "../registry"; export class Executor { userId: any; pipeline: Pipeline; @@ -134,40 +135,38 @@ export class Executor { 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 - delete taskPlugin.define; - const define = taskPlugin.getDefine(); + const instance = new plugin.target(); + // @ts-ignore + const define: PluginDefine = plugin.define; //从outputContext读取输入参数 - _.forEach(define.input, (item, key) => { + _.forEach(define.inputs, (item, key) => { if (item.component?.name === "pi-output-selector") { const contextKey = step.input[key]; if (contextKey != null) { 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 - _.forEach(define.output, (item, key) => { + _.forEach(define.outputs, (item, key) => { const contextKey = `step.${step.id}.${key}`; this.runtime.context[contextKey] = res[key]; }); } - - private async getPlugin(type: string, logger: Logger): Promise { - 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; - } } diff --git a/packages/core/pipeline/src/dns-provider/api.ts b/packages/core/pipeline/src/dns-provider/api.ts index ed3e511f3..16e7ea014 100644 --- a/packages/core/pipeline/src/dns-provider/api.ts +++ b/packages/core/pipeline/src/dns-provider/api.ts @@ -1,6 +1,4 @@ import { Registrable } from "../registry"; -import { dnsProviderRegistry } from "./registry"; -import { AbstractDnsProvider } from "./abstract-dns-provider"; export type DnsProviderDefine = Registrable & { accessType: string; @@ -16,19 +14,6 @@ export type RemoveRecordOptions = CreateRecordOptions & { }; export interface IDnsProvider { - getDefine(): DnsProviderDefine; createRecord(options: CreateRecordOptions): Promise; - removeRecord(options: RemoveRecordOptions): Promise; } - -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); - }; -} diff --git a/packages/core/pipeline/src/dns-provider/decorator.ts b/packages/core/pipeline/src/dns-provider/decorator.ts new file mode 100644 index 000000000..3a14de0e7 --- /dev/null +++ b/packages/core/pipeline/src/dns-provider/decorator.ts @@ -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, + }); + } +} diff --git a/packages/core/pipeline/src/dns-provider/index.ts b/packages/core/pipeline/src/dns-provider/index.ts index e89e52928..c97ebdb45 100644 --- a/packages/core/pipeline/src/dns-provider/index.ts +++ b/packages/core/pipeline/src/dns-provider/index.ts @@ -1,3 +1,3 @@ export * from "./api"; export * from "./registry"; -export * from "./abstract-dns-provider"; +export * from "./decorator"; diff --git a/packages/core/pipeline/src/dns-provider/registry.ts b/packages/core/pipeline/src/dns-provider/registry.ts index 4b6c462d1..ff6cabd1e 100644 --- a/packages/core/pipeline/src/dns-provider/registry.ts +++ b/packages/core/pipeline/src/dns-provider/registry.ts @@ -1,5 +1,4 @@ import { Registry } from "../registry"; -import { AbstractDnsProvider } from "./abstract-dns-provider"; // @ts-ignore -export const dnsProviderRegistry = new Registry(); +export const dnsProviderRegistry = new Registry(); diff --git a/packages/core/pipeline/src/index.ts b/packages/core/pipeline/src/index.ts index d6d2e459d..ac8f3704e 100644 --- a/packages/core/pipeline/src/index.ts +++ b/packages/core/pipeline/src/index.ts @@ -5,3 +5,6 @@ export * from "./registry"; export * from "./dns-provider"; export * from "./plugin"; export * from "./utils"; +export * from "./decorator"; +export * from "./midway"; +export * from "./context"; diff --git a/packages/core/pipeline/src/midway/configuration.ts b/packages/core/pipeline/src/midway/configuration.ts new file mode 100644 index 000000000..961f17a37 --- /dev/null +++ b/packages/core/pipeline/src/midway/configuration.ts @@ -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; + // }); + // } +} diff --git a/packages/core/pipeline/src/midway/index.ts b/packages/core/pipeline/src/midway/index.ts new file mode 100644 index 000000000..6b704bf55 --- /dev/null +++ b/packages/core/pipeline/src/midway/index.ts @@ -0,0 +1,5 @@ +// src/index.ts +export { PipelineConfiguration } from "./configuration"; +// export * from './controller/user'; +// export * from './controller/api'; +// export * from './service/user'; diff --git a/packages/core/pipeline/src/midway/install.ts b/packages/core/pipeline/src/midway/install.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/core/pipeline/src/plugin/api.ts b/packages/core/pipeline/src/plugin/api.ts index 6d9ef5633..4853b312b 100644 --- a/packages/core/pipeline/src/plugin/api.ts +++ b/packages/core/pipeline/src/plugin/api.ts @@ -1,13 +1,6 @@ import { Registrable } from "../registry"; import { pluginRegistry } from "./registry"; import { FormItemProps } from "../d.ts"; -export type TaskInput = { - [key: string]: any; -}; - -export type TaskOutput = { - [key: string]: any; -}; export enum ContextScope { global, @@ -28,17 +21,19 @@ export type TaskOutputDefine = { export type TaskInputDefine = FormItemProps; export type PluginDefine = Registrable & { - input: { + default?: any; + inputs?: { [key: string]: TaskInputDefine; }; - output: { + outputs?: { [key: string]: TaskOutputDefine; }; + + autowire?: any; }; -export interface TaskPlugin { - getDefine(): PluginDefine; - execute(input: TaskInput): Promise; +export interface ITaskPlugin { + execute(): Promise; } export type OutputVO = { @@ -46,15 +41,3 @@ export type OutputVO = { title: string; 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); - }; -} diff --git a/packages/core/pipeline/src/plugin/decorator.ts b/packages/core/pipeline/src/plugin/decorator.ts new file mode 100644 index 000000000..bc433fa92 --- /dev/null +++ b/packages/core/pipeline/src/plugin/decorator.ts @@ -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, + }); + } +} diff --git a/packages/core/pipeline/src/plugin/index.ts b/packages/core/pipeline/src/plugin/index.ts index 2d9b5e5a1..c97ebdb45 100644 --- a/packages/core/pipeline/src/plugin/index.ts +++ b/packages/core/pipeline/src/plugin/index.ts @@ -1,3 +1,3 @@ export * from "./api"; export * from "./registry"; -export * from "./abstract-plugin"; +export * from "./decorator"; diff --git a/packages/core/pipeline/src/plugin/registry.ts b/packages/core/pipeline/src/plugin/registry.ts index 68894221b..91fcb766f 100644 --- a/packages/core/pipeline/src/plugin/registry.ts +++ b/packages/core/pipeline/src/plugin/registry.ts @@ -1,5 +1,4 @@ import { Registry } from "../registry"; -import { AbstractPlugin } from "./abstract-plugin"; // @ts-ignore -export const pluginRegistry = new Registry(); +export const pluginRegistry = new Registry(); diff --git a/packages/core/pipeline/src/plugin/test/echo-plugin.ts b/packages/core/pipeline/src/plugin/test/echo-plugin.ts new file mode 100644 index 000000000..bf2ae03de --- /dev/null +++ b/packages/core/pipeline/src/plugin/test/echo-plugin.ts @@ -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 { + return Promise.resolve(undefined); + } +} diff --git a/packages/core/pipeline/src/registry/registry.ts b/packages/core/pipeline/src/registry/registry.ts index aea7a7782..42fb2758c 100644 --- a/packages/core/pipeline/src/registry/registry.ts +++ b/packages/core/pipeline/src/registry/registry.ts @@ -4,36 +4,16 @@ export type Registrable = { desc?: string; }; -export abstract class AbstractRegistrable { - // @ts-ignore - define: T; - - getDefine(): T { - return this.define; - } -} -export class Registry { +export type RegistryItem = { + define: Registrable; + target: any; +}; +export class Registry { storage: { - [key: string]: T; + [key: string]: RegistryItem; } = {}; - install(pluginClass: T) { - 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) { + register(key: string, value: RegistryItem) { if (!key || value == null) { return; } @@ -68,12 +48,10 @@ export class Registry { } getDefine(key: string) { - const PluginClass = this.storage[key]; - if (!PluginClass) { + const item = this.storage[key]; + if (!item) { return; } - // @ts-ignore - const plugin = new PluginClass(); - return plugin.define; + return item.define; } } diff --git a/packages/plugins/plugin-aliyun/src/plugin/deploy-to-ack-ingress/index.ts b/packages/plugins/plugin-aliyun/src/plugin/deploy-to-ack-ingress/index.ts index 56166cb36..c02d0a9e5 100644 --- a/packages/plugins/plugin-aliyun/src/plugin/deploy-to-ack-ingress/index.ts +++ b/packages/plugins/plugin-aliyun/src/plugin/deploy-to-ack-ingress/index.ts @@ -1,10 +1,10 @@ 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 { K8sClient } from "@certd/plugin-util"; import { appendTimeSuffix } from "../../utils"; -const ROAClient = Core.ROAClient; @IsTask(() => { return { name: "DeployCertToAliyunAckIngress", diff --git a/packages/plugins/plugin-aliyun/src/plugin/upload-to-aliyun/index.ts b/packages/plugins/plugin-aliyun/src/plugin/upload-to-aliyun/index.ts index e5362f713..70179177c 100644 --- a/packages/plugins/plugin-aliyun/src/plugin/upload-to-aliyun/index.ts +++ b/packages/plugins/plugin-aliyun/src/plugin/upload-to-aliyun/index.ts @@ -1,7 +1,5 @@ import { AbstractPlugin, IsTask, RunStrategy, TaskInput, TaskOutput, TaskPlugin } from "@certd/pipeline"; -import dayjs from "dayjs"; import Core from "@alicloud/pop-core"; -import RPCClient from "@alicloud/pop-core"; import { AliyunAccess } from "../../access"; import { appendTimeSuffix, checkRet, ZoneOptions } from "../../utils"; diff --git a/packages/plugins/plugin-tencent/src/access/dnspod-access.ts b/packages/plugins/plugin-tencent/src/access/dnspod-access.ts index d3973aab3..144adf518 100644 --- a/packages/plugins/plugin-tencent/src/access/dnspod-access.ts +++ b/packages/plugins/plugin-tencent/src/access/dnspod-access.ts @@ -1,27 +1,25 @@ -import { AbstractAccess, IsAccess } from "@certd/pipeline"; +import { IsAccess, IsAccessInput } from "@certd/pipeline"; @IsAccess({ name: "dnspod", title: "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 = ""; + @IsAccessInput({ + title: "账户id", + component: { + placeholder: "dnspod接口账户id", + }, + rules: [{ required: true, message: "该项必填" }], + }) id = ""; } diff --git a/packages/plugins/plugin-tencent/src/access/tencent-access.ts b/packages/plugins/plugin-tencent/src/access/tencent-access.ts index a4f4757ff..6df676e1d 100644 --- a/packages/plugins/plugin-tencent/src/access/tencent-access.ts +++ b/packages/plugins/plugin-tencent/src/access/tencent-access.ts @@ -1,26 +1,24 @@ -import { AbstractAccess, IsAccess } from "@certd/pipeline"; +import { IsAccess, IsAccessInput } from "@certd/pipeline"; @IsAccess({ name: "tencent", 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 = ""; + @IsAccessInput({ + title: "secretKey", + component: { + placeholder: "secretKey", + }, + rules: [{ required: true, message: "该项必填" }], + }) secretKey = ""; } diff --git a/packages/plugins/plugin-tencent/src/dns-provider/dnspod-dns-provider.ts b/packages/plugins/plugin-tencent/src/dns-provider/dnspod-dns-provider.ts index 1495e9124..a6efba69e 100644 --- a/packages/plugins/plugin-tencent/src/dns-provider/dnspod-dns-provider.ts +++ b/packages/plugins/plugin-tencent/src/dns-provider/dnspod-dns-provider.ts @@ -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 { DnspodAccess } from "../access"; +import { Inject } from "@midwayjs/decorator"; +import { ILogger } from "@midwayjs/core"; @IsDnsProvider({ name: "dnspod", @@ -8,11 +10,17 @@ import { DnspodAccess } from "../access"; desc: "腾讯云的域名解析接口已迁移到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; - constructor() { - super(); - } + async onInit() { const access: DnspodAccess = this.access as DnspodAccess; this.loginToken = access.id + "," + access.token; diff --git a/packages/plugins/plugin-tencent/src/plugin/deploy-to-cdn/index.ts b/packages/plugins/plugin-tencent/src/plugin/deploy-to-cdn/index.ts index bc09524a1..b5f59235d 100644 --- a/packages/plugins/plugin-tencent/src/plugin/deploy-to-cdn/index.ts +++ b/packages/plugins/plugin-tencent/src/plugin/deploy-to-cdn/index.ts @@ -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 { TencentAccess } from "../../access"; +import { Inject } from "@midwayjs/decorator"; +import { ILogger } from "@midwayjs/core"; -@IsTask(() => { - return { - name: "DeployCertToTencentCDN", - title: "部署到腾讯云CDN", - input: { - domainName: { - 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, - }, +@IsTaskPlugin({ + name: "DeployCertToTencentCDN", + title: "部署到腾讯云CDN", + default: { + strategy: { + runStrategy: RunStrategy.SkipWhenSucceed, }, - default: { - strategy: { - runStrategy: RunStrategy.SkipWhenSucceed, - }, - }, - output: {}, - }; + }, }) -export class DeployToCdnPlugin extends AbstractPlugin implements TaskPlugin { - async execute(input: TaskInput): Promise { - const { cert, accessId } = input; - const accessProvider: TencentAccess = (await this.accessService.getById(accessId)) as TencentAccess; - const client = this.getClient(accessProvider); - const params = this.buildParams(input, cert); - await this.doRequest(client, params); +export class DeployToCdnPlugin implements ITaskPlugin { + @TaskInput({ + title: "域名证书", + helper: "请选择前置任务输出的域名证书", + component: { + name: "pi-output-selector", + 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 { + 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) { @@ -72,17 +80,16 @@ export class DeployToCdnPlugin extends AbstractPlugin implements TaskPlugin { return new CdnClient(clientConfig); } - buildParams(props: TaskInput, cert: any) { - const { domainName } = props; + buildParams() { return { Https: { Switch: "on", CertInfo: { - Certificate: cert.crt, - PrivateKey: cert.key, + Certificate: this.cert.crt, + PrivateKey: this.cert.key, }, }, - Domain: domainName, + Domain: this.domainName, }; } diff --git a/packages/plugins/plugin-tencent/src/plugin/deploy-to-clb/index.ts b/packages/plugins/plugin-tencent/src/plugin/deploy-to-clb/index.ts index 12527dd9e..7fb2688c5 100644 --- a/packages/plugins/plugin-tencent/src/plugin/deploy-to-clb/index.ts +++ b/packages/plugins/plugin-tencent/src/plugin/deploy-to-clb/index.ts @@ -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 { TencentAccess } from "../../access"; import dayjs from "dayjs"; +import { Inject } from "@midwayjs/decorator"; +import { ILogger } from "@midwayjs/core"; -@IsTask(() => { - return { - name: "DeployCertToTencentCLB", - title: "部署到腾讯云CLB", - desc: "暂时只支持单向认证证书,暂时只支持通用负载均衡", - input: { - region: { - 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, - }, +@IsTaskPlugin({ + name: "DeployCertToTencentCLB", + title: "部署到腾讯云CLB", + desc: "暂时只支持单向认证证书,暂时只支持通用负载均衡", + default: { + strategy: { + runStrategy: RunStrategy.SkipWhenSucceed, }, - default: { - strategy: { - runStrategy: RunStrategy.SkipWhenSucceed, - }, - }, - output: {}, - }; + }, }) -export class DeployToClbPlugin extends AbstractPlugin implements TaskPlugin { - async execute(input: TaskInput): Promise { - const { accessId, region, domain } = input; - const accessProvider = (await this.accessService.getById(accessId)) as TencentAccess; - const client = this.getClient(accessProvider, region); +export class DeployToClbPlugin implements ITaskPlugin { + @TaskInput({ + title: "大区", + value: "ap-guangzhou", + component: { + name: "a-select", + options: [{ value: "ap-guangzhou" }], + }, + required: true, + }) + region!: string; - const lastCertId = await this.getCertIdFromProps(client, input); - if (!domain) { - await this.updateListener(client, input); + @TaskInput({ + title: "证书名称前缀", + }) + 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 { + 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 { - await this.updateByDomainAttr(client, input); + await this.updateByDomainAttr(client); } try { await utils.sleep(2000); - let newCertId = await this.getCertIdFromProps(client, input); + let newCertId = await this.getCertIdFromProps(client); if ((lastCertId && newCertId === lastCertId) || (!lastCertId && !newCertId)) { await utils.sleep(2000); - newCertId = await this.getCertIdFromProps(client, input); + newCertId = await this.getCertIdFromProps(client); } if (newCertId === lastCertId) { - return {}; + return; } this.logger.info("腾讯云证书ID:", newCertId); } catch (e) { this.logger.warn("查询腾讯云证书失败", e); } - return {}; + return; } - async getCertIdFromProps(client: any, input: TaskInput) { - const listenerRet = await this.getListenerList(client, input.loadBalancerId, [input.listenerId]); - return this.getCertIdFromListener(listenerRet[0], input.domain); + async getCertIdFromProps(client: any) { + const listenerRet = await this.getListenerList(client, this.loadBalancerId, [this.listenerId]); + return this.getCertIdFromListener(listenerRet[0], this.domain); } getCertIdFromListener(listener: any, domain: string) { @@ -115,28 +131,28 @@ export class DeployToClbPlugin extends AbstractPlugin implements TaskPlugin { return certId; } - async updateListener(client: any, props: TaskInput) { - const params = this.buildProps(props); + async updateListener(client: any) { + const params = this.buildProps(); const ret = await client.ModifyListener(params); 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; } - async updateByDomainAttr(client: any, props: TaskInput) { - const params: any = this.buildProps(props); - params.Domain = props.domain; + async updateByDomainAttr(client: any) { + const params: any = this.buildProps(); + params.Domain = this.domain; const ret = await client.ModifyDomainAttributes(params); this.checkRet(ret); this.logger.info( "设置腾讯云CLB证书(sni)成功:", ret.RequestId, "->loadBalancerId:", - props.loadBalancerId, + this.loadBalancerId, "listenerId", - props.listenerId, + this.listenerId, "domain:", - props.domain + this.domain ); return ret; } @@ -146,26 +162,25 @@ export class DeployToClbPlugin extends AbstractPlugin implements TaskPlugin { } return name + "-" + dayjs().format("YYYYMMDD-HHmmss"); } - buildProps(props: TaskInput) { - const { certName, cert } = props; + buildProps() { return { Certificate: { SSLMode: "UNIDIRECTIONAL", // 单向认证 - CertName: this.appendTimeSuffix(certName || cert.domain), - CertKey: cert.key, - CertContent: cert.crt, + CertName: this.appendTimeSuffix(this.certName || this.cert.domain), + CertKey: this.cert.key, + CertContent: this.cert.crt, }, - LoadBalancerId: props.loadBalancerId, - ListenerId: props.listenerId, + LoadBalancerId: this.loadBalancerId, + ListenerId: this.listenerId, }; } - async getCLBList(client: any, props: TaskInput) { + async getCLBList(client: any) { const params = { Limit: 100, // 最大暂时只支持100个,暂时没做翻页 OrderBy: "CreateTime", OrderType: 0, - ...props.DescribeLoadBalancers, + // ...this.DescribeLoadBalancers, }; const ret = await client.DescribeLoadBalancers(params); this.checkRet(ret); diff --git a/packages/server/certd-server b/packages/server/certd-server index a59e66a11..70f3fd535 160000 --- a/packages/server/certd-server +++ b/packages/server/certd-server @@ -1 +1 @@ -Subproject commit a59e66a11d667dc99b425b14cd6fde7468a8725b +Subproject commit 70f3fd5359ac27b6dbd452f30fbf6630d4c9fa26