This commit is contained in:
2025-12-05 01:34:06 +08:00
parent b6bdfd872a
commit c018ffd422
6 changed files with 8 additions and 240 deletions

View File

@@ -1,4 +1,4 @@
import { Manager } from '@kevisual/local-app-manager/manager'; import { Manager } from '../../local-apps/src/modules/manager.ts';
import type { AssistantConfig } from '@/module/assistant/index.ts'; import type { AssistantConfig } from '@/module/assistant/index.ts';
import { parseIfJson } from '@/module/assistant/index.ts'; import { parseIfJson } from '@/module/assistant/index.ts';
import path from 'node:path'; import path from 'node:path';

View File

@@ -0,0 +1,3 @@
export { Pm2Manager, Pm2Opts } from "./src/modules/pm2.ts";
export { Manager } from "./src/modules/manager.ts";

View File

@@ -7,6 +7,9 @@ import './routes/list.ts';
export { app, manager, loadManager }; export { app, manager, loadManager };
// @ts-ignore
const DEV_SERVER = import.meta.env?.DEV_SERVER || false;
if (DEV_SERVER) { if (DEV_SERVER) {
app app
.route({ .route({

View File

@@ -28,6 +28,6 @@
}, },
"include": [ "include": [
"typings.d.ts", "typings.d.ts",
"src/**/*.ts", "src/**/*.ts", "mod.ts",
], ],
} }

View File

@@ -1,237 +0,0 @@
import { program, Command } from '@/program.ts';
import { checkFileExists, envisionPath, getConfig, writeConfig } from '@/module/index.ts';
import path from 'path';
import fs from 'fs';
import { chalk } from '@/module/chalk.ts';
import inquirer from 'inquirer';
import { spawn, spawnSync } from 'child_process';
import { checkPm2, installDep, installDeps } from '@/uitls/npm.ts';
const command = new Command('init').description('初始化应用').action((optison) => {
console.log('init');
}); //
program.addCommand(command);
const setMainAppConfig = async (mainAppPath: string) => {
const config = getConfig();
config.mainAppPath = mainAppPath;
writeConfig(config);
};
const initMain = async () => {
const mainAppPath = path.join(envisionPath, 'main-app');
const pkgPath = path.join(mainAppPath, 'package.json');
if (!checkFileExists(pkgPath)) {
fs.mkdirSync(mainAppPath, { recursive: true });
const pkg = {
name: 'main-app',
version: '1.0.0',
type: 'module',
main: 'dist/app.mjs',
scripts: {
start: 'node dist/app.mjs',
pm2: 'pm2 start dist/app.mjs --name main-app',
},
dependencies: {
'@kevisual/router': 'latest',
'@kevisual/local-app-manager': 'latest',
'@kevisual/use-config': 'latest',
},
};
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
console.log(chalk.green('初始化 main-app 成功'));
}
// 在对应的路径,安装依赖
installDeps({ appPath: mainAppPath, sync: true });
// 创建 dist/app.mjs
const code = `import { App } from '@kevisual/router';
import { app as LocalApp } from '@kevisual/local-app-manager';
import { useConfig } from '@kevisual/use-config';
const config = useConfig();
const host = config.host || '127.0.0.1';
LocalApp.listen(config.port, host, () => {
console.log(\`LocalApp is running on http://\${host}:\${config.port}\`);
});
`;
const codeDistPath = path.join(mainAppPath, 'dist');
if (!checkFileExists(codeDistPath)) {
fs.mkdirSync(codeDistPath, { recursive: true });
}
fs.writeFileSync(path.join(codeDistPath, 'app.mjs'), code);
console.log(chalk.green('创建 app.mjs 成功'));
// 创建 app.config.json5
const answers = await inquirer.prompt([
{
type: 'input',
name: 'port',
message: '请输入端口号',
default: '11015',
},
{
type: 'input',
name: 'host',
message: '请输入host',
default: '127.0.0.1',
},
{
type: 'input',
name: 'appsPath',
message: '请输入apps路径',
default: path.join(mainAppPath, 'apps'),
},
]);
const json5 = {
port: Number(answers.port),
host: answers.host,
appsPath: answers.appsPath,
};
fs.writeFileSync(path.join(mainAppPath, 'app.config.json5'), JSON.stringify(json5, null, 2));
//
console.log(chalk.green('创建 app.config.json5 成功'));
setMainAppConfig(mainAppPath);
writeConfig({ ...getConfig(), appsPath: answers.appsPath });
};
const checkPid = (pid: number) => {
try {
process.kill(pid, 0);
return true;
} catch (err) {
if (err.code === 'ESRCH') {
// 进程不存在
console.log(`进程 ${pid} 不存在`);
return false;
} else if (err.code === 'EPERM') {
// 没有权限访问该进程
console.log(`没有权限检查进程 ${pid}`);
return true;
} else {
// 其他错误
console.error(`检查进程 ${pid} 时出错:`, err);
return false;
}
}
};
const mainApp = new Command('main')
.description('初始化main的应用') //
.option('-i, --init', '初始化main应用')
.option('-s, --start', '启动main应用')
.option('-r, --restart', '重启main应用')
.option('-e, --exit', '停止main应用')
.option('-p, --pm2', '使用pm2管理只作为启动')
.action(async (options) => {
if (options.init) {
await initMain();
return;
} else if (!options.start && !options.restart && !options.exit && !options.pm2) {
console.warn(chalk.yellow('请使用 --init 初始化 main 应用'));
}
const runChild = () => {
const logDir = path.join(envisionPath, 'log');
const logFile = path.join(logDir, 'child.log');
// 确保日志目录存在
fs.mkdirSync(logDir, { recursive: true });
// 如果日志文件不存在,创建一个
if (!checkFileExists(logFile)) {
fs.writeFileSync(logFile, '');
}
// 如果日志文件大于 10M重命名
const stats = fs.statSync(logFile);
if (stats.size > 1024 * 1024 * 10) {
fs.renameSync(logFile, path.join(logDir, `child-${Date.now()}.log`));
fs.writeFileSync(logFile, '');
}
// 打开日志文件(追加模式)
const logStream = fs.openSync(logFile, 'a');
if (options.pm2) {
if (!checkPm2()) {
console.log('安装pm2');
installDep({ isGlobal: true, sync: true, dep: 'pm2' });
console.log(chalk.green('安装pm2成功'));
}
}
let comm = options.pm2 ? 'pm2' : 'node';
const args = options.pm2 ? ['start', 'dist/app.mjs', '--name', 'main-app'] : ['dist/app.mjs'];
const childProcess = spawn(comm, args, {
cwd: getConfig().mainAppPath,
stdio: ['ignore', logStream, logStream], // 忽略 stdio, 重定向到文件
detached: true, // 使子进程独立运行
});
childProcess.unref(); // 使子进程独立运行
if (!options.pm2) {
writeConfig({ ...getConfig(), mainAppPid: childProcess.pid });
}
};
const config = getConfig();
if (!config.mainAppPath) {
console.log('请先初始化 main 应用');
return;
}
if (options.start) {
if (config.mainAppPid) {
// 检查是否有进程
if (checkPid(config.mainAppPid)) {
console.log('main app 已经启动');
return;
}
}
runChild();
}
if (options.restart) {
if (config.mainAppPid) {
// 检查是否有进程
if (checkPid(config.mainAppPid)) {
process.kill(config.mainAppPid);
}
}
runChild();
} else if (options.exit) {
if (config.mainAppPid) {
// 检查是否有进程
if (checkPid(config.mainAppPid)) {
process.kill(config.mainAppPid);
}
writeConfig({ ...config, mainAppPid: null });
console.log('main app 已经停止');
}
}
});
const mainPathCommand = new Command('path').action(() => {
const config = getConfig();
const appPath = path.resolve(config.mainAppPath);
console.log(`cd ${appPath}`);
});
mainApp.addCommand(mainPathCommand);
const mainLogCommand = new Command('log')
.option('-t, --tail', '查看日志')
.option('-f, --follow', '跟踪日志')
.description('查看main的日志')
.action((options) => {
const logDir = path.join(envisionPath, 'log');
const logFile = path.join(logDir, 'child.log');
if (!checkFileExists(logFile)) {
console.log('日志文件不存在');
return;
}
if (options.tail) {
const childProcess = spawn('tail', ['-n', '20', '-f', logFile]);
childProcess.stdout?.pipe(process.stdout);
childProcess.stderr?.pipe(process.stderr);
return;
}
if (options.follow) {
const childProcess = spawn('tail', ['-n', '50', '-f', logFile]);
childProcess.stdout?.pipe(process.stdout);
childProcess.stderr?.pipe(process.stderr);
return;
}
const log = fs.readFileSync(logFile, 'utf-8');
console.log(log);
});
mainApp.addCommand(mainLogCommand);
program.addCommand(mainApp);

View File

@@ -6,7 +6,6 @@ import './command/config.ts';
import './command/router.ts'; import './command/router.ts';
import './command/npm.ts'; import './command/npm.ts';
import './command/publish.ts'; import './command/publish.ts';
import './command/init.ts';
import './command/proxy.ts'; import './command/proxy.ts';
import './command/update.ts'; import './command/update.ts';