fix: update envision-cli tools for proxy and run app

This commit is contained in:
2024-12-05 02:26:47 +08:00
parent 2145fca826
commit da6211299b
15 changed files with 738 additions and 479 deletions

216
src/command/init.ts Normal file
View File

@@ -0,0 +1,216 @@
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';
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',
};
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
console.log(chalk.green('初始化 main-app 成功'));
}
// 在对应的路径,安装依赖
const needDeps = ['@kevisual/router', '@kevisual/local-app-manager', '@kevisual/use-config'];
let hasPnpm = false;
try {
spawnSync('pnpm', ['--version']);
hasPnpm = true;
} catch (e) {
hasPnpm = false;
}
for (let dep of needDeps) {
console.log(chalk.green('安装依赖', dep));
const npm = hasPnpm ? 'pnpm' : 'npm';
spawnSync(npm, ['install', dep], { cwd: mainAppPath, stdio: 'inherit' });
}
// 创建 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 app = new App();
app.importApp(LocalApp);
const host = config.host || '127.0.0.1';
app.listen(config.port, host, () => {
console.log(\`app 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);
};
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应用')
.action(async (options) => {
if (options.init) {
await initMain();
return;
}
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');
const childProcess = spawn('node', ['dist/app.mjs'], {
cwd: getConfig().mainAppPath,
stdio: ['ignore', logStream, logStream], // 忽略 stdio, 重定向到文件
detached: true, // 使子进程独立运行
});
childProcess.unref(); // 使子进程独立运行
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.stop) {
if (config.mainAppPid) {
// 检查是否有进程
if (checkPid(config.mainAppPid)) {
process.kill(config.mainAppPid);
}
writeConfig({ ...config, mainAppPid: null });
}
}
});
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);