block-proxy 0.1.0
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.
- package/.eslintignore +3 -0
- package/AD_BLOCK.md +38 -0
- package/Dockerfile +51 -0
- package/LICENSE +21 -0
- package/README.md +182 -0
- package/bin/start.js +45 -0
- package/cert/rootCA.crt +20 -0
- package/cert/rootCA.key +27 -0
- package/config.json +234 -0
- package/craco.config.js +52 -0
- package/hack-of-anyproxy/lib/requestHandler.js +1028 -0
- package/package.json +54 -0
- package/proxy/attacker.js +135 -0
- package/proxy/domain.js +26 -0
- package/proxy/fs.js +46 -0
- package/proxy/http.js +224 -0
- package/proxy/mitm/persistentStore.js +34 -0
- package/proxy/mitm/persistentStore.json +3 -0
- package/proxy/mitm/rule.js +116 -0
- package/proxy/mitm/uaFilter.js +47 -0
- package/proxy/mitm/ydcd/ydcd.js +34 -0
- package/proxy/mitm/youtube/youtube.response.js +39 -0
- package/proxy/monitor.js +283 -0
- package/proxy/proxy.js +1488 -0
- package/proxy/scan.js +120 -0
- package/proxy/start.js +7 -0
- package/proxy/wanip.js +76 -0
- package/public/favicon.ico +0 -0
- package/public/index.html +43 -0
- package/public/iphone-proxy-setting.jpg +0 -0
- package/public/logo192.png +0 -0
- package/public/logo512.png +0 -0
- package/public/manifest.json +25 -0
- package/public/proxy.jpg +0 -0
- package/public/robots.txt +3 -0
- package/server/express.js +232 -0
- package/server/start.js +24 -0
- package/server/util.js +166 -0
- package/socks5/server.js +321 -0
- package/socks5/start.js +7 -0
- package/socks5/test_tls_reuse.js +40 -0
- package/src/App.css +505 -0
- package/src/App.js +759 -0
- package/src/App.test.js +8 -0
- package/src/index.css +13 -0
- package/src/index.js +17 -0
- package/src/logo.svg +1 -0
- package/src/reportWebVitals.js +13 -0
- package/src/setupTests.js +5 -0
package/src/App.js
ADDED
|
@@ -0,0 +1,759 @@
|
|
|
1
|
+
// src/App.js
|
|
2
|
+
import React, { useState, useEffect } from 'react';
|
|
3
|
+
import './App.css';
|
|
4
|
+
|
|
5
|
+
// Toast组件
|
|
6
|
+
const Toast = ({ message, type, onClose }) => {
|
|
7
|
+
if (!message) return null;
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<div className={`toast ${type}`}>
|
|
11
|
+
<span>{message}</span>
|
|
12
|
+
<button className="toast-close" onClick={onClose}>×</button>
|
|
13
|
+
</div>
|
|
14
|
+
);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
function App() {
|
|
18
|
+
const [config, setConfig] = useState({
|
|
19
|
+
block_hosts: [],
|
|
20
|
+
proxy_port: 8001,
|
|
21
|
+
socks5_port:8002,
|
|
22
|
+
web_interface_port: 8003,
|
|
23
|
+
auth_username: "",
|
|
24
|
+
auth_password: "",
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const [newHost, setNewHost] = useState('');
|
|
28
|
+
const [timezone, setTimeZone] = useState('');
|
|
29
|
+
const [newMatchRule, setNewMatchRule] = useState('');
|
|
30
|
+
const [newStartTime, setNewStartTime] = useState('00:00');
|
|
31
|
+
const [newEndTime, setNewEndTime] = useState('23:59');
|
|
32
|
+
const [loading, setLoading] = useState(false);
|
|
33
|
+
const [toast, setToast] = useState({ message: '', type: '' });
|
|
34
|
+
const [toastTimer, setToastTimer] = useState(null);
|
|
35
|
+
const [serverIPs, setServerIPs] = useState([]);
|
|
36
|
+
const [isDocker, setIsDocker] = useState(false);
|
|
37
|
+
const [hostIPs, setHostIPs] = useState([]);
|
|
38
|
+
const [devices, setDevices] = useState([]);
|
|
39
|
+
|
|
40
|
+
// 组件加载时获取当前配置和服务器IP
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
fetchConfig();
|
|
43
|
+
fetchServerIPs();
|
|
44
|
+
fetchTimeZone();
|
|
45
|
+
|
|
46
|
+
// 清理定时器
|
|
47
|
+
return () => {
|
|
48
|
+
if (toastTimer) {
|
|
49
|
+
clearTimeout(toastTimer);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}, []);
|
|
53
|
+
|
|
54
|
+
const fetchTimeZone = async () => {
|
|
55
|
+
const response = await fetch('/api/timezone');
|
|
56
|
+
if (response.ok) {
|
|
57
|
+
const data = await response.json();
|
|
58
|
+
setTimeZone(data.timezone);
|
|
59
|
+
} else {
|
|
60
|
+
setTimeZone("");
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const fetchServerIPs = async () => {
|
|
65
|
+
try {
|
|
66
|
+
const response = await fetch('/api/server-ip');
|
|
67
|
+
if (response.ok) {
|
|
68
|
+
const data = await response.json();
|
|
69
|
+
setServerIPs(data.ips);
|
|
70
|
+
setIsDocker(data.docker);
|
|
71
|
+
if (data.hostIPs) {
|
|
72
|
+
setHostIPs(data.hostIPs);
|
|
73
|
+
}
|
|
74
|
+
} else {
|
|
75
|
+
showToast('获取服务器IP失败', 'error');
|
|
76
|
+
}
|
|
77
|
+
} catch (error) {
|
|
78
|
+
showToast('网络错误: ' + error.message, 'error');
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// 修改 fetchConfig 函数:
|
|
83
|
+
const fetchConfig = async () => {
|
|
84
|
+
try {
|
|
85
|
+
const response = await fetch('/api/config');
|
|
86
|
+
if (response.ok) {
|
|
87
|
+
const data = await response.json();
|
|
88
|
+
setConfig(data);
|
|
89
|
+
// 同时设置设备信息
|
|
90
|
+
if (data.devices) {
|
|
91
|
+
setDevices(data.devices);
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
showToast('获取配置失败', 'error');
|
|
95
|
+
}
|
|
96
|
+
} catch (error) {
|
|
97
|
+
showToast('网络错误: ' + error.message, 'error');
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const showToast = (message, type) => {
|
|
102
|
+
// 清除之前的定时器
|
|
103
|
+
if (toastTimer) {
|
|
104
|
+
clearTimeout(toastTimer);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// 设置新的toast
|
|
108
|
+
setToast({ message, type });
|
|
109
|
+
|
|
110
|
+
// 设置新的定时器并保存引用
|
|
111
|
+
const newTimer = setTimeout(() => {
|
|
112
|
+
setToast({ message: '', type: '' });
|
|
113
|
+
setToastTimer(null);
|
|
114
|
+
}, 3000);
|
|
115
|
+
|
|
116
|
+
// 保存定时器引用
|
|
117
|
+
setToastTimer(newTimer);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const closeToast = () => {
|
|
121
|
+
// 清除定时器
|
|
122
|
+
if (toastTimer) {
|
|
123
|
+
clearTimeout(toastTimer);
|
|
124
|
+
setToastTimer(null);
|
|
125
|
+
}
|
|
126
|
+
setToast({ message: '', type: '' });
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const handleAddHost = async () => {
|
|
130
|
+
if (newHost.trim()) {
|
|
131
|
+
const newFilterItem = {
|
|
132
|
+
filter_host: newHost.trim(),
|
|
133
|
+
filter_match_rule: newMatchRule.trim(), // 添加 match_rule 字段,默认为空
|
|
134
|
+
filter_start_time: newStartTime,
|
|
135
|
+
filter_end_time: newEndTime,
|
|
136
|
+
filter_weekday: [1, 2, 3, 4, 5, 6, 7] // 默认每天生效
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const updatedConfig = {
|
|
140
|
+
...config,
|
|
141
|
+
block_hosts: [...config.block_hosts, newFilterItem]
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// Update local state first
|
|
145
|
+
setConfig(updatedConfig);
|
|
146
|
+
setNewHost('');
|
|
147
|
+
|
|
148
|
+
// Save to config.json via API
|
|
149
|
+
setLoading(true);
|
|
150
|
+
await saveConfig(updatedConfig);
|
|
151
|
+
setLoading(false);
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const handleRemoveHost = async (hostToRemove) => {
|
|
156
|
+
// 添加确认框
|
|
157
|
+
const confirmed = window.confirm(`确定要删除主机 "${typeof hostToRemove === 'string' ? hostToRemove : hostToRemove.filter_host}" 吗?`);
|
|
158
|
+
if (confirmed) {
|
|
159
|
+
const updatedConfig = {
|
|
160
|
+
...config,
|
|
161
|
+
block_hosts: config.block_hosts.filter(host => {
|
|
162
|
+
if (typeof host === 'string') {
|
|
163
|
+
return host !== hostToRemove;
|
|
164
|
+
} else {
|
|
165
|
+
return host !== hostToRemove && host.filter_host !== hostToRemove;
|
|
166
|
+
}
|
|
167
|
+
})
|
|
168
|
+
};
|
|
169
|
+
setConfig(updatedConfig);
|
|
170
|
+
await saveConfig(updatedConfig);
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const handleSaveConfig = async () => {
|
|
175
|
+
setLoading(true);
|
|
176
|
+
// 先保存配置
|
|
177
|
+
let saveResult = await saveConfig(config);
|
|
178
|
+
setLoading(false);
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const saveConfig = async (config) => {
|
|
182
|
+
try {
|
|
183
|
+
const response = await fetch('/api/config', {
|
|
184
|
+
method: 'POST',
|
|
185
|
+
headers: {
|
|
186
|
+
'Content-Type': 'application/json'
|
|
187
|
+
},
|
|
188
|
+
body: JSON.stringify(config)
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
if (response.ok) {
|
|
192
|
+
showToast('配置保存成功!', 'success');
|
|
193
|
+
return true;
|
|
194
|
+
} else {
|
|
195
|
+
showToast('保存失败', 'error');
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
} catch (error) {
|
|
199
|
+
showToast('网络错误: ' + error.message, 'error');
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
const handleRestartProxy = async () => {
|
|
205
|
+
setLoading(true);
|
|
206
|
+
|
|
207
|
+
// 先保存配置
|
|
208
|
+
if (!await saveConfig(config)) {
|
|
209
|
+
setLoading(false);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
try {
|
|
214
|
+
const response = await fetch('/api/restart', {
|
|
215
|
+
method: 'POST'
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
if (response.ok) {
|
|
219
|
+
showToast('代理服务器重启成功!', 'success');
|
|
220
|
+
} else {
|
|
221
|
+
const errorData = await response.json();
|
|
222
|
+
showToast('重启失败: ' + errorData.error, 'error');
|
|
223
|
+
}
|
|
224
|
+
} catch (error) {
|
|
225
|
+
showToast('网络错误: ' + error.message, 'error');
|
|
226
|
+
}
|
|
227
|
+
setLoading(false);
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
// 在 handleRestartProxy 函数之后添加以下函数:
|
|
232
|
+
const handleUpdateDevices = async () => {
|
|
233
|
+
setLoading(true);
|
|
234
|
+
try {
|
|
235
|
+
const response = await fetch('/api/update-devices', {
|
|
236
|
+
method: 'POST'
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
if (response.ok) {
|
|
240
|
+
showToast('路由表刷新成功!', 'success');
|
|
241
|
+
await fetchConfig();
|
|
242
|
+
} else {
|
|
243
|
+
const errorData = await response.json();
|
|
244
|
+
showToast('刷新失败: ' + errorData.error, 'error');
|
|
245
|
+
}
|
|
246
|
+
} catch (error) {
|
|
247
|
+
showToast('网络错误: ' + error.message, 'error');
|
|
248
|
+
}
|
|
249
|
+
setLoading(false);
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
// 更新拦截项的时间段
|
|
253
|
+
const updateFilterTime = async (index, startTime, endTime) => {
|
|
254
|
+
const updatedBlockHosts = [...config.block_hosts];
|
|
255
|
+
|
|
256
|
+
// 兼容旧格式转换为新格式
|
|
257
|
+
if (typeof updatedBlockHosts[index] === 'string') {
|
|
258
|
+
updatedBlockHosts[index] = {
|
|
259
|
+
filter_host: updatedBlockHosts[index],
|
|
260
|
+
filter_start_time: startTime,
|
|
261
|
+
filter_end_time: endTime,
|
|
262
|
+
filter_weekday: [1, 2, 3, 4, 5, 6, 7] // 默认每天生效
|
|
263
|
+
};
|
|
264
|
+
} else {
|
|
265
|
+
updatedBlockHosts[index] = {
|
|
266
|
+
...updatedBlockHosts[index],
|
|
267
|
+
filter_start_time: startTime,
|
|
268
|
+
filter_end_time: endTime
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const updatedConfig = {
|
|
273
|
+
...config,
|
|
274
|
+
block_hosts: updatedBlockHosts
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
setConfig(updatedConfig);
|
|
278
|
+
await saveConfig(updatedConfig);
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
// 更新拦截项的星期几设置
|
|
282
|
+
const updateFilterWeekday = async (index, day) => {
|
|
283
|
+
const updatedBlockHosts = [...config.block_hosts];
|
|
284
|
+
|
|
285
|
+
// 兼容旧格式转换为新格式
|
|
286
|
+
if (typeof updatedBlockHosts[index] === 'string') {
|
|
287
|
+
updatedBlockHosts[index] = {
|
|
288
|
+
filter_host: updatedBlockHosts[index],
|
|
289
|
+
filter_start_time: '00:00',
|
|
290
|
+
filter_end_time: '23:59',
|
|
291
|
+
filter_weekday: [1, 2, 3, 4, 5, 6, 7] // 默认每天生效
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// 初始化 filter_weekday 如果不存在
|
|
296
|
+
if (!updatedBlockHosts[index].filter_weekday) {
|
|
297
|
+
updatedBlockHosts[index].filter_weekday = [1, 2, 3, 4, 5, 6, 7];
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// 切换星期几的选中状态
|
|
301
|
+
const weekdays = [...updatedBlockHosts[index].filter_weekday];
|
|
302
|
+
const dayIndex = weekdays.indexOf(day);
|
|
303
|
+
|
|
304
|
+
if (dayIndex > -1) {
|
|
305
|
+
// 如果已存在,则移除
|
|
306
|
+
weekdays.splice(dayIndex, 1);
|
|
307
|
+
} else {
|
|
308
|
+
// 如果不存在,则添加
|
|
309
|
+
weekdays.push(day);
|
|
310
|
+
weekdays.sort((a, b) => a - b); // 排序
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
updatedBlockHosts[index] = {
|
|
314
|
+
...updatedBlockHosts[index],
|
|
315
|
+
filter_weekday: weekdays
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
const updatedConfig = {
|
|
319
|
+
...config,
|
|
320
|
+
block_hosts: updatedBlockHosts
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
setConfig(updatedConfig);
|
|
324
|
+
await saveConfig(updatedConfig);
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
// 更新拦截项的MAC地址
|
|
328
|
+
const updateFilterMac = async (index, mac) => {
|
|
329
|
+
const updatedBlockHosts = [...config.block_hosts];
|
|
330
|
+
|
|
331
|
+
// 兼容旧格式转换为新格式
|
|
332
|
+
if (typeof updatedBlockHosts[index] === 'string') {
|
|
333
|
+
updatedBlockHosts[index] = {
|
|
334
|
+
filter_host: updatedBlockHosts[index],
|
|
335
|
+
filter_start_time: '00:00',
|
|
336
|
+
filter_end_time: '23:59',
|
|
337
|
+
filter_mac: mac,
|
|
338
|
+
filter_weekday: [1, 2, 3, 4, 5, 6, 7] // 默认每天生效
|
|
339
|
+
};
|
|
340
|
+
} else {
|
|
341
|
+
updatedBlockHosts[index] = {
|
|
342
|
+
...updatedBlockHosts[index],
|
|
343
|
+
filter_mac: mac
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const updatedConfig = {
|
|
348
|
+
...config,
|
|
349
|
+
block_hosts: updatedBlockHosts
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
setConfig(updatedConfig);
|
|
353
|
+
await saveConfig(updatedConfig);
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
function getIpAddress() {
|
|
357
|
+
let addr = '';
|
|
358
|
+
if (serverIPs.length == 0) {
|
|
359
|
+
return '0.0.0.0';
|
|
360
|
+
} else {
|
|
361
|
+
serverIPs.map((ip, index) => {
|
|
362
|
+
if(index == 0) {
|
|
363
|
+
addr = ip.address;
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
return addr;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// 获取拦截项的显示名称
|
|
371
|
+
const getFilterHostDisplay = (filterItem) => {
|
|
372
|
+
return typeof filterItem === 'string' ? filterItem : filterItem.filter_host;
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
// 获取拦截项的 match_rule
|
|
376
|
+
const getFilterMatchRule= (filterItem) => {
|
|
377
|
+
if (typeof filterItem === 'string') {
|
|
378
|
+
return ''; // 字符串格式没有 match_rule 字段
|
|
379
|
+
}
|
|
380
|
+
return filterItem.filter_match_rule || '';
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
// 获取拦截项的时间段
|
|
384
|
+
const getFilterTimes = (filterItem) => {
|
|
385
|
+
if (typeof filterItem === 'string') {
|
|
386
|
+
return { start: '00:00', end: '23:59' };
|
|
387
|
+
}
|
|
388
|
+
return {
|
|
389
|
+
start: filterItem.filter_start_time || '00:00',
|
|
390
|
+
end: filterItem.filter_end_time || '23:59'
|
|
391
|
+
};
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
// 获取拦截项的星期几设置
|
|
395
|
+
const getFilterWeekdays = (filterItem) => {
|
|
396
|
+
if (typeof filterItem === 'string') {
|
|
397
|
+
return [1, 2, 3, 4, 5, 6, 7]; // 默认每天生效
|
|
398
|
+
}
|
|
399
|
+
return filterItem.filter_weekday || [1, 2, 3, 4, 5, 6, 7];
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
// 获取拦截项的MAC地址
|
|
403
|
+
const getFilterMac = (filterItem) => {
|
|
404
|
+
if (typeof filterItem === 'string') {
|
|
405
|
+
return ''; // 字符串格式没有MAC地址字段
|
|
406
|
+
}
|
|
407
|
+
return filterItem.filter_mac || '';
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
// 星期几的显示名称
|
|
411
|
+
const weekdayNames = ['一', '二', '三', '四', '五', '六', '日'];
|
|
412
|
+
|
|
413
|
+
return (
|
|
414
|
+
<div className="App">
|
|
415
|
+
<div className="config-container">
|
|
416
|
+
<h1>代理服务器配置</h1>
|
|
417
|
+
|
|
418
|
+
{/* Toast提示组件 */}
|
|
419
|
+
<Toast
|
|
420
|
+
message={toast.message}
|
|
421
|
+
type={toast.type}
|
|
422
|
+
onClose={closeToast}
|
|
423
|
+
/>
|
|
424
|
+
|
|
425
|
+
{/* 服务器IP信息 */}
|
|
426
|
+
<div className="config-section">
|
|
427
|
+
<div className="server-info">
|
|
428
|
+
{serverIPs.length > 0 ? (
|
|
429
|
+
<div>
|
|
430
|
+
<p>
|
|
431
|
+
<strong>服务器IP地址:</strong>
|
|
432
|
+
<span className="docker-info">{isDocker ? ' (Docker环境)' : ' (非Docker环境)'}</span>
|
|
433
|
+
<span> {timezone}</span>
|
|
434
|
+
</p>
|
|
435
|
+
<ul className="ip-list">
|
|
436
|
+
{serverIPs.map((ip, index) => (
|
|
437
|
+
<li key={index} className="ip-item">
|
|
438
|
+
<span className="interface-name">{ip.interface}:</span>
|
|
439
|
+
<span className="ip-address">{ip.address}</span>
|
|
440
|
+
</li>
|
|
441
|
+
))}
|
|
442
|
+
</ul>
|
|
443
|
+
|
|
444
|
+
{/* 显示宿主机IP信息 */}
|
|
445
|
+
{isDocker && (
|
|
446
|
+
<div className="host-ip-info">
|
|
447
|
+
<p><strong>宿主机IP地址:</strong>{hostIPs}</p>
|
|
448
|
+
</div>
|
|
449
|
+
)}
|
|
450
|
+
</div>
|
|
451
|
+
) : (
|
|
452
|
+
<p>正在获取服务器IP地址...</p>
|
|
453
|
+
)}
|
|
454
|
+
</div>
|
|
455
|
+
</div>
|
|
456
|
+
|
|
457
|
+
<div className="config-section">
|
|
458
|
+
<h2>拦截主机列表</h2>
|
|
459
|
+
<div className="host-input">
|
|
460
|
+
<input
|
|
461
|
+
type="text"
|
|
462
|
+
value={newHost}
|
|
463
|
+
onChange={(e) => setNewHost(e.target.value)}
|
|
464
|
+
placeholder="域名,例如:example.com"
|
|
465
|
+
/>
|
|
466
|
+
<input
|
|
467
|
+
type="text"
|
|
468
|
+
value={newMatchRule}
|
|
469
|
+
onChange={(e) => setNewMatchRule(e.target.value)}
|
|
470
|
+
placeholder="例子:^https?:\/\/.+abc\.com\/api\/\/def, 留空拦截全部"
|
|
471
|
+
/>
|
|
472
|
+
<div className="time-inputs">
|
|
473
|
+
<label><span>开始:</span>
|
|
474
|
+
<input
|
|
475
|
+
type="time"
|
|
476
|
+
value={newStartTime}
|
|
477
|
+
onChange={(e) => setNewStartTime(e.target.value)}
|
|
478
|
+
/>
|
|
479
|
+
</label>
|
|
480
|
+
<label><span>结束:</span>
|
|
481
|
+
<input
|
|
482
|
+
type="time"
|
|
483
|
+
value={newEndTime}
|
|
484
|
+
onChange={(e) => setNewEndTime(e.target.value)}
|
|
485
|
+
/>
|
|
486
|
+
</label>
|
|
487
|
+
<button onClick={handleAddHost}>添加</button>
|
|
488
|
+
</div>
|
|
489
|
+
</div>
|
|
490
|
+
<hr className="simple-line" />
|
|
491
|
+
<ul className="host-list">
|
|
492
|
+
<li key={1000} className="host-item">
|
|
493
|
+
<div className="host-info">域名</div>
|
|
494
|
+
<div className="weekday-controls title-weedkey-controls">星期</div>
|
|
495
|
+
<div className="title-mac-input">MAC 地址</div>
|
|
496
|
+
<div className="title-time-controls">时间段</div>
|
|
497
|
+
<div className="table-right-blank"></div>
|
|
498
|
+
</li>
|
|
499
|
+
{config.block_hosts.map((host, index) => (
|
|
500
|
+
<li key={index} className="host-item">
|
|
501
|
+
<div className="host-info">
|
|
502
|
+
<span className="host-text"><strong>{getFilterHostDisplay(host)}</strong><br />{getFilterMatchRule(host)}</span>
|
|
503
|
+
<div className="weekday-controls">
|
|
504
|
+
{weekdayNames.map((name, dayIndex) => {
|
|
505
|
+
const day = dayIndex + 1;
|
|
506
|
+
const weekdays = getFilterWeekdays(host);
|
|
507
|
+
const isActive = weekdays.includes(day);
|
|
508
|
+
return (
|
|
509
|
+
<button
|
|
510
|
+
key={day}
|
|
511
|
+
className={`weekday-btn ${isActive ? 'active' : ''}`}
|
|
512
|
+
onClick={() => updateFilterWeekday(index, day)}
|
|
513
|
+
title={`周${name}`}
|
|
514
|
+
>
|
|
515
|
+
{name}
|
|
516
|
+
</button>
|
|
517
|
+
);
|
|
518
|
+
})}
|
|
519
|
+
</div>
|
|
520
|
+
<div className="mac-input">
|
|
521
|
+
<input
|
|
522
|
+
type="text"
|
|
523
|
+
value={getFilterMac(host)}
|
|
524
|
+
onChange={(e) => updateFilterMac(index, e.target.value)}
|
|
525
|
+
placeholder="MAC地址(可选)"
|
|
526
|
+
/>
|
|
527
|
+
</div>
|
|
528
|
+
<div className="time-controls">
|
|
529
|
+
<label>
|
|
530
|
+
<input
|
|
531
|
+
type="time"
|
|
532
|
+
value={getFilterTimes(host).start}
|
|
533
|
+
onChange={(e) => updateFilterTime(index, e.target.value, getFilterTimes(host).end)}
|
|
534
|
+
/>
|
|
535
|
+
</label>
|
|
536
|
+
<label>~</label>
|
|
537
|
+
<label>
|
|
538
|
+
<input
|
|
539
|
+
type="time"
|
|
540
|
+
value={getFilterTimes(host).end}
|
|
541
|
+
onChange={(e) => updateFilterTime(index, getFilterTimes(host).start, e.target.value)}
|
|
542
|
+
/>
|
|
543
|
+
</label>
|
|
544
|
+
</div>
|
|
545
|
+
</div>
|
|
546
|
+
<button
|
|
547
|
+
onClick={() => handleRemoveHost(host)}
|
|
548
|
+
className="remove-btn"
|
|
549
|
+
>
|
|
550
|
+
X
|
|
551
|
+
</button>
|
|
552
|
+
</li>
|
|
553
|
+
))}
|
|
554
|
+
</ul>
|
|
555
|
+
</div>
|
|
556
|
+
|
|
557
|
+
<div className="config-section">
|
|
558
|
+
<h2>HTTP/Socks5 端口设置,验证信息,下游 VPN_Proxy 代理</h2>
|
|
559
|
+
{/*<p><span>配置页端口默认 8004</span></p> */}
|
|
560
|
+
<div className="setting-row">
|
|
561
|
+
<label>Anyproxy HTTP 代理端口:</label>
|
|
562
|
+
<input
|
|
563
|
+
type="number"
|
|
564
|
+
value={config.proxy_port}
|
|
565
|
+
onChange={(e) => setConfig({...config, proxy_port: parseInt(e.target.value) || 8001})}
|
|
566
|
+
/>
|
|
567
|
+
</div>
|
|
568
|
+
<div className="setting-row">
|
|
569
|
+
<label>Socks5 代理端口:</label>
|
|
570
|
+
<input
|
|
571
|
+
type="number"
|
|
572
|
+
value={config.socks5_port}
|
|
573
|
+
onChange={(e) => setConfig({...config, socks5_port: parseInt(e.target.value) || 8002})}
|
|
574
|
+
/>
|
|
575
|
+
</div>
|
|
576
|
+
|
|
577
|
+
<div className="setting-row">
|
|
578
|
+
<label>Anyproxy 监控端口:</label>
|
|
579
|
+
<input
|
|
580
|
+
type="number"
|
|
581
|
+
value={config.web_interface_port}
|
|
582
|
+
onChange={(e) => setConfig({...config, web_interface_port: parseInt(e.target.value) || 8003})}
|
|
583
|
+
/>
|
|
584
|
+
</div>
|
|
585
|
+
|
|
586
|
+
<div className="setting-row">
|
|
587
|
+
<label>代理用户名:</label>
|
|
588
|
+
<input
|
|
589
|
+
type="string"
|
|
590
|
+
value={config.auth_username}
|
|
591
|
+
onChange={(e) => setConfig({...config, auth_username: e.target.value || ""})}
|
|
592
|
+
/>
|
|
593
|
+
</div>
|
|
594
|
+
<div className="setting-row">
|
|
595
|
+
<label>代理密码: </label>
|
|
596
|
+
<input
|
|
597
|
+
type="string"
|
|
598
|
+
value={config.auth_password}
|
|
599
|
+
onChange={(e) => setConfig({...config, auth_password: e.target.value || ""})}
|
|
600
|
+
/>
|
|
601
|
+
</div>
|
|
602
|
+
|
|
603
|
+
<div className="setting-row">
|
|
604
|
+
<label>公网域名:</label>
|
|
605
|
+
<div>如果要公网可访问,OpenWRT 配置相同的端口转发到 AnyProxy 代理端口,这里写公网域名<br />(仅在浏览器通过公网域名+端口查看系统水位时防止回环)</div>
|
|
606
|
+
<input
|
|
607
|
+
type="text"
|
|
608
|
+
value={config.your_domain}
|
|
609
|
+
onChange={(e) => setConfig({...config, your_domain: e.target.value || ""})}
|
|
610
|
+
/>
|
|
611
|
+
</div>
|
|
612
|
+
|
|
613
|
+
<div className="setting-row">
|
|
614
|
+
<label>VPN_PROXY 设置(留空):</label>
|
|
615
|
+
<div>(格式:“127.0.0.1:1087”,仅调试用)</div>
|
|
616
|
+
<input
|
|
617
|
+
type="text"
|
|
618
|
+
value={config.vpn_proxy}
|
|
619
|
+
onChange={(e) => setConfig({...config, vpn_proxy: e.target.value || ""})}
|
|
620
|
+
/>
|
|
621
|
+
</div>
|
|
622
|
+
<div className="setting-row actions">
|
|
623
|
+
<button
|
|
624
|
+
onClick={handleSaveConfig}
|
|
625
|
+
disabled={loading}
|
|
626
|
+
className="save-btn"
|
|
627
|
+
>
|
|
628
|
+
{loading ? '保存中...' : '保存配置'}
|
|
629
|
+
</button>
|
|
630
|
+
|
|
631
|
+
<button
|
|
632
|
+
onClick={handleUpdateDevices}
|
|
633
|
+
disabled={loading}
|
|
634
|
+
className="restart-btn"
|
|
635
|
+
style={{ backgroundColor: '#17a2b8', color: 'white' }}
|
|
636
|
+
>
|
|
637
|
+
{loading ? '刷新中...' : '刷新路由表'}
|
|
638
|
+
</button>
|
|
639
|
+
|
|
640
|
+
<button
|
|
641
|
+
onClick={handleRestartProxy}
|
|
642
|
+
disabled={loading}
|
|
643
|
+
className="restart-btn"
|
|
644
|
+
>
|
|
645
|
+
{loading ? '重启中...' : '重启代理'}
|
|
646
|
+
</button>
|
|
647
|
+
</div>
|
|
648
|
+
</div>
|
|
649
|
+
|
|
650
|
+
<div className="config-section">
|
|
651
|
+
<h2>代理设置</h2>
|
|
652
|
+
<p>
|
|
653
|
+
<img src="/iphone-proxy-setting.jpg" alt="iPhone Proxy Setting"
|
|
654
|
+
style={{ float: 'right', marginLeft: '10px', width: '166px' }} />
|
|
655
|
+
<b>代理服务器:</b>
|
|
656
|
+
{serverIPs.length > 0 ? (
|
|
657
|
+
<span>
|
|
658
|
+
{getIpAddress()}
|
|
659
|
+
</span>
|
|
660
|
+
) : (
|
|
661
|
+
<span>正在获取服务器IP地址...</span>
|
|
662
|
+
)}
|
|
663
|
+
{isDocker && (
|
|
664
|
+
<span> (当前是docker环境,请填写宿主机IP)</span>
|
|
665
|
+
)}
|
|
666
|
+
</p>
|
|
667
|
+
<p>
|
|
668
|
+
<b>HTTP 代理端口:</b><span>{config.proxy_port}</span> <span>开启</span>
|
|
669
|
+
</p>
|
|
670
|
+
<p>
|
|
671
|
+
<b>Socks5(Over TLS)代理端口:</b><span>{config.socks5_port}</span>
|
|
672
|
+
<span>{
|
|
673
|
+
config.enable_socks5 === "1" ? "开启" : "关闭"
|
|
674
|
+
}</span>
|
|
675
|
+
</p>
|
|
676
|
+
<p>
|
|
677
|
+
<b>用户名</b>:{config.auth_username === "" ? (
|
|
678
|
+
<span>无</span>
|
|
679
|
+
) : (
|
|
680
|
+
config.auth_username
|
|
681
|
+
)}
|
|
682
|
+
</p>
|
|
683
|
+
<p>
|
|
684
|
+
<b>密码</b>:{config.auth_password === "" ? (
|
|
685
|
+
<span>无</span>
|
|
686
|
+
) : (
|
|
687
|
+
config.auth_password
|
|
688
|
+
)}
|
|
689
|
+
</p>
|
|
690
|
+
<p>
|
|
691
|
+
<b>监控地址:</b>
|
|
692
|
+
{serverIPs.length > 0 ? (
|
|
693
|
+
<span>
|
|
694
|
+
<a href={`http://${getIpAddress()}:${config.web_interface_port}`} target="_blank" rel="noopener noreferrer">
|
|
695
|
+
{`http://${getIpAddress()}:${config.web_interface_port}`}
|
|
696
|
+
</a>
|
|
697
|
+
</span>
|
|
698
|
+
) : (
|
|
699
|
+
<span>正在获取服务器IP地址...</span>
|
|
700
|
+
)}
|
|
701
|
+
</p>
|
|
702
|
+
<div>
|
|
703
|
+
<p><b>配置方法</b>:(以Iphone为例)</p>
|
|
704
|
+
<p>设置 → 无线局域网 → 点击当前网络 → HTTP代理/配置代理,设置服务器和端口(图右)</p>
|
|
705
|
+
</div>
|
|
706
|
+
<div>
|
|
707
|
+
<p><b>拦截配置</b>:</p>
|
|
708
|
+
<p>1. 不写 Mac 地址则拦截内网所有设备。</p>
|
|
709
|
+
<p>2. 路径留空则该域名下所有请求都拦截</p>
|
|
710
|
+
<p>3. 路径处写正则表达式</p>
|
|
711
|
+
</div>
|
|
712
|
+
<div>
|
|
713
|
+
<p>
|
|
714
|
+
运维信息:
|
|
715
|
+
<a href={`http://${getIpAddress()}:${config.proxy_port}`} target="_blank">
|
|
716
|
+
{`http://${getIpAddress()}:${config.proxy_port}`}
|
|
717
|
+
</a>
|
|
718
|
+
</p>
|
|
719
|
+
</div>
|
|
720
|
+
</div>
|
|
721
|
+
|
|
722
|
+
<div className="config-section">
|
|
723
|
+
<h2>路由表</h2>
|
|
724
|
+
{devices && devices.length > 0 ? (
|
|
725
|
+
<div>
|
|
726
|
+
<p>当前共有 {devices.length} 个设备</p>
|
|
727
|
+
<ul className="ip-list">
|
|
728
|
+
{devices.map((device, index) => (
|
|
729
|
+
<li key={index} className="ip-item">
|
|
730
|
+
<span className="interface-name">{device.mac || '未知MAC'} </span>
|
|
731
|
+
<span className="ip-address">{device.ip || '未知IP'}</span>
|
|
732
|
+
</li>
|
|
733
|
+
))}
|
|
734
|
+
</ul>
|
|
735
|
+
</div>
|
|
736
|
+
) : (
|
|
737
|
+
<p>暂无设备信息</p>
|
|
738
|
+
)}
|
|
739
|
+
<button
|
|
740
|
+
onClick={fetchConfig}
|
|
741
|
+
disabled={loading}
|
|
742
|
+
style={{ marginTop: '10px' }}
|
|
743
|
+
>
|
|
744
|
+
{loading ? '刷新中...' : '更新路由表'}
|
|
745
|
+
</button>
|
|
746
|
+
</div>
|
|
747
|
+
<div className="config-section">
|
|
748
|
+
<h3>项目源码</h3>
|
|
749
|
+
<div>
|
|
750
|
+
<p>Github 地址:<a href="https://github.com/jayli/block-proxy">Block Proxy</a></p>
|
|
751
|
+
</div>
|
|
752
|
+
</div>
|
|
753
|
+
</div>
|
|
754
|
+
</div>
|
|
755
|
+
);
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
export default App;
|
|
759
|
+
/* vim: set filetype=javascriptreact : */
|