mirror of
https://github.com/cool-team-official/cool-admin-midway.git
synced 2026-01-03 00:38:12 +00:00
270 lines
6.3 KiB
TypeScript
270 lines
6.3 KiB
TypeScript
import { App, Inject, Provide } from '@midwayjs/decorator';
|
||
import { BaseService, CoolCommException } from '@cool-midway/core';
|
||
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||
import { Equal, Not, Repository } from 'typeorm';
|
||
import { PluginInfoEntity } from '../entity/info';
|
||
import {
|
||
Config,
|
||
IMidwayApplication,
|
||
IMidwayContext,
|
||
InjectClient,
|
||
} from '@midwayjs/core';
|
||
import * as _ from 'lodash';
|
||
import { PluginInfo } from '../interface';
|
||
import { PLUGIN_CACHE_KEY, PluginCenterService } from './center';
|
||
import { CachingFactory, MidwayCache } from '@midwayjs/cache-manager';
|
||
|
||
/**
|
||
* 插件信息
|
||
*/
|
||
@Provide()
|
||
export class PluginService extends BaseService {
|
||
@InjectEntityModel(PluginInfoEntity)
|
||
pluginInfoEntity: Repository<PluginInfoEntity>;
|
||
|
||
@Inject()
|
||
ctx: IMidwayContext;
|
||
|
||
@App()
|
||
app: IMidwayApplication;
|
||
|
||
@Inject()
|
||
pluginCenterService: PluginCenterService;
|
||
|
||
@Config('module.plugin.hooks')
|
||
hooksConfig;
|
||
|
||
@InjectClient(CachingFactory, 'default')
|
||
midwayCache: MidwayCache;
|
||
|
||
/**
|
||
* 初始化
|
||
* @param data
|
||
* @param type
|
||
*/
|
||
async modifyAfter() {
|
||
await this.reInit();
|
||
}
|
||
|
||
/**
|
||
* 需要重新初始化插件
|
||
*/
|
||
async reInit() {
|
||
await this.midwayCache.set(PLUGIN_CACHE_KEY, []);
|
||
await this.pluginCenterService.init();
|
||
}
|
||
|
||
/**
|
||
* 删除不经过回收站
|
||
* @param ids
|
||
*/
|
||
async delete(ids: any) {
|
||
await this.pluginInfoEntity.delete(ids);
|
||
await this.reInit();
|
||
}
|
||
|
||
/**
|
||
* 更新
|
||
* @param param
|
||
*/
|
||
async update(param: any) {
|
||
const old = await this.pluginInfoEntity.findOneBy({ id: param.id });
|
||
// 启用插件,禁用同名插件
|
||
if (old.hook && param.status == 1 && old.status != param.status) {
|
||
await this.pluginInfoEntity.update(
|
||
{ hook: old.hook, status: 1, id: Not(old.id) },
|
||
{ status: 0 }
|
||
);
|
||
}
|
||
await super.update(param);
|
||
}
|
||
|
||
/**
|
||
* 获得插件配置
|
||
* @param key
|
||
*/
|
||
async getConfig(key: string) {
|
||
return this.pluginCenterService.pluginInfos.get(key)?.config;
|
||
}
|
||
|
||
/**
|
||
* 调用插件
|
||
* @param key 插件key
|
||
* @param method 方法
|
||
* @param params 参数
|
||
* @returns
|
||
*/
|
||
async invoke(key: string, method: string, ...params) {
|
||
// 实例
|
||
const instance = await this.getInstance(key);
|
||
return await instance[method](...params);
|
||
}
|
||
|
||
/**
|
||
* 获得插件实例
|
||
* @param key
|
||
* @returns
|
||
*/
|
||
async getInstance(key: string) {
|
||
await this.checkStatus(key);
|
||
await this.pluginCenterService.init();
|
||
const instance = new (await this.pluginCenterService.plugins.get(key))();
|
||
await instance.init(
|
||
this.pluginCenterService.pluginInfos.get(key),
|
||
this.ctx,
|
||
this.app
|
||
);
|
||
return instance;
|
||
}
|
||
|
||
/**
|
||
* 检查状态
|
||
* @param key
|
||
*/
|
||
async checkStatus(key: string) {
|
||
if (Object.keys(this.hooksConfig).includes(key)) {
|
||
return;
|
||
}
|
||
const info = await this.pluginInfoEntity
|
||
.createQueryBuilder('a')
|
||
.where({ status: 1, keyName: Equal(key) })
|
||
.getOne();
|
||
if (!info) {
|
||
throw new CoolCommException('插件不存在或已禁用');
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查
|
||
* @param filePath
|
||
*/
|
||
async check(filePath: string) {
|
||
let data;
|
||
try {
|
||
data = await this.data(filePath);
|
||
} catch (e) {
|
||
return {
|
||
type: 0,
|
||
message: `插件信息不完整,请检查${data.errorData}`,
|
||
};
|
||
}
|
||
const check = await this.pluginInfoEntity.findOneBy({
|
||
keyName: data.pluginJson.key,
|
||
});
|
||
if (check && !check.hook) {
|
||
return {
|
||
type: 1,
|
||
message: '插件已存在,继续安装将覆盖',
|
||
};
|
||
}
|
||
if (check && check.hook && check.status == 1) {
|
||
return {
|
||
type: 2,
|
||
message:
|
||
'已存在同名Hook插件,你可以继续安装,但是多个相同的Hook插件只能同时开启一个',
|
||
};
|
||
}
|
||
return {
|
||
type: 3,
|
||
message: '检查通过',
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 获得插件数据
|
||
* @param filePath
|
||
*/
|
||
async data(filePath: string): Promise<{
|
||
pluginJson: any;
|
||
readme: string;
|
||
logo: string;
|
||
content: string;
|
||
errorData: string;
|
||
}> {
|
||
// const plugin = await download(encodeURI(url));
|
||
const decompress = require('decompress');
|
||
const files = await decompress(filePath);
|
||
let errorData;
|
||
let pluginJson: PluginInfo, readme: string, logo: string, content: string;
|
||
try {
|
||
errorData = 'plugin.json';
|
||
pluginJson = JSON.parse(
|
||
_.find(files, { path: 'plugin.json', type: 'file' }).data.toString()
|
||
);
|
||
errorData = 'readme';
|
||
readme = _.find(files, {
|
||
path: pluginJson.readme,
|
||
type: 'file',
|
||
}).data.toString();
|
||
errorData = 'logo';
|
||
logo = _.find(files, {
|
||
path: pluginJson.logo,
|
||
type: 'file',
|
||
}).data.toString('base64');
|
||
content = _.find(files, {
|
||
path: 'src/index.js',
|
||
type: 'file',
|
||
}).data.toString();
|
||
} catch (e) {
|
||
throw new CoolCommException('插件信息不完整');
|
||
}
|
||
return {
|
||
pluginJson,
|
||
readme,
|
||
logo,
|
||
content,
|
||
errorData,
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 安装插件
|
||
* @param file 文件
|
||
* @param force 是否强制安装
|
||
*/
|
||
async install(filePath: string, force = false) {
|
||
const forceBool = typeof force === 'string' ? force === 'true' : force;
|
||
const checkResult = await this.check(filePath);
|
||
if (checkResult.type != 3 && !forceBool) {
|
||
return checkResult;
|
||
}
|
||
const { pluginJson, readme, logo, content } = await this.data(filePath);
|
||
const check = await this.pluginInfoEntity.findOneBy({
|
||
keyName: pluginJson.key,
|
||
});
|
||
const data = {
|
||
name: pluginJson.name,
|
||
keyName: pluginJson.key,
|
||
version: pluginJson.version,
|
||
author: pluginJson.author,
|
||
hook: pluginJson.hook,
|
||
readme,
|
||
logo,
|
||
content: {
|
||
type: 'comm',
|
||
data: content,
|
||
},
|
||
description: pluginJson.description,
|
||
pluginJson,
|
||
config: pluginJson.config,
|
||
status: 1,
|
||
} as PluginInfoEntity;
|
||
// 存在同名插件,更新,保留配置
|
||
if (check) {
|
||
await this.pluginInfoEntity.update(check.id, {
|
||
...data,
|
||
status: check.status,
|
||
config: {
|
||
...pluginJson.config,
|
||
...check.config,
|
||
},
|
||
});
|
||
} else {
|
||
// 全新安装
|
||
await this.pluginInfoEntity.insert(data);
|
||
}
|
||
// 初始化插件
|
||
await this.reInit();
|
||
}
|
||
}
|