block-proxy 0.1.12 → 0.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/.claude/settings.local.json +33 -1
  2. package/.claude/skills/build-client/skill.md +24 -0
  3. package/.claude/skills/release-client/skill.md +68 -0
  4. package/CLAUDE.md +69 -67
  5. package/Dockerfile +1 -1
  6. package/README.md +38 -24
  7. package/build/asset-manifest.json +6 -6
  8. package/build/index.html +1 -1
  9. package/build/static/css/main.3f317ce6.css +2 -0
  10. package/build/static/css/main.3f317ce6.css.map +1 -0
  11. package/build/static/js/{main.2247fb80.js → main.af1923ea.js} +3 -3
  12. package/build/static/js/main.af1923ea.js.map +1 -0
  13. package/client/app.py +312 -0
  14. package/client/build.sh +84 -0
  15. package/client/config.py +49 -0
  16. package/client/config_window.py +155 -0
  17. package/client/icons/app.icns +0 -0
  18. package/client/icons/app_example.png +0 -0
  19. package/client/icons/app_icon.png +0 -0
  20. package/client/icons/backup/app_example.png +0 -0
  21. package/client/icons/backup/christmas-sock_dark.png +0 -0
  22. package/client/icons/backup/christmas-sock_light.png +0 -0
  23. package/client/icons/backup/socks_on_G.png +0 -0
  24. package/client/icons/backup/socks_on_M.png +0 -0
  25. package/client/icons/christmas-sock_dark.png +0 -0
  26. package/client/icons/christmas-sock_light.png +0 -0
  27. package/client/icons/christmas-sock_light_bar.png +0 -0
  28. package/client/icons/socks_on_G.png +0 -0
  29. package/client/icons/socks_on_G_bar.png +0 -0
  30. package/client/icons/socks_on_M.png +0 -0
  31. package/client/icons/socks_on_M_bar.png +0 -0
  32. package/client/main.py +28 -0
  33. package/client/proxy_core.py +475 -0
  34. package/client/requirements.txt +3 -0
  35. package/client/scripts/download_xray.sh +30 -0
  36. package/client/setup.py +30 -0
  37. package/client/system_proxy.py +94 -0
  38. package/client/tests/__init__.py +0 -0
  39. package/client/tests/test_config.py +72 -0
  40. package/client/tests/test_system_proxy.py +69 -0
  41. package/client/watch-icons.js +31 -0
  42. package/config.json +4 -199
  43. package/docs/superpowers/plans/2026-05-27-blockproxyclient.md +1274 -0
  44. package/docs/superpowers/specs/2026-05-27-blockproxyclient-design.md +264 -0
  45. package/package.json +11 -5
  46. package/proxy/proxy.js +19 -34
  47. package/server/express.js +17 -1
  48. package/src/App.css +596 -276
  49. package/src/App.js +25 -32
  50. package/src/index.css +3 -4
  51. package/test/lib/mock-server.js +133 -0
  52. package/test/proxy-tests.js +708 -0
  53. package/test/run.js +330 -0
  54. package/build/static/css/main.8bfa3d5f.css +0 -2
  55. package/build/static/css/main.8bfa3d5f.css.map +0 -1
  56. package/build/static/js/main.2247fb80.js.map +0 -1
  57. package/hack-of-anyproxy/lib/requestHandler.js +0 -1060
  58. /package/build/static/js/{main.2247fb80.js.LICENSE.txt → main.af1923ea.js.LICENSE.txt} +0 -0
