更新依赖并增强应用管理命令功能
This commit is contained in:
@@ -67,6 +67,7 @@
|
|||||||
"nanoid": "^5.1.6",
|
"nanoid": "^5.1.6",
|
||||||
"send": "^1.2.1",
|
"send": "^1.2.1",
|
||||||
"supports-color": "^10.2.2",
|
"supports-color": "^10.2.2",
|
||||||
|
"table": "^6.9.0",
|
||||||
"ws": "npm:@kevisual/ws"
|
"ws": "npm:@kevisual/ws"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { AssistantApp } from '@/module/assistant/index.ts';
|
import { AssistantApp } from '@/module/assistant/index.ts';
|
||||||
import { program, Command, assistantConfig } from '@/program.ts';
|
import { program, Command, assistantConfig } from '@/program.ts';
|
||||||
import { AppDownload } from '@/services/app/index.ts';
|
import { AppDownload } from '@/services/app/index.ts';
|
||||||
|
import { table } from 'table';
|
||||||
|
|
||||||
const appManagerCommand = new Command('app-manager').alias('am').description('Manage Assistant Apps 管理本地的应用模块');
|
const appManagerCommand = new Command('app-manager').alias('am').description('Manage Assistant Apps 管理本地的应用模块');
|
||||||
program.addCommand(appManagerCommand);
|
program.addCommand(appManagerCommand);
|
||||||
@@ -8,11 +9,43 @@ program.addCommand(appManagerCommand);
|
|||||||
appManagerCommand
|
appManagerCommand
|
||||||
.command('list')
|
.command('list')
|
||||||
.description('List all installed apps')
|
.description('List all installed apps')
|
||||||
.action(async () => {
|
.option('-s, --status <status>', '列出状态信息, 可选值: running, stopped, inactive')
|
||||||
|
.option('-w, --wide', '显示更多信息')
|
||||||
|
.action(async (opts) => {
|
||||||
const manager = new AssistantApp(assistantConfig);
|
const manager = new AssistantApp(assistantConfig);
|
||||||
await manager.loadConfig();
|
await manager.loadConfig();
|
||||||
const showInfos = manager.getAllAppShowInfo();
|
let showInfos = manager.getAllAppShowInfo();
|
||||||
console.log('Installed Apps:', showInfos);
|
const isWide = opts.wide ?? false;
|
||||||
|
let header = [];
|
||||||
|
if (!isWide) {
|
||||||
|
showInfos = showInfos.map((item) => {
|
||||||
|
return { key: item.key, status: item.status };
|
||||||
|
});
|
||||||
|
header = ['Key', 'Status'];
|
||||||
|
}
|
||||||
|
if (opts.status) {
|
||||||
|
const showList = showInfos.filter(info => info.status === opts.status);
|
||||||
|
if (showList.length === 0) {
|
||||||
|
console.log(`No apps with status: ${opts.status}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const teables = showList.map(item => Object.values(item));
|
||||||
|
teables.unshift(header);
|
||||||
|
console.log('App Start Info:\n')
|
||||||
|
console.log(table(teables));
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (showInfos.length === 0) {
|
||||||
|
console.log('No installed apps found.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
header = Object.keys(showInfos[0]);;
|
||||||
|
const teables = showInfos.map(item => Object.values(item));
|
||||||
|
teables.unshift(header);
|
||||||
|
|
||||||
|
console.log('Installed Apps:\n')
|
||||||
|
console.log(table(teables));
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
appManagerCommand
|
appManagerCommand
|
||||||
|
|||||||
@@ -1,15 +1,69 @@
|
|||||||
import { program, Command, assistantConfig } from '@/program.ts';
|
import { program, Command, assistantConfig } from '@/program.ts';
|
||||||
import { spawnSync } from 'node:child_process';
|
import { spawnSync } from 'node:child_process';
|
||||||
|
import path from 'node:path';
|
||||||
|
import fs from 'node:fs';
|
||||||
|
import { AssistantApp, checkFileExists } from '@/lib.ts';
|
||||||
|
import { logger } from '@/module/logger.ts';
|
||||||
|
import { LoadApp, StopApp } from '@/module/local-apps/src/modules/manager.ts';
|
||||||
|
|
||||||
const runScriptsCommand = new Command('run-scripts')
|
const runScriptsCommand = new Command('run-scripts')
|
||||||
.alias('run')
|
.alias('run')
|
||||||
.arguments('<cmd> [env]')
|
.arguments('<cmd> [env]')
|
||||||
|
.option('-l --local', '使用当前文件夹的package.json中的scripts', false)
|
||||||
.description('运行脚本,在assistant.config.json中配置的脚本')
|
.description('运行脚本,在assistant.config.json中配置的脚本')
|
||||||
.action(async (cmd, env) => {
|
.action(async (cmd, env, opts) => {
|
||||||
|
const useLocal = opts.local;
|
||||||
|
const showScripts = cmd === 'show';
|
||||||
|
const showScriptFunc = (scripts: any) => {
|
||||||
|
console.log('可用的本地脚本:');
|
||||||
|
let has = false;
|
||||||
|
Object.keys(scripts).forEach((key) => {
|
||||||
|
console.log(`- ${key}: ${scripts[key]}`);
|
||||||
|
has = true;
|
||||||
|
});
|
||||||
|
if (!has) {
|
||||||
|
console.log('当前未定义任何脚本。');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (useLocal) {
|
||||||
|
const pkgPath = path.join(process.cwd(), 'package.json');
|
||||||
|
if (checkFileExists(pkgPath) === false) {
|
||||||
|
console.error('当前目录下未找到 package.json 文件。');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
||||||
|
const scripts = pkg.scripts || {};
|
||||||
|
if (showScripts) {
|
||||||
|
showScriptFunc(scripts);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const script = scripts[cmd];
|
||||||
|
if (!script) {
|
||||||
|
console.error(`Script "${cmd}" not found in local package.json.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const command = [script, ...(env ? [env] : [])].join(' ');
|
||||||
|
const res = spawnSync(command, { shell: true, stdio: 'inherit', cwd: assistantConfig.configDir });
|
||||||
|
console.log(`执行 "[${command}]"...`);
|
||||||
|
if (res.error) {
|
||||||
|
console.error(`执行失败 "${cmd}":`, res.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (res.status !== 0) {
|
||||||
|
console.error(`本地脚本 "${cmd}" 以代码 ${res.status} 退出`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
assistantConfig.checkMounted();
|
assistantConfig.checkMounted();
|
||||||
const configs = assistantConfig.getCacheAssistantConfig();
|
const configs = assistantConfig.getCacheAssistantConfig();
|
||||||
const scripts = configs?.scripts || {};
|
const scripts = configs?.scripts || {};
|
||||||
try {
|
try {
|
||||||
const script = scripts[cmd];
|
const script = scripts[cmd];
|
||||||
|
if (showScripts) {
|
||||||
|
showScriptFunc(scripts);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!script) {
|
if (!script) {
|
||||||
console.error(`Script "${cmd}" not found.`);
|
console.error(`Script "${cmd}" not found.`);
|
||||||
return;
|
return;
|
||||||
@@ -32,3 +86,93 @@ const runScriptsCommand = new Command('run-scripts')
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
program.addCommand(runScriptsCommand);
|
program.addCommand(runScriptsCommand);
|
||||||
|
|
||||||
|
const createRandomApp = (opts: { app: any, package: any, pwd: string, status?: string }) => {
|
||||||
|
const { app, package: packageJson, pwd } = opts;
|
||||||
|
if (!app.status) {
|
||||||
|
app.status = opts.status || 'running'
|
||||||
|
}
|
||||||
|
if (!app.key) {
|
||||||
|
const randomSuffix = Math.random().toString(36).substring(2, 8);
|
||||||
|
app.key = packageJson.basename || `${'unknown-app'}-${randomSuffix}`;
|
||||||
|
}
|
||||||
|
app.path = pwd;
|
||||||
|
if (app.type === 'pm2-system-app' && !app.pm2Options) {
|
||||||
|
app.pm2Options = {
|
||||||
|
cwd: pwd,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
const start = new Command('start')
|
||||||
|
.description('获取package.json中app参数并启动对应的app')
|
||||||
|
.option('-s --save', '保存应用信息到assistant配置中', false)
|
||||||
|
.action(async (opts) => {
|
||||||
|
// assistantConfig.checkMounted();
|
||||||
|
const pwd = process.cwd();
|
||||||
|
const packageJsonPath = path.join(pwd, 'package.json');
|
||||||
|
if (checkFileExists(packageJsonPath) === false) {
|
||||||
|
logger.error('package.json 在当前目录未找到,请在包含 package.json 的目录下运行此命令。');
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
||||||
|
const appKey = packageJson.app;
|
||||||
|
if (!appKey) {
|
||||||
|
logger.error('package.json 中未找到 app 字段,请确保在 package.json 中正确配置 app 字段。');
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const app = createRandomApp({ app: packageJson.app, package: packageJson, pwd });
|
||||||
|
if (app.type !== 'system-app') {
|
||||||
|
const load = await LoadApp(app, {});
|
||||||
|
if (!load) {
|
||||||
|
logger.error(`未能加载应用, 请确保应用名称正确且已安装。`, app.type);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LoadApp(app, {}).then(() => {
|
||||||
|
logger.info(`系统应用已启动: ${app.key}`);
|
||||||
|
}).catch((err) => {
|
||||||
|
logger.error(`启动系统应用失败: ${app.key}`, err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.save) {
|
||||||
|
assistantConfig.checkMounted();
|
||||||
|
const manager = new AssistantApp(assistantConfig, app);
|
||||||
|
await manager.loadConfig();
|
||||||
|
await manager.saveAppInfo(app);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
program.addCommand(start);
|
||||||
|
|
||||||
|
const stop = new Command('stop')
|
||||||
|
.description('获取package.json中app参数并停止对应的app')
|
||||||
|
.option('-t --todo <todo>', '停止应用,在pm2中如果为stop则停止,如果为remove则删除,默认为stop', 'stop')
|
||||||
|
.option('-s --save', '保存应用信息到assistant配置中', false)
|
||||||
|
.action(async (opts) => {
|
||||||
|
// assistantConfig.checkMounted();
|
||||||
|
const pwd = process.cwd();
|
||||||
|
const packageJsonPath = path.join(pwd, 'package.json');
|
||||||
|
if (checkFileExists(packageJsonPath) === false) {
|
||||||
|
logger.error('package.json 在当前目录未找到,请在包含 package.json 的目录下运行此命令。');
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
||||||
|
const appKey = packageJson.app;
|
||||||
|
if (!appKey) {
|
||||||
|
logger.error('package.json 中未找到 app 字段,请确保在 package.json 中正确配置 app 字段。');
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const app = createRandomApp({ app: packageJson.app, package: packageJson, pwd, status: 'stopped' });
|
||||||
|
await StopApp(app, { todo: opts.todo });
|
||||||
|
if (opts.save) {
|
||||||
|
assistantConfig.checkMounted();
|
||||||
|
const manager = new AssistantApp(assistantConfig, app);
|
||||||
|
await manager.loadConfig();
|
||||||
|
await manager.removeApp(app.key, { deleteFile: false });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
program.addCommand(stop);
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,3 @@ if (DEV_SERVER) {
|
|||||||
});
|
});
|
||||||
loadManager({ configFilename: 'b.json' });
|
loadManager({ configFilename: 'b.json' });
|
||||||
}
|
}
|
||||||
|
|
||||||
export const loadApp = (mainApp: App, appInfo?: any) => {
|
|
||||||
//
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ export type AppInfo = {
|
|||||||
path?: string; // 文件路径
|
path?: string; // 文件路径
|
||||||
env?: Record<string, any>; // 环境变量
|
env?: Record<string, any>; // 环境变量
|
||||||
engine?: string; // runtime, python node deno bun etc
|
engine?: string; // runtime, python node deno bun etc
|
||||||
|
init?: boolean; // 是否需要初始化安装npm等依赖
|
||||||
/**
|
/**
|
||||||
* pm2 选项, 仅仅当是AppType.Pm2SystemApp的时候生效
|
* pm2 选项, 仅仅当是AppType.Pm2SystemApp的时候生效
|
||||||
* pm2 选项可以参考 https://pm2.keymetrics.io/docs/usage/application-declaration/
|
* pm2 选项可以参考 https://pm2.keymetrics.io/docs/usage/application-declaration/
|
||||||
@@ -125,71 +126,7 @@ export class Manager<T extends AppInfo = AppInfo> {
|
|||||||
async loadApp(app: T) {
|
async loadApp(app: T) {
|
||||||
const mainApp = this.mainApp;
|
const mainApp = this.mainApp;
|
||||||
this.apps.set(app.key, app);
|
this.apps.set(app.key, app);
|
||||||
if (app.status !== 'running') {
|
return await LoadApp(app, { mainApp, pm2Connect: this.#pm2Connect });
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!fileIsExist(app.path)) {
|
|
||||||
console.error('app is not found');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const pathEntry = path.join(app.path, app.entry);
|
|
||||||
if (!fileIsExist(pathEntry)) {
|
|
||||||
console.error('file entry not found');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const entry = app.entry + `?timestamp=${app?.timestamp}`;
|
|
||||||
// 注册路由
|
|
||||||
if (app.type === AppType.MicroApp) {
|
|
||||||
const childProcess = fork(app.entry, [], {
|
|
||||||
stdio: 'inherit', // 共享主进程的标准输入输出
|
|
||||||
cwd: app.path,
|
|
||||||
env: {
|
|
||||||
...process.env,
|
|
||||||
...app.env,
|
|
||||||
APP_KEY: app.key,
|
|
||||||
APP_PATH: app.path,
|
|
||||||
APP_ENTRY: entry
|
|
||||||
}
|
|
||||||
});
|
|
||||||
app.process = childProcess;
|
|
||||||
} else if (app.type === AppType.SystemApp) {
|
|
||||||
const pathEntryAndTimestamp = path.join(app.path, entry);
|
|
||||||
// Windows下需要使用file://协议,并将反斜杠转换为正斜杠
|
|
||||||
const importPath = process.platform === 'win32'
|
|
||||||
? 'file:///' + pathEntryAndTimestamp.replace(/\\/g, '/')
|
|
||||||
: pathEntryAndTimestamp;
|
|
||||||
const module = await import(importPath);
|
|
||||||
if (module.loadApp && mainApp) {
|
|
||||||
await module.loadApp?.(mainApp, app);
|
|
||||||
}
|
|
||||||
} else if (app.type === AppType.GatewayApp) {
|
|
||||||
console.log('gateway app not support');
|
|
||||||
} else if (app.type === AppType.Pm2SystemApp) {
|
|
||||||
const pathEntry = path.join(app.path, app.entry);
|
|
||||||
const pm2Manager = new Pm2Manager({
|
|
||||||
appName: app.key,
|
|
||||||
script: pathEntry,
|
|
||||||
pm2Connect: this.#pm2Connect
|
|
||||||
});
|
|
||||||
|
|
||||||
// const isInstall = await checkInstall(app);
|
|
||||||
// if (!isInstall) {
|
|
||||||
// console.log('install failed');
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
const pm2Options: StartOptions = app.pm2Options || {};
|
|
||||||
if (app?.engine) {
|
|
||||||
pm2Options.interpreter = pm2Options.interpreter || app?.engine;
|
|
||||||
}
|
|
||||||
if (!pm2Options.cwd) {
|
|
||||||
pm2Options.cwd = path.join(app.path, '../..');
|
|
||||||
}
|
|
||||||
await pm2Manager.start(pm2Options);
|
|
||||||
} else {
|
|
||||||
console.error('app type not support', app.type);
|
|
||||||
}
|
|
||||||
console.log(`load ${app.type} success`, app.key);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* create new app info
|
* create new app info
|
||||||
@@ -308,24 +245,7 @@ export class Manager<T extends AppInfo = AppInfo> {
|
|||||||
if (!app) {
|
if (!app) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (app.status === 'stop' && app.type === AppType.SystemApp) {
|
await StopApp(app, { pm2Connect: this.#pm2Connect, todo: 'stop' });
|
||||||
console.log(`app ${key} is stopped`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
app.status = 'stop';
|
|
||||||
if (app.type === AppType.MicroApp) {
|
|
||||||
if (app.process) {
|
|
||||||
app.process.kill();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (app.type === AppType.Pm2SystemApp) {
|
|
||||||
const pm2Manager = new Pm2Manager({
|
|
||||||
appName: app.key,
|
|
||||||
script: app.entry,
|
|
||||||
pm2Connect: this.#pm2Connect
|
|
||||||
});
|
|
||||||
await pm2Manager.stop();
|
|
||||||
}
|
|
||||||
await this.saveAppInfo(app);
|
await this.saveAppInfo(app);
|
||||||
}
|
}
|
||||||
async restart(key: string) {
|
async restart(key: string) {
|
||||||
@@ -393,8 +313,9 @@ export class Manager<T extends AppInfo = AppInfo> {
|
|||||||
* @param key
|
* @param key
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async removeApp(key: string) {
|
async removeApp(key: string, opts?: { deleteFile?: boolean }) {
|
||||||
const app = this.apps.get(key);
|
const app = this.apps.get(key);
|
||||||
|
const deleteFile = opts?.deleteFile ?? true;
|
||||||
if (!app) {
|
if (!app) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -414,6 +335,9 @@ export class Manager<T extends AppInfo = AppInfo> {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('delete pm2 process error', e);
|
console.log('delete pm2 process error', e);
|
||||||
}
|
}
|
||||||
|
if (!deleteFile) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
deleteFileAppInfo(key, this.appsPath);
|
deleteFileAppInfo(key, this.appsPath);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -485,7 +409,105 @@ export class Manager<T extends AppInfo = AppInfo> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export const LoadApp = async (app: AppInfo, opts?: { mainApp?: any, pm2Connect?: any }) => {
|
||||||
|
const mainApp = opts?.mainApp;
|
||||||
|
const pm2Connect = opts?.pm2Connect;
|
||||||
|
if (app.status !== 'running') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!fileIsExist(app.path)) {
|
||||||
|
console.error('app is not found');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const pathEntry = path.join(app.path, app.entry);
|
||||||
|
if (!fileIsExist(pathEntry)) {
|
||||||
|
console.error('file entry not found');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const entry = app.entry + `?timestamp=${app?.timestamp}`;
|
||||||
|
// 注册路由
|
||||||
|
if (app.type === AppType.MicroApp) {
|
||||||
|
const childProcess = fork(app.entry, [], {
|
||||||
|
stdio: 'inherit', // 共享主进程的标准输入输出
|
||||||
|
cwd: app.path,
|
||||||
|
env: {
|
||||||
|
...process.env,
|
||||||
|
...app.env,
|
||||||
|
APP_KEY: app.key,
|
||||||
|
APP_PATH: app.path,
|
||||||
|
APP_ENTRY: entry
|
||||||
|
}
|
||||||
|
});
|
||||||
|
app.process = childProcess;
|
||||||
|
} else if (app.type === AppType.SystemApp) {
|
||||||
|
const pathEntryAndTimestamp = path.join(app.path, entry);
|
||||||
|
// Windows下需要使用file://协议,并将反斜杠转换为正斜杠
|
||||||
|
const importPath = process.platform === 'win32'
|
||||||
|
? 'file:///' + pathEntryAndTimestamp.replace(/\\/g, '/')
|
||||||
|
: pathEntryAndTimestamp;
|
||||||
|
const module = await import(importPath);
|
||||||
|
if (module.loadApp && mainApp) {
|
||||||
|
await module.loadApp?.(mainApp, app);
|
||||||
|
}
|
||||||
|
} else if (app.type === AppType.GatewayApp) {
|
||||||
|
console.log('gateway app not support');
|
||||||
|
} else if (app.type === AppType.Pm2SystemApp) {
|
||||||
|
const pathEntry = path.join(app.path, app.entry);
|
||||||
|
console.log('pm2 system app start', pathEntry);
|
||||||
|
const pm2Manager = new Pm2Manager({
|
||||||
|
appName: app.key,
|
||||||
|
script: pathEntry,
|
||||||
|
pm2Connect: pm2Connect
|
||||||
|
});
|
||||||
|
if (app?.init) {
|
||||||
|
const isInstall = await checkInstall(app);
|
||||||
|
if (!isInstall) {
|
||||||
|
console.log('install failed');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const pm2Options: StartOptions = app.pm2Options || {};
|
||||||
|
if (app?.engine) {
|
||||||
|
pm2Options.interpreter = pm2Options.interpreter || app?.engine;
|
||||||
|
}
|
||||||
|
if (!pm2Options.cwd) {
|
||||||
|
pm2Options.cwd = path.join(app.path, '../..');
|
||||||
|
}
|
||||||
|
console.log('pm2 start options', pm2Options);
|
||||||
|
await pm2Manager.start(pm2Options);
|
||||||
|
} else if (app.type === AppType.ScriptApp) {
|
||||||
|
// console.log('script app 直接运行,不需要启动');
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
console.error('app type not support', app.type);
|
||||||
|
}
|
||||||
|
console.log(`load ${app.type} success`, app.key);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const StopApp = async (app: AppInfo, opts?: { pm2Connect?: Pm2Connect, todo?: 'stop' | 'remove' | 'restart' }) => {
|
||||||
|
const key = app.key;
|
||||||
|
const pm2Connect = opts?.pm2Connect;
|
||||||
|
const todo = opts?.todo || 'stop';
|
||||||
|
if (app.status === 'stop' && app.type === AppType.SystemApp) {
|
||||||
|
console.log(`app ${key} is stopped`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
app.status = 'stop';
|
||||||
|
if (app.type === AppType.MicroApp) {
|
||||||
|
if (app.process) {
|
||||||
|
app.process.kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (app.type === AppType.Pm2SystemApp) {
|
||||||
|
const pm2Manager = new Pm2Manager({
|
||||||
|
appName: app.key,
|
||||||
|
script: app.entry,
|
||||||
|
pm2Connect: pm2Connect
|
||||||
|
});
|
||||||
|
await pm2Manager[todo]?.();
|
||||||
|
}
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* 安装app通过key
|
* 安装app通过key
|
||||||
* @param key
|
* @param key
|
||||||
|
|||||||
@@ -108,6 +108,9 @@ export class Pm2Connect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
type RunOptions = {
|
type RunOptions = {
|
||||||
|
/**
|
||||||
|
* 是否在操作完成后退出连接
|
||||||
|
*/
|
||||||
needExit?: boolean;
|
needExit?: boolean;
|
||||||
};
|
};
|
||||||
export class Pm2Manager {
|
export class Pm2Manager {
|
||||||
@@ -226,6 +229,9 @@ export class Pm2Manager {
|
|||||||
this.pm2Connect.checkDisconnect(runOpts);
|
this.pm2Connect.checkDisconnect(runOpts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
async remove(runOpts?: RunOptions) {
|
||||||
|
this.deleteProcess(runOpts);
|
||||||
|
}
|
||||||
async deleteProcess(runOpts?: RunOptions) {
|
async deleteProcess(runOpts?: RunOptions) {
|
||||||
try {
|
try {
|
||||||
await this.pm2Connect.checkConnect();
|
await this.pm2Connect.checkConnect();
|
||||||
|
|||||||
62
pnpm-lock.yaml
generated
62
pnpm-lock.yaml
generated
@@ -208,6 +208,9 @@ importers:
|
|||||||
supports-color:
|
supports-color:
|
||||||
specifier: ^10.2.2
|
specifier: ^10.2.2
|
||||||
version: 10.2.2
|
version: 10.2.2
|
||||||
|
table:
|
||||||
|
specifier: ^6.9.0
|
||||||
|
version: 6.9.0
|
||||||
ws:
|
ws:
|
||||||
specifier: npm:@kevisual/ws
|
specifier: npm:@kevisual/ws
|
||||||
version: '@kevisual/ws@8.0.0'
|
version: '@kevisual/ws@8.0.0'
|
||||||
@@ -2314,6 +2317,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==}
|
resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==}
|
||||||
engines: {node: '>= 14'}
|
engines: {node: '>= 14'}
|
||||||
|
|
||||||
|
ajv@8.17.1:
|
||||||
|
resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==}
|
||||||
|
|
||||||
amp-message@0.1.2:
|
amp-message@0.1.2:
|
||||||
resolution: {integrity: sha512-JqutcFwoU1+jhv7ArgW38bqrE+LQdcRv4NxNw0mp0JHQyB6tXesWRjtYKlDgHRY2o3JE5UTaBGUK8kSWUdxWUg==}
|
resolution: {integrity: sha512-JqutcFwoU1+jhv7ArgW38bqrE+LQdcRv4NxNw0mp0JHQyB6tXesWRjtYKlDgHRY2o3JE5UTaBGUK8kSWUdxWUg==}
|
||||||
|
|
||||||
@@ -2392,6 +2398,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==}
|
resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
|
astral-regex@2.0.0:
|
||||||
|
resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
astring@1.9.0:
|
astring@1.9.0:
|
||||||
resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==}
|
resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@@ -3082,6 +3092,9 @@ packages:
|
|||||||
fast-json-patch@3.1.1:
|
fast-json-patch@3.1.1:
|
||||||
resolution: {integrity: sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==}
|
resolution: {integrity: sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==}
|
||||||
|
|
||||||
|
fast-uri@3.1.0:
|
||||||
|
resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==}
|
||||||
|
|
||||||
fastq@1.17.1:
|
fastq@1.17.1:
|
||||||
resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
|
resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
|
||||||
|
|
||||||
@@ -3557,6 +3570,9 @@ packages:
|
|||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
json-schema-traverse@1.0.0:
|
||||||
|
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
|
||||||
|
|
||||||
json-stringify-safe@5.0.1:
|
json-stringify-safe@5.0.1:
|
||||||
resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==}
|
resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==}
|
||||||
|
|
||||||
@@ -3692,6 +3708,9 @@ packages:
|
|||||||
lodash.once@4.1.1:
|
lodash.once@4.1.1:
|
||||||
resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==}
|
resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==}
|
||||||
|
|
||||||
|
lodash.truncate@4.4.2:
|
||||||
|
resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==}
|
||||||
|
|
||||||
lodash@4.17.21:
|
lodash@4.17.21:
|
||||||
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
|
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
|
||||||
|
|
||||||
@@ -4487,6 +4506,10 @@ packages:
|
|||||||
remark-stringify@11.0.0:
|
remark-stringify@11.0.0:
|
||||||
resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==}
|
resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==}
|
||||||
|
|
||||||
|
require-from-string@2.0.2:
|
||||||
|
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
require-in-the-middle@5.2.0:
|
require-in-the-middle@5.2.0:
|
||||||
resolution: {integrity: sha512-efCx3b+0Z69/LGJmm9Yvi4cqEdxnoGnxYxGxBghkkTTFeXRtTCmmhO0AnAfHz59k957uTSuy8WaHqOs8wbYUWg==}
|
resolution: {integrity: sha512-efCx3b+0Z69/LGJmm9Yvi4cqEdxnoGnxYxGxBghkkTTFeXRtTCmmhO0AnAfHz59k957uTSuy8WaHqOs8wbYUWg==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
@@ -4651,6 +4674,10 @@ packages:
|
|||||||
engines: {node: '>=14.0.0', npm: '>=6.0.0'}
|
engines: {node: '>=14.0.0', npm: '>=6.0.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
slice-ansi@4.0.0:
|
||||||
|
resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
smart-buffer@4.2.0:
|
smart-buffer@4.2.0:
|
||||||
resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
|
resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
|
||||||
engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
|
engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
|
||||||
@@ -4792,6 +4819,10 @@ packages:
|
|||||||
os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android]
|
os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android]
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
table@6.9.0:
|
||||||
|
resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==}
|
||||||
|
engines: {node: '>=10.0.0'}
|
||||||
|
|
||||||
tailwind-merge@3.4.0:
|
tailwind-merge@3.4.0:
|
||||||
resolution: {integrity: sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==}
|
resolution: {integrity: sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==}
|
||||||
|
|
||||||
@@ -7771,6 +7802,13 @@ snapshots:
|
|||||||
|
|
||||||
agent-base@7.1.3: {}
|
agent-base@7.1.3: {}
|
||||||
|
|
||||||
|
ajv@8.17.1:
|
||||||
|
dependencies:
|
||||||
|
fast-deep-equal: 3.1.3
|
||||||
|
fast-uri: 3.1.0
|
||||||
|
json-schema-traverse: 1.0.0
|
||||||
|
require-from-string: 2.0.2
|
||||||
|
|
||||||
amp-message@0.1.2:
|
amp-message@0.1.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
amp: 0.3.1
|
amp: 0.3.1
|
||||||
@@ -7902,6 +7940,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
astral-regex@2.0.0: {}
|
||||||
|
|
||||||
astring@1.9.0: {}
|
astring@1.9.0: {}
|
||||||
|
|
||||||
astro@5.16.6(@types/node@25.0.3)(idb-keyval@6.2.2)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.43.0)(typescript@5.8.2):
|
astro@5.16.6(@types/node@25.0.3)(idb-keyval@6.2.2)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.43.0)(typescript@5.8.2):
|
||||||
@@ -8661,6 +8701,8 @@ snapshots:
|
|||||||
|
|
||||||
fast-json-patch@3.1.1: {}
|
fast-json-patch@3.1.1: {}
|
||||||
|
|
||||||
|
fast-uri@3.1.0: {}
|
||||||
|
|
||||||
fastq@1.17.1:
|
fastq@1.17.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
reusify: 1.0.4
|
reusify: 1.0.4
|
||||||
@@ -9244,6 +9286,8 @@ snapshots:
|
|||||||
|
|
||||||
jsesc@3.1.0: {}
|
jsesc@3.1.0: {}
|
||||||
|
|
||||||
|
json-schema-traverse@1.0.0: {}
|
||||||
|
|
||||||
json-stringify-safe@5.0.1:
|
json-stringify-safe@5.0.1:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@@ -9373,6 +9417,8 @@ snapshots:
|
|||||||
|
|
||||||
lodash.once@4.1.1: {}
|
lodash.once@4.1.1: {}
|
||||||
|
|
||||||
|
lodash.truncate@4.4.2: {}
|
||||||
|
|
||||||
lodash@4.17.21: {}
|
lodash@4.17.21: {}
|
||||||
|
|
||||||
longest-streak@3.1.0: {}
|
longest-streak@3.1.0: {}
|
||||||
@@ -10587,6 +10633,8 @@ snapshots:
|
|||||||
mdast-util-to-markdown: 2.1.2
|
mdast-util-to-markdown: 2.1.2
|
||||||
unified: 11.0.5
|
unified: 11.0.5
|
||||||
|
|
||||||
|
require-from-string@2.0.2: {}
|
||||||
|
|
||||||
require-in-the-middle@5.2.0(supports-color@10.2.2):
|
require-in-the-middle@5.2.0(supports-color@10.2.2):
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 4.4.0(supports-color@10.2.2)
|
debug: 4.4.0(supports-color@10.2.2)
|
||||||
@@ -10814,6 +10862,12 @@ snapshots:
|
|||||||
arg: 5.0.2
|
arg: 5.0.2
|
||||||
sax: 1.4.1
|
sax: 1.4.1
|
||||||
|
|
||||||
|
slice-ansi@4.0.0:
|
||||||
|
dependencies:
|
||||||
|
ansi-styles: 4.3.0
|
||||||
|
astral-regex: 2.0.0
|
||||||
|
is-fullwidth-code-point: 3.0.0
|
||||||
|
|
||||||
smart-buffer@4.2.0: {}
|
smart-buffer@4.2.0: {}
|
||||||
|
|
||||||
smol-toml@1.5.2: {}
|
smol-toml@1.5.2: {}
|
||||||
@@ -10952,6 +11006,14 @@ snapshots:
|
|||||||
systeminformation@5.25.11:
|
systeminformation@5.25.11:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
table@6.9.0:
|
||||||
|
dependencies:
|
||||||
|
ajv: 8.17.1
|
||||||
|
lodash.truncate: 4.4.2
|
||||||
|
slice-ansi: 4.0.0
|
||||||
|
string-width: 4.2.3
|
||||||
|
strip-ansi: 6.0.1
|
||||||
|
|
||||||
tailwind-merge@3.4.0: {}
|
tailwind-merge@3.4.0: {}
|
||||||
|
|
||||||
tailwindcss@4.1.18: {}
|
tailwindcss@4.1.18: {}
|
||||||
|
|||||||
Reference in New Issue
Block a user