block-proxy 0.1.11 → 0.1.13

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 (62) hide show
  1. package/.agents/skills/commit/skill.md +40 -0
  2. package/.claude/settings.local.json +29 -1
  3. package/.claude/skills/build-client/skill.md +24 -0
  4. package/.claude/skills/commit/skill.md +34 -26
  5. package/.claude/skills/release-client/skill.md +68 -0
  6. package/CLAUDE.md +109 -47
  7. package/Dockerfile +1 -1
  8. package/README.md +69 -60
  9. package/build/asset-manifest.json +6 -6
  10. package/build/index.html +1 -1
  11. package/build/static/css/main.3f317ce6.css +2 -0
  12. package/build/static/css/main.3f317ce6.css.map +1 -0
  13. package/build/static/js/{main.2247fb80.js → main.68f66be0.js} +3 -3
  14. package/build/static/js/main.68f66be0.js.map +1 -0
  15. package/client/app.py +312 -0
  16. package/client/build.sh +84 -0
  17. package/client/config.py +49 -0
  18. package/client/config_window.py +155 -0
  19. package/client/icons/app.icns +0 -0
  20. package/client/icons/app_example.png +0 -0
  21. package/client/icons/app_icon.png +0 -0
  22. package/client/icons/backup/app_example.png +0 -0
  23. package/client/icons/backup/christmas-sock_dark.png +0 -0
  24. package/client/icons/backup/christmas-sock_light.png +0 -0
  25. package/client/icons/backup/socks_on_G.png +0 -0
  26. package/client/icons/backup/socks_on_M.png +0 -0
  27. package/client/icons/christmas-sock_dark.png +0 -0
  28. package/client/icons/christmas-sock_light.png +0 -0
  29. package/client/icons/christmas-sock_light_bar.png +0 -0
  30. package/client/icons/socks_on_G.png +0 -0
  31. package/client/icons/socks_on_G_bar.png +0 -0
  32. package/client/icons/socks_on_M.png +0 -0
  33. package/client/icons/socks_on_M_bar.png +0 -0
  34. package/client/main.py +28 -0
  35. package/client/proxy_core.py +475 -0
  36. package/client/requirements.txt +3 -0
  37. package/client/scripts/download_xray.sh +30 -0
  38. package/client/setup.py +30 -0
  39. package/client/system_proxy.py +94 -0
  40. package/client/tests/__init__.py +0 -0
  41. package/client/tests/test_config.py +72 -0
  42. package/client/tests/test_system_proxy.py +69 -0
  43. package/client/watch-icons.js +31 -0
  44. package/config.json +82 -5
  45. package/docs/superpowers/plans/2026-05-27-blockproxyclient.md +1274 -0
  46. package/docs/superpowers/specs/2026-05-27-blockproxyclient-design.md +264 -0
  47. package/package.json +11 -5
  48. package/proxy/proxy.js +70 -18
  49. package/server/express.js +17 -1
  50. package/skills-lock.json +11 -0
  51. package/socks5/server.js +2 -2
  52. package/src/App.css +596 -276
  53. package/src/App.js +25 -22
  54. package/src/index.css +3 -4
  55. package/test/lib/mock-server.js +133 -0
  56. package/test/proxy-tests.js +708 -0
  57. package/test/run.js +330 -0
  58. package/build/static/css/main.8bfa3d5f.css +0 -2
  59. package/build/static/css/main.8bfa3d5f.css.map +0 -1
  60. package/build/static/js/main.2247fb80.js.map +0 -1
  61. package/hack-of-anyproxy/lib/requestHandler.js +0 -1060
  62. /package/build/static/js/{main.2247fb80.js.LICENSE.txt → main.68f66be0.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.11",
3
+ "version": "0.1.13",
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.3",
40
+ "@bachi/anyproxy": "^0.1.5",
35
41
  "@craco/craco": "^7.1.0",
36
42
  "axios": "^1.13.2",
37
43
  "commander": "^14.0.2",
package/proxy/proxy.js CHANGED
@@ -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
+ var enable_webinterface = "0"; // "0", "1"
61
+ var enable_mitm = "1"; // "0", "1",是否对 HTTPS 启用 MITM 解密(关闭后纯隧道转发,不拦截)
61
62
  // 域名判断,区分浏览器和 App
62
63
  var filtered_mitm_domains = [
63
64
  ...uaFilter.filtered_mitm_domains
@@ -85,6 +86,7 @@ function preCompileRuleRegexp() {
85
86
  // 对于一些流媒体的链接不支持 407 的情况要排除验证
86
87
  // host 可能携带端口:a.com:443
87
88
  function authPass(protocol, host, url) {
89
+ // console.log("url:", host, url);
88
90
  const passHosts = [
89
91
  "googlevideo.com", // Toutube 视频流
90
92
  "dns.weixin.qq.com.cn", // 微信的 dns 预解析
@@ -94,6 +96,22 @@ function authPass(protocol, host, url) {
94
96
  "xiaohongshu.com",
95
97
  "xhscdn.com",
96
98
  "zhihu.com:443",
99
+ "zhimg.com",
100
+ "zhihu.com",
101
+ //-----千问客户端
102
+ "globalsign.com",
103
+ "quark.cn",
104
+ "qianwen.com",
105
+ "uc.cn",
106
+ "ucweb.com",
107
+ "uc.cmd",
108
+ "alibabausercontent.com",
109
+ "taobao.com",
110
+ "sm.cn",
111
+ "zaodian.com",
112
+ "amap.com",
113
+ "alipay.com",
114
+ "aliyuncs.com",
97
115
  ...filtered_mitm_domains
98
116
  ];
99
117
  // 基于 http 传输的流
@@ -262,6 +280,9 @@ async function loadConfig() {
262
280
  enable_webinterface = loadedConfig.enable_webinterface;
263
281
  config.enable_webinterface = enable_webinterface;
264
282
 
283
+ enable_mitm = loadedConfig.enable_mitm || "1"; // 默认开启,兼容旧配置文件缺少此字段
284
+ config.enable_mitm = enable_mitm;
285
+
265
286
  socks5Port = loadedConfig.socks5_port;
266
287
  config.socks5_port = socks5Port;
267
288
 
@@ -298,6 +319,7 @@ async function loadConfig() {
298
319
  socks5_port: socks5Port,
299
320
  enable_socks5: enable_socks5,
300
321
  enable_webinterface: enable_webinterface,
322
+ enable_mitm: enable_mitm,
301
323
  vpn_proxy: ""
302
324
  });
303
325
  // fs.writeFileSync(configPath, JSON.stringify({
@@ -1035,33 +1057,63 @@ function getAnyProxyOptions() {
1035
1057
  }
1036
1058
 
1037
1059
  const blockRules = getBlockRules(clientIp);
1038
- // requestDetail.host 是域名+端口的形式
1039
- const host = requestDetail.host.split(":")[0];
1040
-
1041
- // rewrite 规则判断
1042
- if (shouldMitm(host)) {
1043
- return true; // 强制 MITM
1060
+ // requestDetail.host 可能是以下格式:
1061
+ // - 域名:端口 如 example.com:443
1062
+ // - IPv4:端口 如 192.168.1.1:443
1063
+ // - [IPv6]:端口 如 [2001:db8::1]:443(标准格式)
1064
+ // - IPv6:端口 如 240c:409f:1000::4:0:a5:443(非标准格式)
1065
+ let host;
1066
+ if (requestDetail.host.startsWith("[")) {
1067
+ // 标准 IPv6 格式: [2001:db8::1]:443
1068
+ const closingBracket = requestDetail.host.indexOf("]");
1069
+ if (closingBracket !== -1) {
1070
+ host = requestDetail.host.substring(1, closingBracket);
1071
+ } else {
1072
+ // 异常情况,缺少闭合括号
1073
+ host = requestDetail.host;
1074
+ }
1075
+ } else {
1076
+ // 通过最后一个冒号判断是否有端口
1077
+ // 如果最后一个冒号后面是纯数字,则认为是端口
1078
+ const lastColonIndex = requestDetail.host.lastIndexOf(":");
1079
+ if (lastColonIndex !== -1) {
1080
+ const possiblePort = requestDetail.host.substring(lastColonIndex + 1);
1081
+ if (/^\d+$/.test(possiblePort)) {
1082
+ // 最后部分是端口号,取前半部分作为 host
1083
+ host = requestDetail.host.substring(0, lastColonIndex);
1084
+ } else {
1085
+ // 最后部分不是数字,整个字符串都是 host
1086
+ host = requestDetail.host;
1087
+ }
1088
+ } else {
1089
+ // 没有冒号,整个字符串就是 host
1090
+ host = requestDetail.host;
1091
+ }
1044
1092
  }
1045
1093
 
1046
- // HTTPS 这里只判断 ip 源和域名
1047
- // 域名不匹配的就直接转发
1048
- // 域名匹配的情况下,再去看 match_rule 的判断,放到 beforeSendRequest
1094
+ // rewrite 规则判断(YouTube 去广告、有道词典 VIP 等)
1095
+ // 这些规则需要 MITM 解密 response body,仅在 enable_mitm 开启时生效
1096
+ if (enable_mitm == "1" && shouldMitm(host)) {
1097
+ return true; // MITM 解密,执行 rewrite 规则
1098
+ }
1049
1099
 
1050
1100
  // 如果是裸IP请求,全部放行
1051
1101
  if (net.isIPv4(host) || net.isIPv6(host)) {
1052
1102
  return false;
1053
1103
  }
1054
1104
 
1055
- // 如果没有对应ip的匹配规则
1056
- let shouldBlock = shouldBlockHost(host, blockRules, "");
1057
- if (blockRules.length === 0) {
1105
+ // enable_mitm 关闭时纯隧道转发,不做任何拦截
1106
+ if (enable_mitm != "1") {
1058
1107
  return false;
1059
- } else if (shouldBlock) {
1108
+ }
1109
+
1110
+ let shouldBlock = shouldBlockHost(host, blockRules, "");
1111
+ if (blockRules.length > 0 && shouldBlock) {
1112
+ // 有证书:走 MITM 解密后在 beforeSendRequest 中做完整 URL 路径级过滤
1060
1113
  console.log('https 拦截', host, '接下来判断是否根据 match_rule 进行拦截');
1061
- // 只对配置中的域名进行 HTTPS 拦截
1062
- return true; // 允许 HTTPS 拦截
1114
+ return true;
1063
1115
  }
1064
- return false; // 不拦截 HTTPS
1116
+ return false; // 不拦截 HTTPS,透明隧道转发
1065
1117
  },
1066
1118
 
1067
1119
  // 拦截 HTTP 请求以及 HTTPS 拆包的请求
@@ -1305,7 +1357,7 @@ function getAnyProxyOptions() {
1305
1357
  forceProxyHttps: false, // 关闭全局 HTTPS 拦截
1306
1358
  wsIntercept: false,
1307
1359
  silent: true,
1308
- timeout: 60 * 1000 // 60
1360
+ timeout: 120 * 1000 // 120
1309
1361
  };
1310
1362
  }
1311
1363
 
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'));
@@ -0,0 +1,11 @@
1
+ {
2
+ "version": 1,
3
+ "skills": {
4
+ "commit": {
5
+ "source": "jayli/skills",
6
+ "sourceType": "github",
7
+ "skillPath": "skills/commit/SKILL.md",
8
+ "computedHash": "e3225c68b408307a74e5039265193606db6b568bcc21194b3ad3ed24638fc4a5"
9
+ }
10
+ }
11
+ }
package/socks5/server.js CHANGED
@@ -115,7 +115,7 @@ async function init() {
115
115
  function handleTcpRequest(clientSocket, targetHost, targetPort) {
116
116
  // jayli
117
117
  // console.log(targetHost);
118
- clientSocket.setTimeout(30_000);
118
+ clientSocket.setTimeout(120_000);
119
119
  clientSocket.on('timeout', () => clientSocket.destroy());
120
120
 
121
121
  const proxySocket = net.connect(DOWNSTREAM_HTTP_PROXY_PORT, DOWNSTREAM_HTTP_PROXY_HOST, () => {
@@ -149,7 +149,7 @@ async function init() {
149
149
  proxySocket.on('data', onProxyData);
150
150
  });
151
151
 
152
- proxySocket.setTimeout(30_000);
152
+ proxySocket.setTimeout(120_000);
153
153
  proxySocket.on('timeout', () => proxySocket.destroy());
154
154
  proxySocket.on('error', (err) => {
155
155
  console.warn(`Proxy error: ${err.message}`);