@@ -0,0 +1,264 @@
1
+ # BlockProxyClient macOS 桌面客户端设计
2
+
3
+ ## 概述
4
+
5
+ BlockProxyClient 是一个 macOS 状态栏应用,用于连接 block-proxy 服务端的 SOCKS5 over TLS 代理服务。通过 xray-core 作为本地转发核心,提供本地 SOCKS5 和 HTTP 代理端口,并可选自动设置系统全局代理。
6
+
7
+ ## 技术选型
8
+
9
+ | 层次 | 技术 | 理由 |
10
+ |------|------|------|
11
+ | UI 框架 | Python + rumps | 极简 macOS 状态栏库,几十行代码即可实现菜单+窗口 |
12
+ | 转发核心 | xray-core | 成熟稳定,原生支持 SOCKS5 over TLS、本地 SOCKS/HTTP inbound、UDP |
13
+ | 系统代理 | networksetup CLI | macOS 原生命令,无需额外依赖 |
14
+ | 配置窗口 | tkinter | 多字段表单需要完整窗口控件(rumps.Window 仅支持单行输入) |
15
+ | 打包 | py2app | 打包为 .app,内嵌 xray-core 二进制 |
16
+
17
+ ## 目录结构
18
+
19
+ ```
20
+ client/
21
+ ├── main.py # 入口,启动 rumps 状态栏应用
22
+ ├── app.py # BlockProxyClient 主类(rumps.App 子类)
23
+ ├── config.py # 节点配置管理(读写 JSON)
24
+ ├── xray_manager.py # xray-core 进程管理(启动/停止/生成配置)
25
+ ├── system_proxy.py # macOS 系统代理设置(networksetup 封装)
26
+ ├── resources/
27
+ │ ├── icon.png # 状态栏图标(已连接)
28
+ │ ├── icon_off.png # 状态栏图标(未连接)
29
+ │ └── xray-core # 内嵌的 xray-core 二进制(开发阶段可从 PATH 获取)
30
+ ├── requirements.txt # rumps, py2app
31
+ └── setup.py # py2app 打包配置
32
+ ```
33
+
34
+ ## UI 设计
35
+
36
+ ### 状态栏菜单
37
+
38
+ ```
39
+ [图标] BlockProxyClient
40
+ ├── ● 开启代理 / ○ 关闭代理 (点击切换状态)
41
+ ├── 节点配置... (打开配置窗口)
42
+ ├── ─────────────
43
+ ├── ◉ 全局代理 (自动设置系统 SOCKS/HTTP 代理)
44
+ ├── ○ 手动模式 (仅启动本地监听,不修改系统设置)
45
+ ├── ─────────────
46
+ └── 退出
47
+ ```
48
+
49
+ ### 状态栏图标
50
+
51
+ - 未连接:灰色图标
52
+ - 已连接:黑色/彩色图标
53
+
54
+ ### 节点配置窗口
55
+
56
+ | 字段 | 类型 | 默认值 | 说明 |
57
+ |------|------|--------|------|
58
+ | 地址 | 文本输入 | 空 | 服务器 IP 或域名 |
59
+ | 端口 | 数字输入 | 8002 | 服务端 SOCKS5 over TLS 端口 |
60
+ | 用户名 | 文本输入 | 空 | SOCKS5 认证用户名 |
61
+ | 密码 | 密码输入 | 空 | SOCKS5 认证密码 |
62
+ | 启用 TLS | 复选框 | 开启 | 是否使用 TLS 加密连接 |
63
+ | allowInsecure | 下拉 | true | 是否允许不安全证书(自签证书需设为 true) |
64
+ | 本地 SOCKS 监听端口 | 数字输入 | 1080 | 本地 SOCKS5 代理端口 |
65
+ | 本地 HTTP 监听端口 | 数字输入 | 1087 | 本地 HTTP 代理端口 |
66
+ | 启用 UDP | 复选框 | 开启 | 是否启用 UDP 转发 |
67
+
68
+ ## 核心模块设计
69
+
70
+ ### config.py - 配置管理
71
+
72
+ 配置文件位置:`~/Library/Application Support/BlockProxyClient/config.json`
73
+
74
+ ```json
75
+ {
76
+ "server": {
77
+ "address": "",
78
+ "port": 8002,
79
+ "username": "",
80
+ "password": "",
81
+ "tls": true,
82
+ "allowInsecure": true
83
+ },
84
+ "local": {
85
+ "socks_port": 1080,
86
+ "http_port": 1087,
87
+ "udp": true
88
+ },
89
+ "mode": "global"
90
+ }
91
+ ```
92
+
93
+ 功能:
94
+ - 读取/保存配置到 JSON 文件
95
+ - 配置变更后通知其他模块(用于重启 xray)
96
+ - 首次运行创建默认配置
97
+
98
+ ### xray_manager.py - xray-core 进程管理
99
+
100
+ 职责:
101
+ 1. 根据用户配置生成 xray JSON 配置文件
102
+ 2. 启动/停止 xray-core 子进程
103
+ 3. 监控进程状态(意外退出时通知 UI)
104
+
105
+ 生成的 xray 配置结构:
106
+
107
+ ```json
108
+ {
109
+ "inbounds": [
110
+ {
111
+ "tag": "socks-in",
112
+ "port": "<local.socks_port>",
113
+ "listen": "127.0.0.1",
114
+ "protocol": "socks",
115
+ "settings": { "udp": "<local.udp>" }
116
+ },
117
+ {
118
+ "tag": "http-in",
119
+ "port": "<local.http_port>",
120
+ "listen": "127.0.0.1",
121
+ "protocol": "http"
122
+ }
123
+ ],
124
+ "outbounds": [
125
+ {
126
+ "protocol": "socks",
127
+ "settings": {
128
+ "servers": [{
129
+ "address": "<server.address>",
130
+ "port": "<server.port>",
131
+ "users": [{ "user": "<server.username>", "pass": "<server.password>" }]
132
+ }]
133
+ },
134
+ "streamSettings": {
135
+ "network": "tcp",
136
+ "security": "<tls|none>",
137
+ "tlsSettings": {
138
+ "allowInsecure": "<server.allowInsecure>",
139
+ "serverName": "<server.address>"
140
+ }
141
+ }
142
+ }
143
+ ]
144
+ }
145
+ ```
146
+
147
+ 进程管理:
148
+ - 启动:`subprocess.Popen([xray_path, "run", "-c", config_path], stdout=PIPE, stderr=PIPE)`
149
+ - 停止:`process.terminate()` → 等待 3 秒 → `process.kill()`
150
+ - xray-core 路径:开发时从 PATH 查找,打包后从 .app bundle Resources 目录读取
151
+ - 健康检查:后台线程定期 poll 进程状态
152
+
153
+ ### system_proxy.py - macOS 系统代理
154
+
155
+ 通过 `networksetup` 命令操作:
156
+
157
+ 开启全局代理:
158
+ ```bash
159
+ # 自动检测活跃网络接口(Wi-Fi / Ethernet / etc.)
160
+ networksetup -setsocksfirewallproxy "<interface>" 127.0.0.1 <socks_port>
161
+ networksetup -setsocksfirewallproxystate "<interface>" on
162
+ networksetup -setwebproxy "<interface>" 127.0.0.1 <http_port>
163
+ networksetup -setwebproxystate "<interface>" on
164
+ networksetup -setsecurewebproxy "<interface>" 127.0.0.1 <http_port>
165
+ networksetup -setsecurewebproxystate "<interface>" on
166
+ ```
167
+
168
+ 关闭全局代理:
169
+ ```bash
170
+ networksetup -setsocksfirewallproxystate "<interface>" off
171
+ networksetup -setwebproxystate "<interface>" off
172
+ networksetup -setsecurewebproxystate "<interface>" off
173
+ ```
174
+
175
+ 安全机制:
176
+ - `atexit` 注册清除代理的回调,防止程序崩溃后代理残留
177
+ - 信号处理(SIGTERM, SIGINT)也清除代理
178
+
179
+ 网络接口检测:
180
+ - 通过 `networksetup -listallnetworkservices` 获取所有接口
181
+ - 通过 `networksetup -getinfo <interface>` 判断哪些接口活跃
182
+ - 对所有活跃接口设置代理
183
+
184
+ ### app.py - 主应用
185
+
186
+ rumps.App 子类,负责:
187
+ 1. 状态栏菜单渲染和事件处理
188
+ 2. 协调 config / xray_manager / system_proxy 三个模块
189
+ 3. 维护全局状态(是否已连接、当前模式)
190
+
191
+ 状态机:
192
+ ```
193
+ 初始 → [用户点击开启] → 读取配置 → 生成 xray config → 启动 xray → (全局代理模式?) → 设置系统代理 → 已连接
194
+ 已连接 → [用户点击关闭] → 清除系统代理 → 停止 xray → 未连接
195
+ 已连接 → [切换模式] → 设置/清除系统代理(xray 不重启)
196
+ 已连接 → [xray 意外退出] → 清除系统代理 → 更新 UI 为未连接
197
+ ```
198
+
199
+ ## 打包与分发
200
+
201
+ ### py2app 配置
202
+
203
+ ```python
204
+ # setup.py
205
+ from setuptools import setup
206
+
207
+ APP = ['main.py']
208
+ DATA_FILES = [('resources', ['resources/xray-core', 'resources/icon.png', 'resources/icon_off.png'])]
209
+ OPTIONS = {
210
+ 'argv_emulation': False,
211
+ 'iconfile': 'resources/app_icon.icns',
212
+ 'plist': {
213
+ 'LSUIElement': True,
214
+ 'CFBundleName': 'BlockProxyClient',
215
+ 'CFBundleIdentifier': 'com.jaylli.blockproxyclient',
216
+ },
217
+ 'packages': ['rumps'],
218
+ }
219
+
220
+ setup(
221
+ app=APP,
222
+ data_files=DATA_FILES,
223
+ options={'py2app': OPTIONS},
224
+ setup_requires=['py2app'],
225
+ )
226
+ ```
227
+
228
+ - `LSUIElement: True`:无 Dock 图标,仅状态栏显示
229
+ - xray-core 二进制内嵌在 .app/Contents/Resources/ 中
230
+ - 构建命令:`python setup.py py2app`
231
+
232
+ ### xray-core 获取
233
+
234
+ 开发阶段:
235
+ - 从 xray-core GitHub Releases 下载对应平台二进制
236
+ - 放入 `client/resources/xray-core`
237
+ - 确保有执行权限
238
+
239
+ ## 依赖清单
240
+
241
+ ```
242
+ # requirements.txt
243
+ rumps>=0.4.0
244
+ py2app>=0.28
245
+ ```
246
+
247
+ 运行时无其他 Python 依赖(xray-core 是独立二进制)。
248
+
249
+ ## 与服务端的协议兼容性
250
+
251
+ 客户端通过 xray-core 的 SOCKS outbound + TLS streamSettings 连接服务端,协议流程:
252
+
253
+ ```
254
+ 本地应用 → 本地 SOCKS5/HTTP (xray inbound)
255
+ → xray outbound: TLS 握手 → SOCKS5 认证 (username/password)
256
+ → SOCKS5 CONNECT/UDP ASSOCIATE
257
+ → 服务端 (socks5/server.js) → 下游 HTTP 代理 → 目标
258
+ ```
259
+
260
+ 兼容性要点:
261
+ - 服务端使用自签证书 → 客户端 `allowInsecure: true`
262
+ - 服务端认证方式:SOCKS5 username/password (RFC 1929)
263
+ - 服务端支持 TCP CONNECT 和 UDP ASSOCIATE
264
+ - TLS 最低版本:TLSv1.2(服务端设定)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "block-proxy",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "description": "Small-scale network mitm proxy filter",
5
5
  "bin": {
6
6
  "block-proxy": "bin/start.js"
@@ -17,7 +17,7 @@
17
17
  },
