cursor2api / src /utils /proxyLauncher.js
github-actions[bot]
Update from GitHub Actions
0d3f8ee
const { spawn } = require('child_process');
const path = require('path');
const fs = require('fs');
const os = require('os');
const logger = require('./logger');
let mainProxyProcess = null;
let othersProxyProcess = null;
let mainProxyLogStream = null;
let othersProxyLogStream = null;
/**
* 获取当前系统平台
* @returns {string} 平台标识
*/
function detectPlatform() {
const platform = os.platform();
const arch = os.arch();
if (platform === 'win32' && arch === 'x64') {
return 'windows_x64';
} else if (platform === 'linux' && arch === 'x64') {
return 'linux_x64';
} else if ((platform === 'android' || platform === 'linux') && (arch === 'arm64' || arch === 'aarch64')) {
return 'android_arm64';
}
// 默认返回linux版本
logger.warn(`未识别的平台: ${platform} ${arch},将使用linux_x64代理`);
return 'linux_x64';
}
/**
* 获取代理服务器可执行文件路径
* @param {string} platform 平台类型
* @param {string} proxyType 代理类型 ('main' 或 'others')
* @returns {string} 可执行文件路径
*/
function getProxyExecutablePath(platform, proxyType = 'main') {
let proxyDir;
if (proxyType === 'others') {
proxyDir = path.join(process.cwd(), 'src', 'proxy', 'others');
} else {
proxyDir = path.join(process.cwd(), 'src', 'proxy');
}
// 根据平台选择可执行文件
switch (platform) {
case 'windows_x64':
return path.join(proxyDir, 'cursor_proxy_server_windows_amd64.exe');
case 'linux_x64':
return path.join(proxyDir, 'cursor_proxy_server_linux_amd64');
case 'android_arm64':
return path.join(proxyDir, 'cursor_proxy_server_android_arm64');
default:
logger.warn(`未知平台: ${platform},将使用linux_x64代理`);
return path.join(proxyDir, 'cursor_proxy_server_linux_amd64');
}
}
/**
* 创建并打开代理服务器日志文件
* @param {string} platform 平台类型
* @param {string} proxyType 代理类型 ('main' 或 'others')
* @returns {fs.WriteStream} 日志文件写入流
*/
function createProxyLogFile(platform, proxyType = 'main') {
try {
// 确保logs目录存在
const logsDir = path.join(process.cwd(), 'logs');
if (!fs.existsSync(logsDir)) {
fs.mkdirSync(logsDir, { recursive: true });
}
// 创建日志文件名,包含日期和平台信息
const now = new Date();
const dateStr = now.toISOString().split('T')[0];
const logFileName = `proxy_server_${proxyType}_${platform}_${dateStr}.log`;
const logFilePath = path.join(logsDir, logFileName);
// 创建日志文件流
const logStream = fs.createWriteStream(logFilePath, { flags: 'a' });
// 写入日志文件头
const headerLine = `\n\n========== ${proxyType}代理服务器日志 - ${platform} - ${now.toISOString()} ==========\n\n`;
logStream.write(headerLine);
logger.info(`${proxyType}代理服务器详细日志将记录到: ${logFilePath}`);
return logStream;
} catch (error) {
logger.error(`创建${proxyType}代理服务器日志文件失败: ${error.message}`);
return null;
}
}
/**
* 写入日志到代理服务器日志文件
* @param {fs.WriteStream} logStream 日志文件流
* @param {string} message 日志消息
* @param {string} type 日志类型 (stdout 或 stderr)
*/
function writeToProxyLog(logStream, message, type = 'stdout') {
if (!logStream) return;
try {
const timestamp = new Date().toISOString();
const logLine = `[${timestamp}] [${type}] ${message}\n`;
logStream.write(logLine);
} catch (error) {
logger.error(`写入代理服务器日志失败: ${error.message}`);
}
}
/**
* 启动单个代理服务器
* @param {string} platform 平台类型
* @param {string} proxyType 代理类型 ('main' 或 'others')
* @param {number} port 代理服务器端口
* @returns {object} 包含进程和日志流的对象
*/
function startSingleProxyServer(platform, proxyType, port) {
try {
// 获取可执行文件路径
const execPath = getProxyExecutablePath(platform, proxyType);
// 检查文件是否存在
if (!fs.existsSync(execPath)) {
logger.error(`${proxyType}代理服务器可执行文件不存在: ${execPath}`);
return { process: null, logStream: null };
}
// 在Linux/Android上,设置可执行权限
if (platform !== 'windows_x64') {
try {
fs.chmodSync(execPath, '755');
} catch (err) {
logger.warn(`无法设置${proxyType}代理服务器可执行权限: ${err.message}`);
}
}
// 创建代理服务器日志文件
const logStream = createProxyLogFile(platform, proxyType);
// 启动代理服务器进程
logger.info(`正在启动${platform}平台的${proxyType}代理服务器: ${execPath},端口: ${port}`);
// 添加端口参数
const args = port ? [`--port=${port}`] : [];
const proxyProcess = spawn(execPath, args, {
detached: false,
stdio: ['ignore', 'pipe', 'pipe']
});
// 记录代理服务器的详细日志到文件
proxyProcess.stdout.on('data', (data) => {
const output = data.toString().trim();
writeToProxyLog(logStream, output, 'stdout');
});
proxyProcess.stderr.on('data', (data) => {
const errorOutput = data.toString().trim();
writeToProxyLog(logStream, errorOutput, 'stderr');
// 只在启动失败时记录错误信息到控制台
if (!proxyProcess.startSuccessful && errorOutput.includes('error')) {
logger.error(`${proxyType}代理服务器启动错误: ${errorOutput.split('\n')[0]}`);
}
});
proxyProcess.on('error', (err) => {
logger.error(`${proxyType}代理服务器启动失败: ${err.message}`);
writeToProxyLog(logStream, `启动失败: ${err.message}`, 'error');
return { process: null, logStream: null };
});
proxyProcess.on('close', (code) => {
// 只有在非正常退出时记录到控制台
if (code !== 0) {
logger.warn(`${proxyType}代理服务器已退出,代码: ${code}`);
}
writeToProxyLog(logStream, `进程已退出,退出代码: ${code}`, 'info');
// 关闭日志文件
if (logStream) {
logStream.end();
}
});
// 等待一段时间确保启动成功
setTimeout(() => {
if (proxyProcess && proxyProcess.exitCode === null) {
proxyProcess.startSuccessful = true;
logger.info(`${proxyType}代理服务器已成功启动`);
writeToProxyLog(logStream, `${proxyType}代理服务器已成功启动`, 'info');
} else {
logger.error(`${proxyType}代理服务器启动失败或异常退出`);
writeToProxyLog(logStream, `${proxyType}代理服务器启动失败或异常退出`, 'error');
}
}, 1000);
return { process: proxyProcess, logStream };
} catch (error) {
logger.error(`启动${proxyType}代理服务器出错: ${error.message}`);
return { process: null, logStream: null };
}
}
/**
* 启动代理服务器
* @returns {boolean} 是否成功启动
*/
function startProxyServer() {
try {
// 检查是否启用代理
const useTlsProxy = process.env.USE_TLS_PROXY === 'true';
if (!useTlsProxy) {
logger.warn('TLS代理服务器未启用,跳过启动');
return true;
}
// 检查是否启用辅助代理服务器
const useOthersProxy = process.env.USE_OTHERS_PROXY === 'true';
// 确定要使用的平台
let platform = process.env.PROXY_PLATFORM || 'auto';
if (platform === 'auto') {
platform = detectPlatform();
}
// 启动主代理服务器(默认使用8080端口)
const mainProxy = startSingleProxyServer(platform, 'main', 8080);
mainProxyProcess = mainProxy.process;
mainProxyLogStream = mainProxy.logStream;
// 根据配置决定是否启动辅助代理服务器
if (useOthersProxy) {
logger.info('辅助代理服务器已启用,正在启动...');
// 启动others代理服务器(端口 10654)
const othersProxy = startSingleProxyServer(platform, 'others', 10654);
othersProxyProcess = othersProxy.process;
othersProxyLogStream = othersProxy.logStream;
// 如果辅助代理启动失败,记录警告
if (!othersProxyProcess) {
logger.warn('辅助代理服务器启动失败');
} else {
logger.info('辅助代理服务器启动成功');
}
} else {
logger.warn('辅助代理服务器未启用,跳过启动');
}
// 如果主代理启动失败,记录警告
if (!mainProxyProcess) {
logger.warn('主代理服务器启动失败');
return false;
}
return true;
} catch (error) {
logger.error(`启动代理服务器出错: ${error.message}`);
return false;
}
}
/**
* 停止代理服务器
*/
function stopProxyServer() {
const stopSingleProxy = (proxyProcess, logStream, proxyType) => {
if (proxyProcess) {
logger.info(`正在停止${proxyType}代理服务器...`);
writeToProxyLog(logStream, `正在停止${proxyType}代理服务器`, 'info');
// 在Windows上,使用taskkill强制终止
if (os.platform() === 'win32') {
try {
spawn('taskkill', ['/pid', proxyProcess.pid, '/f', '/t']);
} catch (err) {
logger.error(`使用taskkill终止${proxyType}代理进程失败: ${err.message}`);
writeToProxyLog(logStream, `使用taskkill终止${proxyType}代理进程失败: ${err.message}`, 'error');
}
} else {
// 在Linux/Mac上直接kill
proxyProcess.kill('SIGTERM');
}
// 允许一些时间写入最后的日志
setTimeout(() => {
// 关闭日志文件
if (logStream) {
logStream.end();
}
}, 500);
}
};
// 停止主代理服务器
stopSingleProxy(mainProxyProcess, mainProxyLogStream, 'main');
mainProxyProcess = null;
mainProxyLogStream = null;
// 停止others代理服务器
stopSingleProxy(othersProxyProcess, othersProxyLogStream, 'others');
othersProxyProcess = null;
othersProxyLogStream = null;
}
// 导出模块
module.exports = {
startProxyServer,
stopProxyServer
};