add plugin-cli

This commit is contained in:
COOL 2025-03-08 15:27:57 +08:00
parent 68b9d86500
commit 665148ead7
17 changed files with 1847 additions and 0 deletions

2
plugin-cli/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
node_modules/
dist/

3
plugin-cli/.npmignore Normal file
View File

@ -0,0 +1,3 @@
node_modules/
src/
tsconfig.json

27
plugin-cli/README.md Normal file
View File

@ -0,0 +1,27 @@
### COOL-ADMIN
cool-admin一个很酷的后台权限管理系统开源免费模块化、插件化、极速开发CRUD方便快速构建迭代后台管理系统
大数据、微服务、AI编码快速开发
### 技术栈
- 后端node.js midway.js koa.js mysql typescript
- 前端vue.js element-plus jsx pinia vue-router
### 官网
[https://cool-js.com](https://cool-js.com)
### 演示地址
[https://show.cool-admin.com](https://show.cool-admin.com)
- 账户admin
- 密码123456
### 项目地址
[https://github.com/cool-team-official/cool-admin-midway](https://github.com/cool-team-official/cool-admin-midway)

36
plugin-cli/package.json Normal file
View File

@ -0,0 +1,36 @@
{
"name": "@cool-midway/plugin-cli",
"version": "8.0.0",
"description": "cool-admin midway plugin",
"main": "dist/index.js",
"scripts": {
"build": "tsc"
},
"keywords": [
"cool",
"cool-admin",
"cooljs"
],
"repository": {
"type": "git",
"url": "https://cool-js.com"
},
"bin": {
"cool-plugin-cli": "scripts/index.js"
},
"author": "COOL",
"readme": "README.md",
"license": "MIT",
"dependencies": {
"@cool-midway/cache-manager-fs-hash": "^7.0.0",
"@midwayjs/cache-manager": "^3.20.3",
"@midwayjs/core": "^3.20.3",
"archiver": "^7.0.1",
"esbuild": "^0.25.0",
"esbuild-plugin-copy": "^2.1.1"
},
"devDependencies": {
"@types/node": "^22.13.9",
"typescript": "^5.8.2"
}
}

1223
plugin-cli/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,47 @@
console.log("cool-plugin-cli run build");
const esbuild = require('esbuild');
const copyPlugin = require('esbuild-plugin-copy').default;
const packageJson = require('../package.json');
const path = require('path');
const fs = require('fs');
const projectRoot = process.cwd();
// 输出目录
const outdir = path.join(projectRoot, 'dist');
// 删除 dist 目录
if (fs.existsSync(outdir)) {
fs.rmdirSync(outdir, { recursive: true });
}
// 构建
module.exports.result = esbuild.build({
entryPoints: [path.join(projectRoot, 'src', 'index.ts'), path.join(projectRoot, 'test', 'index.ts')],
external: Object.keys(packageJson.devDependencies),
bundle: true,
platform: 'node',
outdir,
plugins: [
copyPlugin({
assets: [{
from: ['./README.md'],
to: ['./README.md']
},{
from: ['./package.json'],
to: ['./package.json']
},{
from: ['./plugin.json'],
to: ['./plugin.json']
},{
from: ['./assets/*'],
to: ['./assets']
},{
from: ['./src/*'],
to: ['./source']
}]
})
]
}).catch(() => process.exit(1));

12
plugin-cli/scripts/index.js Executable file
View File

@ -0,0 +1,12 @@
#!/usr/bin/env node
const args = process.argv.slice(2); // 去掉数组的前两个元素
// 发布
if(args.includes('--release')){
require('./release.js');
}
// 打包
if(args.includes('--build')){
require('./build.js');
}

View File

@ -0,0 +1,48 @@
console.log("cool-plugin-cli run release");
const projectRoot = process.cwd();
const fs = require('fs');
const path = require('path');
const archiver = require('archiver');
const pluginJson = require(path.join(projectRoot, 'plugin.json'));
const packFolder = (folderPath, outputPath) => {
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
// 创建文件写入流
const output = fs.createWriteStream(outputPath);
const archive = archiver('zip', {
zlib: { level: 9 } // 设置压缩级别
});
// 监听关闭事件
output.on('close', function() {
console.log(`cool plugin package successfully. Total size: ${archive.pointer()} bytes`);
});
// 监听错误事件
archive.on('error', function(err) {
throw err;
});
// 将压缩内容流连接到文件写入流
archive.pipe(output);
// 添加文件夹
archive.directory(folderPath, false);
// 完成归档
archive.finalize();
}
// 打包
const folderPath = path.join(projectRoot, 'dist'); // 替换为你的文件夹路径
const outputPath = path.join(projectRoot, 'release', `${pluginJson.name}_v${pluginJson.version}.cool`); // 替换为你希望创建的 .cool 文件路径
const { result } = require('./build.js');
result.then(()=>{
packFolder(folderPath, outputPath);
})

64
plugin-cli/src/cache/store.ts vendored Normal file
View File

@ -0,0 +1,64 @@
import * as FsStore from "@cool-midway/cache-manager-fs-hash";
/**
* cool
*/
export class FsCacheStore {
options: any;
store: FsStore;
constructor(options = {}) {
options = {
...options,
path: "cache",
ttl: -1,
};
this.options = options;
this.store = FsStore.create(options);
}
/**
*
* @param key
* @returns
*/
async get<T>(key: string): Promise<T> {
return await this.store.get(key);
}
/**
*
* @param key
* @param value
* @param ttl
*/
async set<T>(key: string, value: T, ttl?: number): Promise<void> {
let t = ttl ? ttl : this.options.ttl;
if (t > 0) {
t = t / 1000;
}
await this.store.set(key, value, {
ttl: t,
});
}
/**
*
* @param key
*/
async del(key: string): Promise<void> {
await this.store.del(key);
}
/**
*
*/
async reset(): Promise<void> {
await this.store.reset();
}
}
export const CoolCacheStore = function (options = {}) {
return new FsCacheStore(options);
};

View File

@ -0,0 +1,84 @@
/**
*
*/
export enum RESCODE {
// 成功
SUCCESS = 1000,
// 失败
COMMFAIL = 1001,
// 参数验证失败
VALIDATEFAIL = 1002,
// 参数验证失败
COREFAIL = 1003,
}
/**
*
*/
export enum RESMESSAGE {
// 成功
SUCCESS = "success",
// 失败
COMMFAIL = "comm fail",
// 参数验证失败
VALIDATEFAIL = "validate fail",
// 核心异常
COREFAIL = "core fail",
}
/**
*
*/
export enum ERRINFO {
NOENTITY = "未设置操作实体",
NOID = "查询参数[id]不存在",
SORTFIELD = "排序参数不正确",
}
/**
*
*/
export enum EVENT {
// 软删除
SOFT_DELETE = "onSoftDelete",
// 服务成功启动
SERVER_READY = "onServerReady",
// 服务就绪
READY = "onReady",
// ES 数据改变
ES_DATA_CHANGE = "esDataChange",
}
export class GlobalConfig {
private static instance: GlobalConfig;
RESCODE = {
SUCCESS: 1000,
COMMFAIL: 1001,
VALIDATEFAIL: 1002,
COREFAIL: 1003,
};
RESMESSAGE = {
SUCCESS: "success",
COMMFAIL: "comm fail",
VALIDATEFAIL: "validate fail",
COREFAIL: "core fail",
};
// ... 其他的配置 ...
private constructor() {}
static getInstance(): GlobalConfig {
if (!GlobalConfig.instance) {
GlobalConfig.instance = new GlobalConfig();
}
return GlobalConfig.instance;
}
}

View File

@ -0,0 +1,13 @@
/**
*
*/
export class BaseException extends Error {
status: number;
constructor(name: string, code: number, message: string) {
super(message);
this.name = name;
this.status = code;
}
}

View File

@ -0,0 +1,16 @@
import { GlobalConfig } from '../constant/global';
import { BaseException } from './base';
/**
*
*/
export class CoolCommException extends BaseException {
constructor(message: string) {
const { RESCODE, RESMESSAGE } = GlobalConfig.getInstance();
super(
'CoolCommException',
RESCODE.COMMFAIL,
message ? message : RESMESSAGE.COMMFAIL
);
}
}

View File

@ -0,0 +1,16 @@
import { GlobalConfig } from '../constant/global';
import { BaseException } from './base';
/**
*
*/
export class CoolCoreException extends BaseException {
constructor(message: string) {
const { RESCODE, RESMESSAGE } = GlobalConfig.getInstance();
super(
'CoolCoreException',
RESCODE.COREFAIL,
message ? message : RESMESSAGE.COREFAIL
);
}
}

View File

@ -0,0 +1,16 @@
import { GlobalConfig } from "../constant/global";
import { BaseException } from "./base";
/**
*
*/
export class CoolValidateException extends BaseException {
constructor(message: string) {
const { RESCODE, RESMESSAGE } = GlobalConfig.getInstance();
super(
"CoolValidateException",
RESCODE.VALIDATEFAIL,
message ? message : RESMESSAGE.VALIDATEFAIL
);
}
}

View File

@ -0,0 +1,67 @@
// 模式
export enum MODETYPE {
// 本地
LOCAL = "local",
// 云存储
CLOUD = "cloud",
// 其他
OTHER = "other",
}
export enum CLOUDTYPE {
// 阿里云存储
OSS = "oss",
// 腾讯云存储
COS = "cos",
// 七牛云存储
QINIU = "qiniu",
/** AWS S3 */
AWS = "aws",
}
/**
*
*/
export interface Mode {
// 模式
mode: MODETYPE;
// 类型
type: string;
}
/**
*
*/
export interface BaseUpload {
/**
*
*/
getMode(): Promise<Mode>;
/**
*
* @returns
*/
getMetaFileObj();
/**
*
* @param url
* @param fileName
*/
downAndUpload(url: string, fileName?: string): Promise<string>;
/**
* Key()
* @param filePath
* @param key
*/
uploadWithKey(filePath, key): Promise<string>;
/**
*
* @param ctx
* @param key
*/
upload(ctx): Promise<any>;
}

158
plugin-cli/src/index.ts Normal file
View File

@ -0,0 +1,158 @@
import { MidwayCache } from "@midwayjs/cache-manager";
import { IMidwayContext, IMidwayApplication } from "@midwayjs/core";
import { CoolCacheStore, FsCacheStore } from "./cache/store";
// 文件上传
export * from "./hook/upload";
// 异常处理
export * from "./exception/base";
export * from "./exception/comm";
export * from "./exception/core";
export * from "./exception/validate";
// 全局参数
export * from "./constant/global";
/**
*
*/
export interface PluginInfo {
/** 名称 */
name: string;
/** 唯一标识 */
key: string;
/** 钩子 */
hook: string;
/** 版本 */
version: string;
/** 描述 */
description: string;
/** 作者 */
author: string;
/** logo */
logo: string;
/** README 使用说明 */
readme: string;
/** 配置 */
config: any;
}
/**
*
*/
export abstract class BasePlugin {
/** 插件信息 */
pluginInfo: PluginInfo;
/** 请求上下文用到此项无法本地调试需安装到cool-admin中才能调试 */
ctx: IMidwayContext;
/** 应用实例用到此项无法本地调试需安装到cool-admin中才能调试 */
app: IMidwayApplication;
/** 缓存 */
cache: FsCacheStore | MidwayCache;
/** 插件 */
pluginService: PluginService;
/** 基础目录位置 */
baseDir: string;
setCtx(ctx: IMidwayContext) {
this.ctx = ctx;
}
setApp(app: IMidwayApplication) {
this.app = app;
this.baseDir = app.getBaseDir();
this.configDir();
}
constructor() {}
/**
* App级别的实例
* @param name
* @returns
*/
async getAppInstance(name: string) {
return await this.app.getApplicationContext().getAsync(name);
}
/**
*
* @param name
* @returns
*/
async getCtxInstance(name: string) {
return await this.ctx.requestContext.getAsync(name);
}
/**
*
* @param pluginInfo
* @param ctx
* @param app
*/
async init(
pluginInfo: PluginInfo,
ctx?: IMidwayContext,
app?: IMidwayApplication,
other?: any
) {
this.pluginInfo = pluginInfo;
this.ctx = ctx;
this.app = app;
this.baseDir = process.cwd();
if (this.app) {
this.baseDir = this.app?.getBaseDir();
}
this.configDir();
this.cache = CoolCacheStore({});
if (other) {
this.cache = other.cache;
this.pluginService = other.pluginService;
}
await this.ready();
}
/**
*
*/
private configDir() {
// 替换\为/
this.baseDir = this.baseDir.replace(/\\/g, "/");
let config = this.pluginInfo.config || {};
config = JSON.stringify(config);
this.pluginInfo.config = JSON.parse(
config.replace(/@baseDir/g, this.baseDir)
);
}
/**
*
*/
async ready() {}
}
/**
*
*/
export declare class PluginService {
/**
*
* @param key key
* @param method
* @param params
* @returns
*/
invoke(key: string, method: string, ...params: any[]): Promise<any>;
/**
*
* @param key
* @returns
*/
getInstance(key: string): Promise<any>;
/**
*
* @param key
*/
checkStatus(key: string): Promise<void>;
}

15
plugin-cli/tsconfig.json Normal file
View File

@ -0,0 +1,15 @@
{
"compilerOptions": {
"declaration": true,
"target": "ES2018",
"module": "CommonJS",
"esModuleInterop": true,
"moduleResolution": "Node",
"noImplicitAny": false,
"skipLibCheck": true,
"resolveJsonModule":true,
"outDir": "./dist"
},
"include": ["src/**/*", "scripts"],
"exclude": ["node_modules", "dist", "**/*.test.ts"]
}