import {
|
App,
|
IMidwayApplication,
|
Inject,
|
InjectClient,
|
Scope,
|
Provide,
|
ScopeEnum,
|
} from '@midwayjs/core';
|
import * as fs from 'fs';
|
import * as path from 'path';
|
import { PluginInfoEntity } from '../entity/info';
|
import { InjectEntityModel } from '@midwayjs/typeorm';
|
import { Repository } from 'typeorm';
|
import { PluginInfo } from '../interface';
|
import * as _ from 'lodash';
|
import { CachingFactory, MidwayCache } from '@midwayjs/cache-manager';
|
import { CoolEventManager } from '@cool-midway/core';
|
import { PluginService } from './info';
|
|
export const PLUGIN_CACHE_KEY = 'plugin:init';
|
|
export const EVENT_PLUGIN_READY = 'EVENT_PLUGIN_READY';
|
|
/**
|
* 插件中心
|
*/
|
@Provide()
|
@Scope(ScopeEnum.Singleton)
|
export class PluginCenterService {
|
// 插件列表
|
plugins: Map<string, any> = new Map();
|
|
// 插件配置
|
pluginInfos: Map<string, PluginInfo> = new Map();
|
|
@App()
|
app: IMidwayApplication;
|
|
@InjectEntityModel(PluginInfoEntity)
|
pluginInfoEntity: Repository<PluginInfoEntity>;
|
|
@InjectClient(CachingFactory, 'default')
|
midwayCache: MidwayCache;
|
|
@Inject()
|
coolEventManager: CoolEventManager;
|
|
@Inject()
|
pluginService: PluginService;
|
|
/**
|
* 初始化
|
* @returns
|
*/
|
async init() {
|
this.plugins.clear();
|
await this.initHooks();
|
await this.initPlugin();
|
this.coolEventManager.emit(EVENT_PLUGIN_READY);
|
}
|
|
/**
|
* 初始化一个
|
* @param keyName key名
|
*/
|
async initOne(keyName: string) {
|
await this.initPlugin({
|
keyName,
|
});
|
this.coolEventManager.emit(EVENT_PLUGIN_READY, keyName);
|
}
|
|
/**
|
* 移除插件
|
* @param keyName
|
* @param isHook
|
*/
|
async remove(keyName: string, isHook = false) {
|
this.plugins.delete(keyName);
|
this.pluginInfos.delete(keyName);
|
if (isHook) {
|
await this.initHooks();
|
}
|
}
|
|
/**
|
* 注册插件
|
* @param key 唯一标识
|
* @param cls 类
|
* @param pluginInfo 插件信息
|
*/
|
async register(key: string, cls: any, pluginInfo?: PluginInfo) {
|
// 单例插件
|
if (pluginInfo?.singleton) {
|
const instance = new cls();
|
await instance.init(this.pluginInfos.get(key), null, this.app, {
|
cache: this.midwayCache,
|
pluginService: this.pluginService,
|
});
|
this.plugins.set(key, instance);
|
} else {
|
// 普通插件
|
this.plugins.set(key, cls);
|
}
|
}
|
|
/**
|
* 初始化钩子
|
*/
|
async initHooks() {
|
const hooksPath = path.join(
|
this.app.getBaseDir(),
|
'modules',
|
'plugin',
|
'hooks'
|
);
|
for (const key of fs.readdirSync(hooksPath)) {
|
const stat = fs.statSync(path.join(hooksPath, key));
|
if (!stat.isDirectory()) {
|
continue;
|
}
|
const { Plugin } = await import(path.join(hooksPath, key, 'index'));
|
await this.register(key, Plugin);
|
this.pluginInfos.set(key, {
|
name: key,
|
config: this.app.getConfig('module.plugin.hooks.' + key),
|
});
|
}
|
}
|
|
/**
|
* 初始化插件
|
* @param condition 插件条件
|
*/
|
async initPlugin(condition?: {
|
hook?: string;
|
id?: number;
|
keyName?: string;
|
}) {
|
let find: any = { status: 1 };
|
if (condition) {
|
find = {
|
...find,
|
...condition,
|
};
|
}
|
const plugins = await this.pluginInfoEntity.find({
|
where: find,
|
select: [
|
'id',
|
'name',
|
'description',
|
'keyName',
|
'hook',
|
'version',
|
'pluginJson',
|
'config',
|
],
|
});
|
for (const plugin of plugins) {
|
const data = await this.pluginService.getData(plugin.keyName);
|
if (!data) {
|
continue;
|
}
|
const instance = await this.getInstance(data.content.data);
|
const pluginInfo = {
|
...plugin.pluginJson,
|
config: this.getConfig(plugin.config),
|
};
|
if (plugin.hook) {
|
this.pluginInfos.set(plugin.hook, pluginInfo);
|
await this.register(plugin.hook, instance, pluginInfo);
|
} else {
|
this.pluginInfos.set(plugin.keyName, pluginInfo);
|
await this.register(plugin.keyName, instance, pluginInfo);
|
}
|
}
|
}
|
|
/**
|
* 获得配置
|
* @param config
|
* @returns
|
*/
|
private getConfig(config: any) {
|
// 处理配置为字符串的情况
|
if (typeof config === 'string') {
|
try {
|
config = JSON.parse(config);
|
} catch (e) {
|
return {};
|
}
|
}
|
// 如果配置为空或非对象类型,则返回空对象
|
if (!config || typeof config !== 'object') {
|
return {};
|
}
|
const env = this.app.getEnv();
|
let isMulti = false;
|
for (const key in config) {
|
if (key.includes('@')) {
|
isMulti = true;
|
break;
|
}
|
}
|
return isMulti ? config[`@${env}`] : config;
|
}
|
|
/**
|
* 获得实例
|
* @param content
|
* @returns
|
*/
|
async getInstance(content: string) {
|
let _instance;
|
const script = `
|
${content}
|
_instance = Plugin;
|
`;
|
eval(script);
|
return _instance;
|
}
|
}
|