18
18
  "scripts": {
19
19
  "cp": "echo '----- program start -----'",
20
- "rm_bkconfig": "rm ./config_backup.json",
20
+ "rm_bkconfig": "rm -f ./config_backup.json",
21
21
  "craco": "craco start",
22
22
  "dev": "BLOCK_PROXY_DEV=1 npm run express",
23
23
  "start": "npm run express",
@@ -25,13 +25,19 @@
25
25
  "express": "npm run cp && node ./server/start.js",
26
26
  "proxy": "npm run cp && node ./proxy/start.js",
27
27
  "build": "npm run rm_bkconfig && react-scripts build",
28
- "docker:build": "npm run build && docker build -t block-proxy .",
29
- "docker:build_arm": "npm run build && docker buildx build --platform linux/arm64/v8 -t block-proxy .",
28
+ "docker:build": "npm run build && docker buildx build --platform linux/amd64 --load -t block-proxy:amd64 .",
29
+ "docker:build:arm": "npm run build && docker buildx build --platform linux/arm64 --load -t block-proxy:arm64 .",
30
+ "docker:push": "npm run build && docker buildx build --platform linux/amd64 --push -t crpi-x1zji86f6jpcd7t1.cn-hangzhou.personal.cr.aliyuncs.com/lijing00333/block-proxy:latest-amd64 . && docker buildx build --platform linux/arm64 --push -t crpi-x1zji86f6jpcd7t1.cn-hangzhou.personal.cr.aliyuncs.com/lijing00333/block-proxy:latest-arm64 . && docker manifest create crpi-x1zji86f6jpcd7t1.cn-hangzhou.personal.cr.aliyuncs.com/lijing00333/block-proxy:latest crpi-x1zji86f6jpcd7t1.cn-hangzhou.personal.cr.aliyuncs.com/lijing00333/block-proxy:latest-amd64 crpi-x1zji86f6jpcd7t1.cn-hangzhou.personal.cr.aliyuncs.com/lijing00333/block-proxy:latest-arm64 && docker manifest push crpi-x1zji86f6jpcd7t1.cn-hangzhou.personal.cr.aliyuncs.com/lijing00333/block-proxy:latest",
31
+ "docker:push:amd64": "npm run build && docker buildx build --platform linux/amd64 --push -t crpi-x1zji86f6jpcd7t1.cn-hangzhou.personal.cr.aliyuncs.com/lijing00333/block-proxy:latest-amd64 .",
32
+ "docker:push:arm64": "npm run build && docker buildx build --platform linux/arm64 --push -t crpi-x1zji86f6jpcd7t1.cn-hangzhou.personal.cr.aliyuncs.com/lijing00333/block-proxy:latest-arm64 .",
30
33
  "test": "react-scripts test",
34
+ "gen-icons": "node client/watch-icons.js",
35
+ "watch:icons": "node client/watch-icons.js --watch",
36
+ "test:proxy": "node test/run.js",
31
37
  "eject": "react-scripts eject"
32
38
  },
