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/proxy/scan.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// 扫描当前网络,得到ip和mac的对应表
|
|
2
|
+
// /proxy/scan.js
|
|
3
|
+
const { exec } = require('child_process');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const _fs = require('./fs.js');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const ping = require('ping'); // 这个包通常镜像里有
|
|
9
|
+
const configPath = path.join(__dirname, '../config.json');
|
|
10
|
+
|
|
11
|
+
function getLocalSubnet() {
|
|
12
|
+
const nets = os.networkInterfaces();
|
|
13
|
+
for (const name of Object.keys(nets)) {
|
|
14
|
+
for (const net of nets[name]) {
|
|
15
|
+
if (net.family === 'IPv4' && !net.internal && !net.address.startsWith('172.')) {
|
|
16
|
+
const parts = net.address.split('.');
|
|
17
|
+
return `${parts[0]}.${parts[1]}.${parts[2]}`;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
throw new Error('No valid LAN IP found');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function parseArpTable(arpOutput, subnet) {
|
|
25
|
+
const lines = arpOutput.split('\n');
|
|
26
|
+
const result = [];
|
|
27
|
+
|
|
28
|
+
for (const line of lines) {
|
|
29
|
+
// macOS / Linux 格式: ? (192.168.1.5) at aa:bb:cc:dd:ee:ff on en0 ...
|
|
30
|
+
// Windows 格式: 192.168.1.5 aa-bb-cc-dd-ee-ff dynamic
|
|
31
|
+
const ipMatch = line.match(/(\d+\.\d+\.\d+\.\d+)/);
|
|
32
|
+
if (!ipMatch) continue;
|
|
33
|
+
|
|
34
|
+
const ip = ipMatch[1];
|
|
35
|
+
if (!ip.startsWith(subnet)) continue;
|
|
36
|
+
|
|
37
|
+
let macMatch;
|
|
38
|
+
if (process.platform === 'win32') {
|
|
39
|
+
macMatch = line.match(/(([0-9a-fA-F]{1,2}[:-]){5}([0-9a-fA-F]{1,2}))/);
|
|
40
|
+
} else {
|
|
41
|
+
macMatch = line.match(/(([0-9a-fA-F]{1,2}[:\-]){5}([0-9a-fA-F]{1,2}))/);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (macMatch) {
|
|
45
|
+
let mac = macMatch[1].toUpperCase();
|
|
46
|
+
if (process.platform !== 'win32') {
|
|
47
|
+
mac = mac.replace(/-/g, ':');
|
|
48
|
+
}
|
|
49
|
+
result.push({ ip, mac });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// "0","1"
|
|
56
|
+
async function setScanStatus(status) {
|
|
57
|
+
// const configFileContent = fs.readFileSync(configPath, 'utf-8');
|
|
58
|
+
// const loadedConfig = JSON.parse(configFileContent);
|
|
59
|
+
const loadedConfig = await _fs.readConfig();
|
|
60
|
+
loadedConfig.network_scanning_status = status.toString();
|
|
61
|
+
_fs.writeConfig({
|
|
62
|
+
...loadedConfig
|
|
63
|
+
});
|
|
64
|
+
// fs.writeFileSync(configPath, JSON.stringify({
|
|
65
|
+
// }, null, 2));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// "0","1"
|
|
69
|
+
async function getScanStatus() {
|
|
70
|
+
// const configFileContent = fs.readFileSync(configPath, 'utf-8');
|
|
71
|
+
// const loadedConfig = JSON.parse(configFileContent);
|
|
72
|
+
const loadedConfig = await _fs.readConfig();
|
|
73
|
+
return loadedConfig.network_scanning_status;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function doScan() {
|
|
77
|
+
const subnet = getLocalSubnet();
|
|
78
|
+
// 如果是 30 网段,直接返回空
|
|
79
|
+
if (subnet.startsWith("30.")) {
|
|
80
|
+
return []
|
|
81
|
+
}
|
|
82
|
+
console.log(`Scanning subnet: ${subnet}.0/24`);
|
|
83
|
+
|
|
84
|
+
// Ping all IPs to populate ARP cache
|
|
85
|
+
const ips = Array.from({ length: 254 }, (_, i) => `${subnet}.${i + 1}`);
|
|
86
|
+
await Promise.allSettled(ips.map(ip => ping.promise.probe(ip, { timeout: 1 })));
|
|
87
|
+
|
|
88
|
+
// Read ARP table via system command
|
|
89
|
+
const cmd = process.platform === 'win32' ? 'arp -a' : 'arp -a';
|
|
90
|
+
const arpOutput = await new Promise((resolve, reject) => {
|
|
91
|
+
exec(cmd, async (error, stdout) => {
|
|
92
|
+
if (error) {
|
|
93
|
+
await setScanStatus("0");
|
|
94
|
+
reject(error);
|
|
95
|
+
} else {
|
|
96
|
+
resolve(stdout);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const devices = parseArpTable(arpOutput, subnet);
|
|
102
|
+
return devices;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
var tempDevices = [];
|
|
106
|
+
async function scanNetwork() {
|
|
107
|
+
var status = await getScanStatus();
|
|
108
|
+
if (status == "1") {
|
|
109
|
+
await setScanStatus("0");
|
|
110
|
+
return tempDevices;
|
|
111
|
+
} else {
|
|
112
|
+
await setScanStatus("1");
|
|
113
|
+
var devices = await doScan();
|
|
114
|
+
tempDevices = devices;
|
|
115
|
+
await setScanStatus("0");
|
|
116
|
+
return devices;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
module.exports.scanNetwork = scanNetwork;
|
package/proxy/start.js
ADDED
package/proxy/wanip.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
const https = require('https');
|
|
2
|
+
// /proxy/wanip.js
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 从 ipip.net 获取当前公网 IP 地址
|
|
6
|
+
* @returns {Promise<string>} 公网 IP 地址(如 "114.253.239.240")
|
|
7
|
+
* @throws {Error} 请求失败、返回格式错误或 IP 无效时抛出错误
|
|
8
|
+
*/
|
|
9
|
+
async function getPublicIp() {
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
const url = 'https://myip.ipip.net/json';
|
|
12
|
+
|
|
13
|
+
const req = https.get(url, {
|
|
14
|
+
headers: {
|
|
15
|
+
'User-Agent': 'Node.js IP Fetcher'
|
|
16
|
+
}
|
|
17
|
+
}, (res) => {
|
|
18
|
+
let data = '';
|
|
19
|
+
|
|
20
|
+
// 监听数据块
|
|
21
|
+
res.on('data', chunk => {
|
|
22
|
+
data += chunk;
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// 响应结束
|
|
26
|
+
res.on('end', () => {
|
|
27
|
+
if (res.statusCode !== 200) {
|
|
28
|
+
return reject(new Error(`HTTP ${res.statusCode}: ${data}`));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const json = JSON.parse(data);
|
|
33
|
+
if (json.ret === 'ok' && json.data?.ip) {
|
|
34
|
+
const ip = json.data.ip.trim();
|
|
35
|
+
// 简单校验 IPv4 或 IPv6 格式
|
|
36
|
+
if (
|
|
37
|
+
/^(\d{1,3}\.){3}\d{1,3}$/.test(ip) ||
|
|
38
|
+
/^[a-fA-F0-9:]+$/.test(ip) // 粗略匹配 IPv6
|
|
39
|
+
) {
|
|
40
|
+
resolve(ip);
|
|
41
|
+
} else {
|
|
42
|
+
reject(new Error(`Invalid IP format: ${ip}`));
|
|
43
|
+
}
|
|
44
|
+
} else {
|
|
45
|
+
reject(new Error('Unexpected response format from ipip.net'));
|
|
46
|
+
}
|
|
47
|
+
} catch (parseErr) {
|
|
48
|
+
reject(new Error(`Failed to parse JSON: ${parseErr.message}`));
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
req.setTimeout(5000, () => {
|
|
54
|
+
req.destroy();
|
|
55
|
+
reject(new Error('Request timeout (5s)'));
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
req.on('error', (err) => {
|
|
59
|
+
reject(new Error(`Network error: ${err.message}`));
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
module.exports = {
|
|
65
|
+
getPublicIp
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// 使用示例
|
|
69
|
+
// (async () => {
|
|
70
|
+
// try {
|
|
71
|
+
// const ip = await getPublicIp();
|
|
72
|
+
// console.log('Your public IP is:', ip);
|
|
73
|
+
// } catch (err) {
|
|
74
|
+
// console.error('Failed to get IP:', err.message);
|
|
75
|
+
// }
|
|
76
|
+
// })();
|
|
Binary file
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7
|
+
<meta name="theme-color" content="#000000" />
|
|
8
|
+
<meta
|
|
9
|
+
name="description"
|
|
10
|
+
content="Web site created using create-react-app"
|
|
11
|
+
/>
|
|
12
|
+
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
|
13
|
+
<!--
|
|
14
|
+
manifest.json provides metadata used when your web app is installed on a
|
|
15
|
+
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
|
16
|
+
-->
|
|
17
|
+
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
|
18
|
+
<!--
|
|
19
|
+
Notice the use of %PUBLIC_URL% in the tags above.
|
|
20
|
+
It will be replaced with the URL of the `public` folder during the build.
|
|
21
|
+
Only files inside the `public` folder can be referenced from the HTML.
|
|
22
|
+
|
|
23
|
+
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
|
24
|
+
work correctly both with client-side routing and a non-root public URL.
|
|
25
|
+
Learn how to configure a non-root public URL by running `npm run build`.
|
|
26
|
+
-->
|
|
27
|
+
<title>React App</title>
|
|
28
|
+
</head>
|
|
29
|
+
<body>
|
|
30
|
+
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
31
|
+
<div id="root"></div>
|
|
32
|
+
<!--
|
|
33
|
+
This HTML file is a template.
|
|
34
|
+
If you open it directly in the browser, you will see an empty page.
|
|
35
|
+
|
|
36
|
+
You can add webfonts, meta tags, or analytics to this file.
|
|
37
|
+
The build step will place the bundled scripts into the <body> tag.
|
|
38
|
+
|
|
39
|
+
To begin the development, run `npm start` or `yarn start`.
|
|
40
|
+
To create a production bundle, use `npm run build` or `yarn build`.
|
|
41
|
+
-->
|
|
42
|
+
</body>
|
|
43
|
+
</html>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"short_name": "React App",
|
|
3
|
+
"name": "Create React App Sample",
|
|
4
|
+
"icons": [
|
|
5
|
+
{
|
|
6
|
+
"src": "favicon.ico",
|
|
7
|
+
"sizes": "64x64 32x32 24x24 16x16",
|
|
8
|
+
"type": "image/x-icon"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"src": "logo192.png",
|
|
12
|
+
"type": "image/png",
|
|
13
|
+
"sizes": "192x192"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"src": "logo512.png",
|
|
17
|
+
"type": "image/png",
|
|
18
|
+
"sizes": "512x512"
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"start_url": ".",
|
|
22
|
+
"display": "standalone",
|
|
23
|
+
"theme_color": "#000000",
|
|
24
|
+
"background_color": "#ffffff"
|
|
25
|
+
}
|
package/public/proxy.jpg
ADDED
|
Binary file
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
// server.js
|
|
2
|
+
const express = require('express');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const _fs = require('../proxy/fs.js');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const util = require('./util');
|
|
7
|
+
const net = require('net');
|
|
8
|
+
const os = require('os');
|
|
9
|
+
const { exec, execSync } = require('child_process');
|
|
10
|
+
const LocalProxy = require('../proxy/proxy');
|
|
11
|
+
|
|
12
|
+
const app = express();
|
|
13
|
+
const PORT = 8004;
|
|
14
|
+
const DEV = process.env.BLOCK_PROXY_DEV || 0;
|
|
15
|
+
const configPath = path.join(__dirname, '../config.json');
|
|
16
|
+
|
|
17
|
+
// 1. 托管 React build 后的静态文件
|
|
18
|
+
const staticPath = path.join(__dirname, '../build/');
|
|
19
|
+
app.use(express.static(staticPath));
|
|
20
|
+
|
|
21
|
+
// 2. (可选)处理 API 接口 —— 这里可以放你原来的“本地服务”逻辑
|
|
22
|
+
app.get('/api/hello', (req, res) => {
|
|
23
|
+
res.json({ message: 'Hello from Express!' });
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
app.get('/api/timezone', async (req, res) => {
|
|
27
|
+
res.status(200).json({ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone });
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// 获取服务器IP地址接口
|
|
31
|
+
app.get('/api/server-ip', async (req, res) => {
|
|
32
|
+
try {
|
|
33
|
+
const interfaces = os.networkInterfaces();
|
|
34
|
+
const ipAddresses = [];
|
|
35
|
+
|
|
36
|
+
// 遍历所有网络接口
|
|
37
|
+
for (const interfaceName in interfaces) {
|
|
38
|
+
const interface = interfaces[interfaceName];
|
|
39
|
+
for (const iface of interface) {
|
|
40
|
+
// 过滤掉内部地址和IPv6地址
|
|
41
|
+
if (!iface.internal && iface.family === 'IPv4') {
|
|
42
|
+
ipAddresses.push({
|
|
43
|
+
interface: interfaceName,
|
|
44
|
+
address: iface.address,
|
|
45
|
+
family: iface.family,
|
|
46
|
+
mac: iface.mac
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const isDocker = util.isRunningInDocker();
|
|
53
|
+
const response = {
|
|
54
|
+
ips: ipAddresses,
|
|
55
|
+
primary: ipAddresses.length > 0 ? ipAddresses[0].address : null,
|
|
56
|
+
docker: isDocker
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// 如果在Docker环境中,尝试获取宿主机IP
|
|
60
|
+
if (isDocker) {
|
|
61
|
+
response.hostIPs = util.getDockerHostIP();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
res.status(200).json(response);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
res.status(500).json({ error: 'Failed to get server IP addresses: ' + error.message });
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// 获取配置接口
|
|
71
|
+
app.get('/api/config', async (req, res) => {
|
|
72
|
+
try {
|
|
73
|
+
const configPath = path.join(__dirname, '../config.json');
|
|
74
|
+
if (fs.existsSync(configPath)) {
|
|
75
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
76
|
+
res.status(200).json(config);
|
|
77
|
+
} else {
|
|
78
|
+
res.status(404).json({ error: 'Config file not found' });
|
|
79
|
+
}
|
|
80
|
+
} catch (error) {
|
|
81
|
+
res.status(500).json({ error: 'Failed to read config file' + error.message });
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// 更新配置接口
|
|
86
|
+
app.post('/api/config', async (req, res) => {
|
|
87
|
+
try {
|
|
88
|
+
let body = '';
|
|
89
|
+
req.on('data', chunk => {
|
|
90
|
+
body += chunk.toString();
|
|
91
|
+
});
|
|
92
|
+
req.on('end', () => {
|
|
93
|
+
try {
|
|
94
|
+
const newConfig = JSON.parse(body);
|
|
95
|
+
const configPath = path.join(__dirname, '../config.json');
|
|
96
|
+
// fs.writeFileSync(configPath, JSON.stringify(newConfig, null, 2));
|
|
97
|
+
_fs.writeConfig(newConfig);
|
|
98
|
+
res.status(200).json({ message: 'Config updated successfully' });
|
|
99
|
+
} catch (err) {
|
|
100
|
+
res.status(400).json({ error: 'Invalid JSON or write error: ' + err.message });
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
} catch (error) {
|
|
104
|
+
res.status(500).json({ error: 'Failed to update config' });
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// 在 devServer.js 的 onBeforeSetupMiddleware 中添加新的 API 端点
|
|
109
|
+
app.post('/api/update-devices', async (req, res) => {
|
|
110
|
+
try {
|
|
111
|
+
// 调用本地代理的更新设备方法
|
|
112
|
+
await LocalProxy.updateDevices();
|
|
113
|
+
res.status(200).json({ message: 'Devices updated successfully' });
|
|
114
|
+
} catch (error) {
|
|
115
|
+
res.status(500).json({ error: 'Failed to update devices: ' + error.message });
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
function sendRestartMessage(callback) {
|
|
120
|
+
const configFileContent = fs.readFileSync(configPath, 'utf-8');
|
|
121
|
+
const loadedConfig = JSON.parse(configFileContent);
|
|
122
|
+
loadedConfig.progress_time_stamp = new Date().getTime().toString();
|
|
123
|
+
// fs.writeFileSync(configPath, JSON.stringify(loadedConfig, null, 2));
|
|
124
|
+
_fs.writeConfig(loadedConfig);
|
|
125
|
+
setTimeout(() => {
|
|
126
|
+
callback();
|
|
127
|
+
}, 300);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// 重启代理接口
|
|
131
|
+
app.post('/api/restart', async (req, res) => {
|
|
132
|
+
try {
|
|
133
|
+
// 调用本地代理的重启方法
|
|
134
|
+
// sendRestartMessage(function() {
|
|
135
|
+
// res.status(200).json({ message: 'Proxy restarted successfully' });
|
|
136
|
+
// });
|
|
137
|
+
await LocalProxy.restart(function() {
|
|
138
|
+
res.status(200).json({ message: 'Proxy restarted successfully' });
|
|
139
|
+
});
|
|
140
|
+
} catch (error) {
|
|
141
|
+
res.status(500).json({ error: 'Failed to restart proxy: ' + error.message });
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// post /proxy/https://www.baidu.com/...
|
|
146
|
+
app.use(/\/proxy\/*/, async (req, res) => {
|
|
147
|
+
try {
|
|
148
|
+
// 提取目标URL,移除'/proxy/'前缀
|
|
149
|
+
const targetUrl = util.extractTrailingHttpUrl(req.originalUrl);
|
|
150
|
+
|
|
151
|
+
// 解析URL以确定使用哪个HTTP模块
|
|
152
|
+
const parsedUrl = new URL(targetUrl);
|
|
153
|
+
const httpClient = parsedUrl.protocol === 'https:' ? https : http;
|
|
154
|
+
|
|
155
|
+
// 准备代理请求选项
|
|
156
|
+
const proxyOptions = {
|
|
157
|
+
hostname: parsedUrl.hostname,
|
|
158
|
+
port: parsedUrl.port || (parsedUrl.protocol === 'https:' ? 443 : 80),
|
|
159
|
+
path: parsedUrl.pathname + parsedUrl.search,
|
|
160
|
+
method: req.method,
|
|
161
|
+
headers: { ...req.headers }
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// 移除不应该转发的头部
|
|
165
|
+
delete proxyOptions.headers['host'];
|
|
166
|
+
delete proxyOptions.headers['connection'];
|
|
167
|
+
|
|
168
|
+
// 创建代理请求
|
|
169
|
+
const proxyReq = httpClient.request(proxyOptions, (proxyRes) => {
|
|
170
|
+
// 转发状态码
|
|
171
|
+
res.status(proxyRes.statusCode);
|
|
172
|
+
|
|
173
|
+
// 转发头部(除了content-length会自动设置)
|
|
174
|
+
Object.keys(proxyRes.headers).forEach(key => {
|
|
175
|
+
if (key.toLowerCase() !== 'content-length') {
|
|
176
|
+
res.setHeader(key, proxyRes.headers[key]);
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// 管道传输响应数据
|
|
181
|
+
proxyRes.pipe(res);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// 处理代理请求错误
|
|
185
|
+
proxyReq.on('error', (err) => {
|
|
186
|
+
res.status(502).json({ error: `代理错误: ${err.message}` });
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// 如果有请求体则转发
|
|
190
|
+
if (['POST', 'PUT', 'PATCH'].includes(req.method)) {
|
|
191
|
+
req.pipe(proxyReq);
|
|
192
|
+
} else {
|
|
193
|
+
proxyReq.end();
|
|
194
|
+
}
|
|
195
|
+
} catch (error) {
|
|
196
|
+
res.status(502).json({ error: `代理错误: ${error.message}` });
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// 3. 所有其他请求都返回 index.html(支持 React Router 的前端路由)
|
|
201
|
+
app.get((req, res) => {
|
|
202
|
+
res.sendFile(path.join(__dirname, 'build', 'index.html'));
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
module.exports = {
|
|
206
|
+
init: function() {
|
|
207
|
+
// 启动服务器
|
|
208
|
+
app.listen(PORT, async () => {
|
|
209
|
+
console.log(`✅ 静态服务器运行在 http://localhost:${PORT}`);
|
|
210
|
+
// 如果是开发环境,则启动SSR服务,开启端口3000
|
|
211
|
+
if (DEV === '1') {
|
|
212
|
+
const child = exec('npm run craco', { cwd: path.join(__dirname,'../') });
|
|
213
|
+
console.log('启动 craco start');
|
|
214
|
+
|
|
215
|
+
// Stream the output to the console
|
|
216
|
+
child.stdout.on('data', (data) => {
|
|
217
|
+
process.stdout.write(data);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
child.stderr.on('data', (data) => {
|
|
221
|
+
process.stderr.write(data);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
child.on('close', (code) => {
|
|
225
|
+
console.log(`craco process exited with code ${code}`);
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
// 启动本地代理
|
|
229
|
+
await LocalProxy.init();
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
};
|
package/server/start.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// express/start.js
|
|
2
|
+
// 总入口,根据 config.enable_express 参数判断是启动后台+代理,还是只启动代理
|
|
3
|
+
// 如果后台没启动,直接访问 http://代理IP:8001,打开开关后重启服务即可
|
|
4
|
+
const _fs = require('../proxy/fs.js');
|
|
5
|
+
|
|
6
|
+
(async function() {
|
|
7
|
+
const config = await _fs.readConfig();
|
|
8
|
+
if (config.enable_socks5 && config.enable_socks5 == "1") {
|
|
9
|
+
console.log("启动Socks5");
|
|
10
|
+
const Socks5 = require("../socks5/server.js");
|
|
11
|
+
Socks5.init();
|
|
12
|
+
}
|
|
13
|
+
if (config.enable_express && config.enable_express == "1") {
|
|
14
|
+
console.log("启动express");
|
|
15
|
+
const ExpressServer = require("./express.js");
|
|
16
|
+
ExpressServer.init();
|
|
17
|
+
} else {
|
|
18
|
+
console.log("只启动proxy");
|
|
19
|
+
const LocalProxy = require('../proxy/proxy');
|
|
20
|
+
await LocalProxy.init();
|
|
21
|
+
}
|
|
22
|
+
})();
|
|
23
|
+
|
|
24
|
+
|