isididiidid commited on
Commit
a630600
·
verified ·
1 Parent(s): c129353

Update src/ProxyServer.js

Browse files
Files changed (1) hide show
  1. src/ProxyServer.js +217 -204
src/ProxyServer.js CHANGED
@@ -1,204 +1,217 @@
1
- import { spawn } from 'child_process';
2
- import { fileURLToPath } from 'url';
3
- import { dirname, join } from 'path';
4
- import fs from 'fs';
5
- import os from 'os';
6
- import dotenv from 'dotenv';
7
- import chalk from 'chalk';
8
-
9
- // 获取当前文件的目录路径
10
- const __filename = fileURLToPath(import.meta.url);
11
- const __dirname = dirname(__filename);
12
-
13
- // 加载环境变量
14
- dotenv.config({ path: join(dirname(__dirname), '.env') });
15
-
16
- // 日志配置
17
- const logger = {
18
- info: (message) => console.log(chalk.blue(`[ProxyServer] ${message}`)),
19
- error: (message) => console.error(chalk.red(`[ProxyServer] ${message}`)),
20
- warning: (message) => console.warn(chalk.yellow(`[ProxyServer] ${message}`)),
21
- success: (message) => console.log(chalk.green(`[ProxyServer] ${message}`)),
22
- };
23
-
24
- class ProxyServer {
25
- constructor() {
26
- this.proxyProcess = null;
27
- this.platform = process.env.PROXY_SERVER_PLATFORM || 'auto';
28
- this.port = process.env.PROXY_SERVER_PORT || 10655;
29
- this.logPath = process.env.PROXY_SERVER_LOG_PATH || './proxy_server.log';
30
- this.enabled = process.env.ENABLE_PROXY_SERVER === 'true';
31
- this.proxyAuthToken = process.env.PROXY_AUTH_TOKEN || 'default_token';
32
- this.logStream = null;
33
- }
34
-
35
- // 获取当前系统平台
36
- detectPlatform() {
37
- if (this.platform !== 'auto') {
38
- return this.platform;
39
- }
40
-
41
- const platform = os.platform();
42
- const arch = os.arch();
43
-
44
- if (platform === 'win32') {
45
- return 'windows';
46
- } else if (platform === 'linux') {
47
- if (arch === 'arm64') {
48
- return 'android';
49
- } else {
50
- return 'linux';
51
- }
52
- } else if (platform === 'android') {
53
- return 'android';
54
- } else {
55
- logger.warning(`未知平台: ${platform}, ${arch}, 默认使用linux版本`);
56
- return 'linux';
57
- }
58
- }
59
-
60
- // 获取代理服务器可执行文件路径
61
- getProxyServerPath() {
62
- const platform = this.detectPlatform();
63
- const proxyDir = join(__dirname, 'proxy');
64
-
65
- switch (platform) {
66
- case 'windows':
67
- return join(proxyDir, 'chrome_proxy_server_windows_amd64.exe');
68
- case 'linux':
69
- return join(proxyDir, 'chrome_proxy_server_linux_amd64');
70
- case 'android':
71
- return join(proxyDir, 'chrome_proxy_server_android_arm64');
72
- default:
73
- logger.error(`不支持的平台: ${platform}`);
74
- return null;
75
- }
76
- }
77
-
78
- // 启动代理服务器
79
- async start() {
80
- if (!this.enabled) {
81
- logger.info('代理服务器未启用,跳过启动');
82
- return;
83
- }
84
-
85
- if (this.proxyProcess) {
86
- logger.warning('代理服务器已经在运行中');
87
- return;
88
- }
89
-
90
- const proxyServerPath = this.getProxyServerPath();
91
- if (!proxyServerPath) {
92
- logger.error('无法获取代理服务器路径');
93
- return;
94
- }
95
-
96
- try {
97
- // 确保可执行文件有执行权限(在Linux/Android上)
98
- if (this.detectPlatform() !== 'windows') {
99
- try {
100
- fs.chmodSync(proxyServerPath, 0o755);
101
- } catch (err) {
102
- logger.warning(`无法设置执行权限: ${err.message}`);
103
- }
104
- }
105
-
106
- // 创建日志文件
107
- this.logStream = fs.createWriteStream(this.logPath, { flags: 'a' });
108
-
109
- // 修复 stdio 参数问题
110
- // 启动代理服务器进程
111
- this.proxyProcess = spawn(proxyServerPath, [
112
- '--port', this.port.toString(),
113
- '--token', this.proxyAuthToken
114
- ], {
115
- stdio: ['ignore', 'pipe', 'pipe'], // 使用pipe而不是直接传递流
116
- detached: false
117
- });
118
-
119
- // 将进程的输出重定向到日志文件
120
- if (this.proxyProcess.stdout) {
121
- this.proxyProcess.stdout.pipe(this.logStream);
122
- }
123
-
124
- if (this.proxyProcess.stderr) {
125
- this.proxyProcess.stderr.pipe(this.logStream);
126
- }
127
-
128
- // 设置进程事件处理
129
- this.proxyProcess.on('error', (err) => {
130
- logger.error(`代理服务器启动失败: ${err.message}`);
131
- this.proxyProcess = null;
132
- if (this.logStream) {
133
- this.logStream.end();
134
- this.logStream = null;
135
- }
136
- });
137
-
138
- this.proxyProcess.on('exit', (code, signal) => {
139
- logger.info(`代理服务器已退出,退出码: ${code}, 信号: ${signal}`);
140
- this.proxyProcess = null;
141
- if (this.logStream) {
142
- this.logStream.end();
143
- this.logStream = null;
144
- }
145
- });
146
-
147
- // 等待一段时间,确保服务器启动
148
- await new Promise(resolve => setTimeout(resolve, 1000));
149
-
150
- if (this.proxyProcess && this.proxyProcess.exitCode === null) {
151
- logger.success(`代理服务器已启动,端口: ${this.port}, 日志文件: ${this.logPath}`);
152
- return true;
153
- } else {
154
- logger.error('代理服务器启动失败');
155
- if (this.logStream) {
156
- this.logStream.end();
157
- this.logStream = null;
158
- }
159
- return false;
160
- }
161
- } catch (error) {
162
- logger.error(`启动代理服务器时出错: ${error.message}`);
163
- if (this.logStream) {
164
- this.logStream.end();
165
- this.logStream = null;
166
- }
167
- return false;
168
- }
169
- }
170
-
171
- // 停止代理服务器
172
- stop() {
173
- if (!this.proxyProcess) {
174
- //logger.info('代理服务器已关闭');
175
- return;
176
- }
177
-
178
- try {
179
- // 在Windows上使用taskkill确保子进程也被终止
180
- if (this.detectPlatform() === 'windows' && this.proxyProcess.pid) {
181
- spawn('taskkill', ['/pid', this.proxyProcess.pid, '/f', '/t']);
182
- } else {
183
- // 在Linux/Android上使用kill信号
184
- this.proxyProcess.kill('SIGTERM');
185
- }
186
-
187
- logger.success('代理服务器已停止');
188
- } catch (error) {
189
- logger.error(`停止代理服务器时出错: ${error.message}`);
190
- } finally {
191
- this.proxyProcess = null;
192
- if (this.logStream) {
193
- this.logStream.end();
194
- this.logStream = null;
195
- }
196
- }
197
- }
198
- }
199
-
200
- // 创建单例
201
- const proxyServer = new ProxyServer();
202
-
203
- // 导出
204
- export { proxyServer };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { spawn } from 'child_process';
2
+ import { fileURLToPath } from 'url';
3
+ import { dirname, join } from 'path';
4
+ import fs from 'fs';
5
+ import os from 'os';
6
+ import dotenv from 'dotenv';
7
+ import chalk from 'chalk';
8
+
9
+ // 获取当前文件的目录路径
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = dirname(__filename);
12
+
13
+ // 加载环境变量
14
+ dotenv.config({ path: join(dirname(__dirname), '.env') });
15
+
16
+ // 日志配置
17
+ const logger = {
18
+ info: (message) => console.log(chalk.blue(`[ProxyServer] ${message}`)),
19
+ error: (message) => console.error(chalk.red(`[ProxyServer] ${message}`)),
20
+ warning: (message) => console.warn(chalk.yellow(`[ProxyServer] ${message}`)),
21
+ success: (message) => console.log(chalk.green(`[ProxyServer] ${message}`)),
22
+ };
23
+
24
+ class ProxyServer {
25
+ constructor() {
26
+ this.proxyProcess = null;
27
+ this.platform = process.env.PROXY_SERVER_PLATFORM || 'auto';
28
+ this.port = process.env.PROXY_SERVER_PORT || 10655;
29
+ // ***【修改 1】*** 将日志路径指向 /tmp/logs,这是一个在容器中保证可写的目录
30
+ this.logPath = process.env.PROXY_SERVER_LOG_PATH || '/tmp/logs/proxy_server.log';
31
+ this.enabled = process.env.ENABLE_PROXY_SERVER === 'true';
32
+ this.proxyAuthToken = process.env.PROXY_AUTH_TOKEN || 'default_token';
33
+ this.logStream = null;
34
+ }
35
+
36
+ // 获取当前系统平台
37
+ detectPlatform() {
38
+ if (this.platform !== 'auto') {
39
+ return this.platform;
40
+ }
41
+
42
+ const platform = os.platform();
43
+ const arch = os.arch();
44
+
45
+ if (platform === 'win32') {
46
+ return 'windows';
47
+ } else if (platform === 'linux') {
48
+ // 这里的逻辑保持不变,因为 Docker 容器通常是 linux amd64
49
+ if (arch === 'arm64') {
50
+ return 'android'; // 或者 'linux_arm64' 如果你有这个二进制
51
+ } else {
52
+ return 'linux';
53
+ }
54
+ } else if (platform === 'android') {
55
+ return 'android';
56
+ } else {
57
+ logger.warning(`未知平台: ${platform}, ${arch}, 默认使用linux版本`);
58
+ return 'linux';
59
+ }
60
+ }
61
+
62
+ // 获取代理服务器可执行文件路径
63
+ getProxyServerPath() {
64
+ const platform = this.detectPlatform();
65
+
66
+ // ***【修改 2】*** 在生产环境中(如Docker),从 /tmp/proxy 获取二进制文件
67
+ // 在本地开发时,仍然从 src/proxy 获取
68
+ const isProduction = process.env.NODE_ENV === 'production';
69
+ const proxyDir = isProduction ? '/tmp/proxy' : join(__dirname, 'proxy');
70
+
71
+ logger.info(`环境: ${isProduction ? 'Production' : 'Development'}, 代理二进制目录: ${proxyDir}`);
72
+
73
+ switch (platform) {
74
+ case 'windows':
75
+ return join(proxyDir, 'chrome_proxy_server_windows_amd64.exe');
76
+ case 'linux':
77
+ return join(proxyDir, 'chrome_proxy_server_linux_amd64');
78
+ case 'android':
79
+ return join(proxyDir, 'chrome_proxy_server_android_arm64');
80
+ default:
81
+ logger.error(`不支持的平台: ${platform}`);
82
+ return null;
83
+ }
84
+ }
85
+
86
+ // 启动代理服务器
87
+ async start() {
88
+ if (!this.enabled) {
89
+ logger.info('代理服务器未启用,跳过启动');
90
+ return;
91
+ }
92
+
93
+ if (this.proxyProcess) {
94
+ logger.warning('代理服务器已经在运行中');
95
+ return;
96
+ }
97
+
98
+ const proxyServerPath = this.getProxyServerPath();
99
+ if (!proxyServerPath) {
100
+ logger.error('无法获取代理服务器路径');
101
+ return;
102
+ }
103
+
104
+ // 检查文件是否存在,提供更详细的调试信息
105
+ if (!fs.existsSync(proxyServerPath)) {
106
+ logger.error(`代理二进制文件不存在于路径: ${proxyServerPath}`);
107
+ return;
108
+ }
109
+
110
+ try {
111
+ // 在生产环境中,Dockerfile 已经处理了权限问题,本地开发时���以尝试设置
112
+ if (process.env.NODE_ENV !== 'production' && this.detectPlatform() !== 'windows') {
113
+ try {
114
+ fs.chmodSync(proxyServerPath, 0o755);
115
+ } catch (err) {
116
+ logger.warning(`本地开发环境无法设置执行权限: ${err.message}`);
117
+ }
118
+ }
119
+
120
+ // 创建日志文件
121
+ this.logStream = fs.createWriteStream(this.logPath, { flags: 'a' });
122
+
123
+ // 启动代理服务器进程
124
+ this.proxyProcess = spawn(proxyServerPath, [
125
+ '--port', this.port.toString(),
126
+ '--token', this.proxyAuthToken
127
+ ], {
128
+ stdio: ['ignore', 'pipe', 'pipe'], // 使用 pipe 而不是直接传递流
129
+ detached: false
130
+ });
131
+
132
+ // 将进程的输出重定向到日志文件
133
+ if (this.proxyProcess.stdout) {
134
+ this.proxyProcess.stdout.pipe(this.logStream);
135
+ }
136
+
137
+ if (this.proxyProcess.stderr) {
138
+ this.proxyProcess.stderr.pipe(this.logStream);
139
+ }
140
+
141
+ // 设置进程事件处理
142
+ this.proxyProcess.on('error', (err) => {
143
+ logger.error(`代理服务器启动失败: ${err.message}`);
144
+ this.proxyProcess = null;
145
+ if (this.logStream) {
146
+ this.logStream.end();
147
+ this.logStream = null;
148
+ }
149
+ });
150
+
151
+ this.proxyProcess.on('exit', (code, signal) => {
152
+ logger.info(`代理服务器已退出,退出码: ${code}, 信号: ${signal}`);
153
+ this.proxyProcess = null;
154
+ if (this.logStream) {
155
+ this.logStream.end();
156
+ this.logStream = null;
157
+ }
158
+ });
159
+
160
+ // 等待一段时间,确保服务器启动
161
+ await new Promise(resolve => setTimeout(resolve, 1000));
162
+
163
+ if (this.proxyProcess && this.proxyProcess.exitCode === null) {
164
+ logger.success(`代理服务器已启动,端口: ${this.port}, 日志文件: ${this.logPath}`);
165
+ return true;
166
+ } else {
167
+ logger.error('代理服务器启动失败或立即退出。请检查日志。');
168
+ if (this.logStream) {
169
+ this.logStream.end();
170
+ this.logStream = null;
171
+ }
172
+ return false;
173
+ }
174
+ } catch (error) {
175
+ logger.error(`启动代理服务器时出错: ${error.message}`);
176
+ if (this.logStream) {
177
+ this.logStream.end();
178
+ this.logStream = null;
179
+ }
180
+ return false;
181
+ }
182
+ }
183
+
184
+ // 停止代理服务器
185
+ stop() {
186
+ if (!this.proxyProcess) {
187
+ //logger.info('代理服务器已关闭');
188
+ return;
189
+ }
190
+
191
+ try {
192
+ // 在Windows上使用taskkill确保子进程也被终止
193
+ if (this.detectPlatform() === 'windows' && this.proxyProcess.pid) {
194
+ spawn('taskkill', ['/pid', this.proxyProcess.pid, '/f', '/t']);
195
+ } else {
196
+ // 在Linux/Android上使用kill信号
197
+ this.proxyProcess.kill('SIGTERM');
198
+ }
199
+
200
+ logger.success('代理服务器已停止');
201
+ } catch (error) {
202
+ logger.error(`停止代理服务器时出错: ${error.message}`);
203
+ } finally {
204
+ this.proxyProcess = null;
205
+ if (this.logStream) {
206
+ this.logStream.end();
207
+ this.logStream = null;
208
+ }
209
+ }
210
+ }
211
+ }
212
+
213
+ // 创建单例
214
+ const proxyServer = new ProxyServer();
215
+
216
+ // 导出
217
+ export { proxyServer };