Spaces:
Sleeping
Sleeping
import { spawn } from 'child_process'; | |
import { fileURLToPath } from 'url'; | |
import { dirname, join } from 'path'; | |
import fs from 'fs'; | |
import os from 'os'; | |
import dotenv from 'dotenv'; | |
import chalk from 'chalk'; | |
// 获取当前文件的目录路径 | |
const __filename = fileURLToPath(import.meta.url); | |
const __dirname = dirname(__filename); | |
// 加载环境变量 | |
dotenv.config({ path: join(dirname(__dirname), '.env') }); | |
// 日志配置 | |
const logger = { | |
info: (message) => console.log(chalk.blue(`[ProxyServer] ${message}`)), | |
error: (message) => console.error(chalk.red(`[ProxyServer] ${message}`)), | |
warning: (message) => console.warn(chalk.yellow(`[ProxyServer] ${message}`)), | |
success: (message) => console.log(chalk.green(`[ProxyServer] ${message}`)), | |
}; | |
class ProxyServer { | |
constructor() { | |
this.proxyProcess = null; | |
this.platform = process.env.PROXY_SERVER_PLATFORM || 'auto'; | |
this.port = process.env.PROXY_SERVER_PORT || 10655; | |
this.logPath = process.env.PROXY_SERVER_LOG_PATH || '/tmp/proxy_server.log'; | |
this.enabled = process.env.ENABLE_PROXY_SERVER === 'true'; | |
this.proxyAuthToken = process.env.PROXY_AUTH_TOKEN || 'default_token'; | |
this.logStream = null; | |
} | |
// 获取当前系统平台 | |
detectPlatform() { | |
if (this.platform !== 'auto') { | |
return this.platform; | |
} | |
const platform = os.platform(); | |
const arch = os.arch(); | |
if (platform === 'win32') { | |
return 'windows'; | |
} else if (platform === 'linux') { | |
if (arch === 'arm64') { | |
return 'android'; | |
} else { | |
return 'linux'; | |
} | |
} else if (platform === 'android') { | |
return 'android'; | |
} else { | |
logger.warning(`未知平台: ${platform}, ${arch}, 默认使用linux版本`); | |
return 'linux'; | |
} | |
} | |
// 获取代理服务器可执行文件路径 | |
getProxyServerPath() { | |
const platform = this.detectPlatform(); | |
const proxyDir = join(__dirname, 'proxy'); | |
switch (platform) { | |
case 'windows': | |
return join(proxyDir, 'chrome_proxy_server_windows_amd64.exe'); | |
case 'linux': | |
return join(proxyDir, 'chrome_proxy_server_linux_amd64'); | |
case 'android': | |
return join(proxyDir, 'chrome_proxy_server_android_arm64'); | |
default: | |
logger.error(`不支持的平台: ${platform}`); | |
return null; | |
} | |
} | |
// 启动代理服务器 | |
async start() { | |
if (!this.enabled) { | |
logger.info('代理服务器未启用,跳过启动'); | |
return; | |
} | |
if (this.proxyProcess) { | |
logger.warning('代理服务器已经在运行中'); | |
return; | |
} | |
const proxyServerPath = this.getProxyServerPath(); | |
if (!proxyServerPath) { | |
logger.error('无法获取代理服务器路径'); | |
return; | |
} | |
try { | |
// 确保可执行文件有执行权限(在Linux/Android上) | |
// 在Hugging Face环境中,文件权限应在Dockerfile中设置,运行时修改可能会失败 | |
// if (this.detectPlatform() !== 'windows') { | |
// try { | |
// fs.chmodSync(proxyServerPath, 0o755); | |
// } catch (err) { | |
// logger.warning(`无法设置执行权限: ${err.message}`); | |
// } | |
// } | |
// 创建日志文件 | |
this.logStream = fs.createWriteStream(this.logPath, { flags: 'a' }); | |
// 修复 stdio 参数问题 | |
// 启动代理服务器进程 | |
this.proxyProcess = spawn(proxyServerPath, [ | |
'--port', this.port.toString(), | |
'--token', this.proxyAuthToken | |
], { | |
stdio: ['ignore', 'pipe', 'pipe'], // 使用pipe而不是直接传递流 | |
detached: false | |
}); | |
// 将进程的输出重定向到日志文件 | |
if (this.proxyProcess.stdout) { | |
this.proxyProcess.stdout.pipe(this.logStream); | |
} | |
if (this.proxyProcess.stderr) { | |
this.proxyProcess.stderr.pipe(this.logStream); | |
} | |
// 设置进程事件处理 | |
this.proxyProcess.on('error', (err) => { | |
logger.error(`代理服务器启动失败: ${err.message}`); | |
this.proxyProcess = null; | |
if (this.logStream) { | |
this.logStream.end(); | |
this.logStream = null; | |
} | |
}); | |
this.proxyProcess.on('exit', (code, signal) => { | |
logger.info(`代理服务器已退出,退出码: ${code}, 信号: ${signal}`); | |
this.proxyProcess = null; | |
if (this.logStream) { | |
this.logStream.end(); | |
this.logStream = null; | |
} | |
}); | |
// 等待一段时间,确保服务器启动 | |
await new Promise(resolve => setTimeout(resolve, 1000)); | |
if (this.proxyProcess && this.proxyProcess.exitCode === null) { | |
logger.success(`代理服务器已启动,端口: ${this.port}, 日志文件: ${this.logPath}`); | |
return true; | |
} else { | |
logger.error('代理服务器启动失败'); | |
if (this.logStream) { | |
this.logStream.end(); | |
this.logStream = null; | |
} | |
return false; | |
} | |
} catch (error) { | |
logger.error(`启动代理服务器时出错: ${error.message}`); | |
if (this.logStream) { | |
this.logStream.end(); | |
this.logStream = null; | |
} | |
return false; | |
} | |
} | |
// 停止代理服务器 | |
stop() { | |
if (!this.proxyProcess) { | |
//logger.info('代理服务器已关闭'); | |
return; | |
} | |
try { | |
// 在Windows上使用taskkill确保子进程也被终止 | |
if (this.detectPlatform() === 'windows' && this.proxyProcess.pid) { | |
spawn('taskkill', ['/pid', this.proxyProcess.pid, '/f', '/t']); | |
} else { | |
// 在Linux/Android上使用kill信号 | |
this.proxyProcess.kill('SIGTERM'); | |
} | |
logger.success('代理服务器已停止'); | |
} catch (error) { | |
logger.error(`停止代理服务器时出错: ${error.message}`); | |
} finally { | |
this.proxyProcess = null; | |
if (this.logStream) { | |
this.logStream.end(); | |
this.logStream = null; | |
} | |
} | |
} | |
} | |
// 创建单例 | |
const proxyServer = new ProxyServer(); | |
// 导出 | |
export { proxyServer }; |