149 lines
5.2 KiB
TypeScript
149 lines
5.2 KiB
TypeScript
import { app, assistantConfig } from './app.ts';
|
|
import { proxyRoute, proxyWs } from './services/proxy/proxy-page-index.ts';
|
|
import './routes/index.ts';
|
|
|
|
import getPort, { portNumbers } from 'get-port';
|
|
import { program } from 'commander';
|
|
import { spawnSync } from 'node:child_process';
|
|
import path from 'node:path'
|
|
import chalk from 'chalk';
|
|
import { AssistantApp } from './lib.ts';
|
|
import { getBunPath } from './module/get-bun-path.ts';
|
|
export const runServer = async (port?: number, listenPath = '127.0.0.1') => {
|
|
let _port: number | undefined;
|
|
if (port) {
|
|
_port = await getPort({ port });
|
|
if (_port !== port) {
|
|
console.log(`Port ${port} is not available`);
|
|
_port = undefined;
|
|
}
|
|
}
|
|
if (!_port) {
|
|
// 检车端口可用性
|
|
const isPortAvailable = await getPort({ port: portNumbers(51015, 52000) });
|
|
if (!isPortAvailable) {
|
|
console.log(`Port ${isPortAvailable} is not available`);
|
|
process.exit(1);
|
|
}
|
|
_port = isPortAvailable;
|
|
}
|
|
const hasSocket = listenPath.includes('.sock');
|
|
if (hasSocket) {
|
|
app.listen(listenPath, () => {
|
|
console.log(`Server is running on ${listenPath}`);
|
|
});
|
|
} else {
|
|
app.listen(_port, listenPath, () => {
|
|
const protocol = assistantConfig.getHttps().protocol;
|
|
console.log(`Server is running on ${protocol}://${listenPath}:${_port}`);
|
|
});
|
|
}
|
|
app.server.on(proxyRoute);
|
|
proxyWs();
|
|
const manager = new AssistantApp(assistantConfig, app);
|
|
setTimeout(() => {
|
|
manager.load({ runtime: 'client' }).then(() => {
|
|
console.log('Assistant App Loaded');
|
|
});
|
|
}, 1000);
|
|
return {
|
|
app,
|
|
port: _port,
|
|
};
|
|
};
|
|
program
|
|
.description('启动服务')
|
|
.option('-d, --daemon', '是否以守护进程方式运行')
|
|
.option('-n, --name <name>', '服务名称', 'assistant-server')
|
|
.option('-p, --port <port>', '服务端口')
|
|
.option('-s, --start', '是否启动服务')
|
|
.option('-e, --interpreter <interpreter>', '指定使用的解释器', 'bun')
|
|
.action(async (options) => {
|
|
// console.log('当前执行路径:', execPath, inte);
|
|
if (options.daemon) {
|
|
const [_interpreter, execPath] = process.argv;
|
|
const name = options.name;
|
|
const port = options.port;
|
|
const runPath = path.resolve(execPath);
|
|
|
|
// Windows 下需要对路径进行转义处理
|
|
const escapePath = (p: string) => {
|
|
// 将反斜杠转换为正斜杠,PM2 在 Windows 上更好地支持正斜杠
|
|
let normalized = p.replace(/\\/g, '/');
|
|
// 如果路径包含空格,用引号包裹
|
|
return normalized.includes(' ') ? `"${normalized}"` : normalized;
|
|
};
|
|
|
|
// 获取解释器路径
|
|
let interpreterPath = options.interpreter;
|
|
if (options.interpreter === 'bun') {
|
|
interpreterPath = getBunPath();
|
|
console.log(chalk.gray('Bun 路径:'), interpreterPath);
|
|
}
|
|
|
|
// 构建 pm2 命令字符串
|
|
let pm2Command = `pm2 start ${escapePath(runPath)} --interpreter ${escapePath(interpreterPath)} --name ${name} -- -s`;
|
|
|
|
if (port) {
|
|
pm2Command += ` -p ${port}`;
|
|
}
|
|
|
|
console.log(chalk.gray('执行命令:'), pm2Command);
|
|
console.log(chalk.gray('脚本路径:'), runPath);
|
|
|
|
try {
|
|
// 先删除可能存在的同名进程
|
|
console.log(chalk.yellow('尝试删除旧进程...'));
|
|
spawnSync(`pm2 delete ${name}`, [], {
|
|
shell: true,
|
|
stdio: 'pipe', // 忽略删除时的输出
|
|
});
|
|
|
|
const result = spawnSync(pm2Command, [], {
|
|
stdio: 'inherit',
|
|
shell: true,
|
|
windowsHide: false,
|
|
});
|
|
|
|
if (result.error) {
|
|
console.error(chalk.red('Error starting server:'), result.error.message);
|
|
console.log(chalk.yellow('\n提示: 请检查:'));
|
|
console.log(' 1. pm2 是否已安装: npm install -g pm2');
|
|
console.log(' 2. bun 是否已安装且在 PATH 中');
|
|
console.log(' 3. 尝试手动执行:', pm2Command);
|
|
process.exit(1);
|
|
}
|
|
|
|
if (result.status !== 0) {
|
|
console.error(chalk.red(`PM2 exited with code ${result.status}`));
|
|
console.log(chalk.yellow('\n查看详细日志:'), `pm2 logs ${name}`);
|
|
console.log(chalk.yellow('查看进程状态:'), 'pm2 list');
|
|
process.exit(result.status || 1);
|
|
}
|
|
|
|
console.log(chalk.green('✓ 以守护进程方式运行'));
|
|
console.log(chalk.gray('查看日志:'), `pm2 logs ${name}`);
|
|
} catch (error) {
|
|
console.error(chalk.red('Error starting server:'), error.message);
|
|
process.exit(1);
|
|
}
|
|
process.exit(0);
|
|
} else if (options.start) {
|
|
console.log('启动服务', chalk.green(assistantConfig.configDir));
|
|
const config = assistantConfig.getCacheAssistantConfig();
|
|
const listenPort = options.port || config?.server?.port;
|
|
const listenPath = config?.server?.path || '::';
|
|
const server = await runServer(listenPort, listenPath);
|
|
} else {
|
|
console.log('请使用 -s 参数启动服务');
|
|
}
|
|
});
|
|
|
|
export const runParser = async (argv: string[]) => {
|
|
try {
|
|
program.parse(argv);
|
|
} catch (error) {
|
|
console.error('执行错误:', error.message);
|
|
}
|
|
};
|