33
39
  "devDependencies": {
34
- "@bachi/anyproxy": "^0.1.5",
40
+ "@bachi/anyproxy": "^0.1.6",
35
41
  "@craco/craco": "^7.1.0",
36
42
  "axios": "^1.13.2",
37
43
  "commander": "^14.0.2",
package/proxy/proxy.js CHANGED
@@ -42,7 +42,7 @@ var anyproxy_started = false;
42
42
  var blockHosts = [];
43
43
  var proxyPort = 8001; // http 代理端口
44
44
  var socks5Port = 8002; // socks5 端口
45
- var webInterfacePort = 8003; // anyproxy 监控端口
45
+
46
46
  var vpn_proxy = "";
47
47
  var devices = [];
48
48
  var progress_time_stamp = "";
@@ -57,7 +57,8 @@ var is_running_in_docker = false;
57
57
  var docker_host_IP = '';
58
58
  var enable_express = "1"; // "0", "1"
59
59
  var enable_socks5 = "1";
60
- var enable_webinterface = "1"; // "0", "1"
60
+
61
+ var enable_mitm = "1"; // "0", "1",是否对 HTTPS 启用 MITM 解密(关闭后纯隧道转发,不拦截)
61
62
  // 域名判断,区分浏览器和 App
62
63
  var filtered_mitm_domains = [
63
64
  ...uaFilter.filtered_mitm_domains
@@ -217,7 +218,6 @@ async function loadConfig() {
217
218
  progress_time_stamp: progress_time_stamp,
218
219
  block_hosts: blockHosts,
219
220
  proxy_port: proxyPort,
220
- web_interface_port: webInterfacePort,
221
221
  your_domain: your_domain,
222
222
  vpn_proxy:"",
223
223
  auth_username:"",
@@ -225,7 +225,6 @@ async function loadConfig() {
225
225
  enable_express: enable_express,
226
226
  enable_socks5: enable_socks5,
227
227
  socks5_port: socks5Port,
228
- enable_webinterface: enable_webinterface,
229
228
  devices: []
230
229
  };
231
230
 
@@ -276,8 +275,8 @@ async function loadConfig() {
276
275
  enable_socks5 = loadedConfig.enable_socks5;
277
276
  config.enable_socks5 = enable_socks5;
278
277
 
279
- enable_webinterface = loadedConfig.enable_webinterface;
280
- config.enable_webinterface = enable_webinterface;
278
+ enable_mitm = loadedConfig.enable_mitm || "1"; // 默认开启,兼容旧配置文件缺少此字段
279
+ config.enable_mitm = enable_mitm;
281
280
 
282
281
  socks5Port = loadedConfig.socks5_port;
283
282
  config.socks5_port = socks5Port;
@@ -290,11 +289,6 @@ async function loadConfig() {
290
289
  config.proxy_port = proxyPort;
291
290
  }
292
291
 
293
- if (loadedConfig.web_interface_port) {
294
- webInterfacePort = loadedConfig.web_interface_port;
295
- config.web_interface_port = webInterfacePort;
296
- }
297
-
298
292
  if (loadedConfig.devices) {
299
293
  devices = loadedConfig.devices;
300
294
  config.devices = devices;
@@ -307,14 +301,13 @@ async function loadConfig() {
307
301
  progress_time_stamp: progress_time_stamp,
308
302
  block_hosts: blockHosts,
309
303
  proxy_port: proxyPort,
310
- web_interface_port: webInterfacePort,
311
304
  auth_password:"",
312
305
  auth_username:"",
313
306
  enable_express: enable_express,
314
307
  your_domain: your_domain,
315
308
  socks5_port: socks5Port,
316
309
  enable_socks5: enable_socks5,
317
- enable_webinterface: enable_webinterface,
310
+ enable_mitm: enable_mitm,
318
311
  vpn_proxy: ""
319
312
  });
320
313
  // fs.writeFileSync(configPath, JSON.stringify({
@@ -518,9 +511,6 @@ function startProxyServer() {
518
511
 
519
512
  proxyServerInstance.on('ready', () => {
520
513
  console.log(`✅ \x1b[32mHTTP 代理服务启动,IP: ${localIp}, 端口: ${proxyPort}\x1b[0m`);
521
- if (enable_webinterface == "1") {
522
- console.log(`✅ \x1b[32mAnyProxy 监控面板启动,http://${localIp}:${webInterfacePort}\x1b[0m`);
523
- }
524
514
  });
525
515
 
526
516
  proxyServerInstance.on('error', (e) => {
@@ -1086,30 +1076,29 @@ function getAnyProxyOptions() {
1086
1076
  }
1087
1077
  }
1088
1078
 
1089
- // rewrite 规则判断
1090
- if (shouldMitm(host)) {
1091
- return true; // 强制 MITM
1079
+ // rewrite 规则判断(YouTube 去广告、有道词典 VIP 等)
1080
+ // 这些规则需要 MITM 解密 response body,仅在 enable_mitm 开启时生效
1081
+ if (enable_mitm == "1" && shouldMitm(host)) {
1082
+ return true; // MITM 解密,执行 rewrite 规则
1092
1083
  }
1093
1084
 
1094
- // HTTPS 这里只判断 ip 源和域名
1095
- // 域名不匹配的就直接转发
1096
- // 域名匹配的情况下,再去看 match_rule 的判断,放到 beforeSendRequest 中
1097
-
1098
1085
  // 如果是裸IP请求,全部放行
1099
1086
  if (net.isIPv4(host) || net.isIPv6(host)) {
1100
1087
  return false;
1101
1088
  }
1102
1089
 
1103
- // 如果没有对应ip的匹配规则
1104
- let shouldBlock = shouldBlockHost(host, blockRules, "");
1105
- if (blockRules.length === 0) {
1090
+ // enable_mitm 关闭时纯隧道转发,不做任何拦截
1091
+ if (enable_mitm != "1") {
1106
1092
  return false;
1107
- } else if (shouldBlock) {
1093
+ }
1094
+
1095
+ let shouldBlock = shouldBlockHost(host, blockRules, "");
1096
+ if (blockRules.length > 0 && shouldBlock) {
1097
+ // 有证书:走 MITM 解密后在 beforeSendRequest 中做完整 URL 路径级过滤
1108
1098
  console.log('https 拦截', host, '接下来判断是否根据 match_rule 进行拦截');
1109
- // 只对配置中的域名进行 HTTPS 拦截
1110
- return true; // 允许 HTTPS 拦截
1099
+ return true;
1111
1100
  }
1112
- return false; // 不拦截 HTTPS
1101
+ return false; // 不拦截 HTTPS,透明隧道转发
1113
1102
  },
1114
1103
 
1115
1104
  // 拦截 HTTP 请求以及 HTTPS 拆包的请求
@@ -1345,10 +1334,6 @@ function getAnyProxyOptions() {
1345
1334
  },
1346
1335
 
1347
1336
  },
1348
- webInterface: {
1349
- enable: enable_webinterface == "1" ? true : false,
1350
- webPort: webInterfacePort
1351
- },
1352
1337
  throttle: 800 * 1024 * 1024, // 800 Mbps
1353
1338
  forceProxyHttps: false, // 关闭全局 HTTPS 拦截
1354
1339
  wsIntercept: false,
package/server/express.js CHANGED
@@ -11,7 +11,7 @@ const { exec, execSync } = require('child_process');
11
11
  const LocalProxy = require('../proxy/proxy');
12
12
 
13
13
  const app = express();
14
- const PORT = 8004;
14
+ const PORT = 8003;
15
15
  const DEV = process.env.BLOCK_PROXY_DEV || 0;
16
16
  const configPath = path.join(__dirname, '../config.json');
17
17
 
@@ -199,6 +199,22 @@ app.use(/\/proxy\/*/, async (req, res) => {
199
199
  }
200
200
  });
201
201
 
202
+ // 证书下载接口
203
+ app.get('/fetchCrtFile', (req, res) => {
204
+ try {
205
+ const certPath = path.join(__dirname, '../cert/rootCA.crt');
206
+ if (fs.existsSync(certPath)) {
207
+ res.setHeader('Content-Type', 'application/x-x509-ca-cert');
208
+ res.setHeader('Content-Disposition', 'attachment; filename="rootCA.crt"');
209
+ res.sendFile(certPath);
210
+ } else {
211
+ res.status(404).json({ error: '证书文件不存在,请先生成根证书' });
212
+ }
213
+ } catch (error) {
214
+ res.status(500).json({ error: '证书下载失败: ' + error.message });
215
+ }
216
+ });
217
+
202
218
  // 3. 所有其他请求都返回 index.html(支持 React Router 的前端路由)
203
219
  app.get((req, res) => {
204
220
  res.sendFile(path.join(__dirname, 'build', 'index.html'));