diff --git a/packages/core/pipeline/src/core/executor.ts b/packages/core/pipeline/src/core/executor.ts index 8f4f9864..0e1b0ded 100644 --- a/packages/core/pipeline/src/core/executor.ts +++ b/packages/core/pipeline/src/core/executor.ts @@ -1,6 +1,6 @@ import { ConcurrencyStrategy, NotificationWhen, Pipeline, ResultType, Runnable, RunStrategy, Stage, Step, Task, ResultError } from "../dt/index.js"; import { RunHistory, RunnableCollection } from "./run-history.js"; -import { AbstractTaskPlugin, PluginDefine, pluginRegistry, TaskInstanceContext, UserInfo } from "../plugin/index.js"; +import { AbstractTaskPlugin, ITaskPlugin, PluginDefine, pluginRegistry, TaskInstanceContext, UserInfo } from "../plugin/index.js"; import { ContextFactory, IContext } from "./context.js"; import { IStorage } from "./storage.js"; import { createAxiosService, hashUtils, HttpRequestConfig, ILogger, logger, utils } from "@certd/basic"; @@ -261,6 +261,7 @@ export class Executor { const resList: ResultType[] = []; for (const step of task.steps) { step.runnableType = "step"; + // @ts-ignore const res: ResultType = await this.runWithHistory(step, "step", async () => { return await this.runStep(step); }); @@ -276,8 +277,22 @@ export class Executor { //执行任务 const plugin: RegistryItem = pluginRegistry.get(step.type); - // @ts-ignore - const instance: ITaskPlugin = new plugin.target(); + //@ts-ignore + let instance: ITaskPlugin = null; + try { + //@ts-ignore + if (plugin.target.define) { + //@ts-ignore + instance = new plugin.target(); + } else { + //@ts-ignore + instance = await plugin.target(); + } + } catch (e: any) { + currentLogger.error(`实例化插件失败:${e.message}`); + throw new Error(`实例化插件失败`, e); + } + // @ts-ignore const define: PluginDefine = plugin.define; const pluginName = define.name; diff --git a/packages/core/pipeline/src/registry/registry.ts b/packages/core/pipeline/src/registry/registry.ts index eeaeaf89..529cec26 100644 --- a/packages/core/pipeline/src/registry/registry.ts +++ b/packages/core/pipeline/src/registry/registry.ts @@ -8,10 +8,10 @@ export type Registrable = { deprecated?: string; order?: number; }; - +export type TargetGetter = () => Promise; export type RegistryItem = { define: Registrable; - target: T; + target: T | TargetGetter; }; export type OnRegisterContext = { diff --git a/packages/ui/certd-client/src/views/sys/plugin/demo/access.yaml b/packages/ui/certd-client/src/views/sys/plugin/demo/access.yaml index 13afa946..122b461f 100644 --- a/packages/ui/certd-client/src/views/sys/plugin/demo/access.yaml +++ b/packages/ui/certd-client/src/views/sys/plugin/demo/access.yaml @@ -6,7 +6,8 @@ title: 阿里云授权 desc: 阿里云授权 icon: fa-cloud type: access - +dependentPlugins: + - greper/AliyunSdk metadata: dependencies: - "@alicloud/pop-core": "^1.7.10" diff --git a/packages/ui/certd-server/db/migration/v10021__plugin.sql b/packages/ui/certd-server/db/migration/v10021__plugin.sql new file mode 100644 index 00000000..f54b4107 --- /dev/null +++ b/packages/ui/certd-server/db/migration/v10021__plugin.sql @@ -0,0 +1,3 @@ +ALTER TABLE pi_plugin ADD COLUMN "pluginType" varchar(100); +ALTER TABLE pi_plugin ADD COLUMN "metadata" varchar(40960); +ALTER TABLE pi_plugin ADD COLUMN "author" varchar(100); diff --git a/packages/ui/certd-server/src/modules/auto/auto-b-load-plugins.ts b/packages/ui/certd-server/src/modules/auto/auto-b-load-plugins.ts new file mode 100644 index 00000000..a952004a --- /dev/null +++ b/packages/ui/certd-server/src/modules/auto/auto-b-load-plugins.ts @@ -0,0 +1,19 @@ +import { Autoload, Init, Inject, Scope, ScopeEnum } from "@midwayjs/core"; +import { logger } from "@certd/basic"; +import { PluginService } from "../plugin/service/plugin-service.js"; + +@Autoload() +@Scope(ScopeEnum.Request, { allowDowngrade: true }) +export class AutoBLoadPlugins { + @Inject() + pluginService: PluginService; + + + @Init() + async init() { + logger.info('加载插件开始'); + await this.pluginService.registerFromDb() + logger.info('加载插件完成'); + + } +} diff --git a/packages/ui/certd-server/src/modules/plugin/service/plugin-service.ts b/packages/ui/certd-server/src/modules/plugin/service/plugin-service.ts index 0847de0d..62257e93 100644 --- a/packages/ui/certd-server/src/modules/plugin/service/plugin-service.ts +++ b/packages/ui/certd-server/src/modules/plugin/service/plugin-service.ts @@ -1,11 +1,14 @@ -import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core'; -import { BaseService, PageReq } from '@certd/lib-server'; -import { PluginEntity } from '../entity/plugin.js'; -import { InjectEntityModel } from '@midwayjs/typeorm'; -import { Repository } from 'typeorm'; -import { isComm } from '@certd/plus-core'; -import { BuiltInPluginService } from '../../pipeline/service/builtin-plugin-service.js'; -import { merge } from 'lodash-es'; +import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core"; +import { BaseService, PageReq } from "@certd/lib-server"; +import { PluginEntity } from "../entity/plugin.js"; +import { InjectEntityModel } from "@midwayjs/typeorm"; +import { Repository } from "typeorm"; +import { isComm } from "@certd/plus-core"; +import { BuiltInPluginService } from "../../pipeline/service/builtin-plugin-service.js"; +import { merge } from "lodash-es"; +import { accessRegistry, pluginRegistry } from "@certd/pipeline"; +import { dnsProviderRegistry } from "@certd/plugin-cert"; +import { logger } from "@certd/basic"; @Provide() @Scope(ScopeEnum.Request, { allowDowngrade: true }) @@ -28,7 +31,7 @@ export class PluginService extends BaseService { records: builtInList, total: builtInList.length, offset: 0, - limit: 99999, + limit: 99999 }; } @@ -46,9 +49,9 @@ export class PluginService extends BaseService { } const list = await this.list({ query: { - type: 'builtIn', - disabled: true, - }, + type: "builtIn", + disabled: true + } }); const disabledNames = list.map(it => it.name); for (const key in groups) { @@ -60,6 +63,7 @@ export class PluginService extends BaseService { } return groups; } + async getEnabledBuiltInList(): Promise { const builtInList = this.builtInPluginService.getList(); if (!isComm()) { @@ -68,9 +72,9 @@ export class PluginService extends BaseService { const list = await this.list({ query: { - type: 'builtIn', - disabled: true, - }, + type: "builtIn", + disabled: true + } }); const disabledNames = list.map(it => it.name); @@ -81,8 +85,8 @@ export class PluginService extends BaseService { const builtInList = this.builtInPluginService.getList(); const list = await this.list({ query: { - type: 'builtIn', - }, + type: "builtIn" + } }); const records: PluginEntity[] = []; @@ -96,10 +100,10 @@ export class PluginService extends BaseService { merge(record, { name: item.name, title: item.title, - type: 'builtIn', + type: "builtIn", icon: item.icon, desc: item.desc, - group: item.group, + group: item.group }); records.push(record); } @@ -109,7 +113,7 @@ export class PluginService extends BaseService { async setDisabled(opts: { id?: number; name?: string; type: string; disabled: boolean }) { const { id, name, type, disabled } = opts; if (!type) { - throw new Error('参数错误: type 不能为空'); + throw new Error("参数错误: type 不能为空"); } if (id > 0) { //update @@ -117,7 +121,7 @@ export class PluginService extends BaseService { return; } - if (name && type === 'builtIn') { + if (name && type === "builtIn") { const pluginEntity = new PluginEntity(); pluginEntity.name = name; pluginEntity.type = type; @@ -125,10 +129,71 @@ export class PluginService extends BaseService { await this.repository.save(pluginEntity); return; } - throw new Error('参数错误: id 和 name 必须有一个'); + throw new Error("参数错误: id 和 name 必须有一个"); } async getDefineByType(type: string) { return this.builtInPluginService.getByType(type); } + + async getPluginTarget(pluginName: string){ + //获取插件类实例对象 + let author = undefined; + let name = ''; + if(pluginName.includes('/')){ + const arr = pluginName.split('/'); + author = arr[0]; + name = arr[1]; + }else { + name = pluginName; + } + const info = await this.find({ + where: { + name: name, + author: author + } + }); + if (info.length > 0) { + const plugin = info[0]; + const AsyncFunction = Object.getPrototypeOf(async () => {}).constructor; + const getPluginClass = new AsyncFunction(plugin.content); + return await getPluginClass(); + } + } + /** + * 从数据库加载插件 + */ + async registerFromDb() { + const res = await this.list({ + buildQuery: ((bq) => { + bq.andWhere( "type != :type", { + type: 'builtIn' + }) + }) + }); + + + + for (const item of res) { + const pluginName = item.author ? item.author +"/"+ item.name : item.name; + let registry = null + if(item.pluginType === 'access'){ + registry = accessRegistry; + }else if (item.pluginType === 'plugin'){ + registry = pluginRegistry; + }else if (item.pluginType === 'dnsProvider'){ + registry = dnsProviderRegistry + }else { + logger.warn(`插件${pluginName}类型错误:${item.pluginType}`) + continue + } + + registry.register(pluginName, { + define:item, + target: ()=>{ + return this.getPluginTarget(pluginName); + } + }); + } + } }