pac-proxy-cli 0.1.7 → 1.0.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/.env +1 -3
- package/README.md +71 -63
- package/bin/cli.js +3 -0
- package/dist/assets/index-BT0RwRSQ.css +1 -0
- package/dist/assets/index-BeZfrxlH.js +27 -0
- package/dist/index.html +16 -0
- package/lib/capture-log.js +138 -0
- package/lib/index.js +22 -0
- package/lib/local-store.js +122 -0
- package/lib/mitm-proxy-server.js +143 -0
- package/lib/pac-file.js +49 -0
- package/lib/pac-match.js +44 -0
- package/lib/proxy-server.js +247 -0
- package/lib/server.js +239 -0
- package/lib/socks-http.js +47 -0
- package/lib/system-proxy.js +264 -0
- package/lib/traffic-log.js +14 -0
- package/package.json +37 -15
- package/bin/proxy.js +0 -30
- package/public/index.html +0 -732
- package/src/capture/store.js +0 -40
- package/src/cli/panel.js +0 -23
- package/src/cli/start.js +0 -62
- package/src/pac/store.js +0 -186
- package/src/panel/server.js +0 -145
- package/src/proxy/server.js +0 -262
- package/src/sysproxy/index.js +0 -270
package/src/proxy/server.js
DELETED
|
@@ -1,262 +0,0 @@
|
|
|
1
|
-
import http from 'node:http';
|
|
2
|
-
import net from 'node:net';
|
|
3
|
-
import stream from 'node:stream';
|
|
4
|
-
import { SocksClient } from 'socks';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* 创建 HTTP 代理服务,上游为 SOCKS5 或 Node Server
|
|
8
|
-
*/
|
|
9
|
-
export function createProxyServer(port, upstream, opts = {}) {
|
|
10
|
-
const captureStore = opts.captureStore || null;
|
|
11
|
-
|
|
12
|
-
const server = http.createServer((clientReq, clientRes) => {
|
|
13
|
-
const targetHost = clientReq.headers.host;
|
|
14
|
-
if (!targetHost) {
|
|
15
|
-
clientRes.writeHead(400, { 'Content-Type': 'text/plain' });
|
|
16
|
-
clientRes.end('Missing Host header');
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
const url = clientReq.url || '/';
|
|
20
|
-
const method = clientReq.method || 'GET';
|
|
21
|
-
const fullUrl = (url.startsWith('http://') || url.startsWith('https://')) ? url : `http://${targetHost}${url}`;
|
|
22
|
-
|
|
23
|
-
let captureId = null;
|
|
24
|
-
if (captureStore) {
|
|
25
|
-
captureId = captureStore.add({
|
|
26
|
-
time: Date.now(),
|
|
27
|
-
method,
|
|
28
|
-
url: fullUrl,
|
|
29
|
-
host: targetHost,
|
|
30
|
-
requestHeaders: copyHeaders(clientReq.headers),
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const onConnect = (err, socketOrRes) => {
|
|
35
|
-
if (err) {
|
|
36
|
-
if (captureId) captureStore.update(captureId, { statusCode: 502 });
|
|
37
|
-
clientRes.writeHead(502, { 'Content-Type': 'text/plain' });
|
|
38
|
-
clientRes.end('Upstream connect failed: ' + err.message);
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
const socket = socketOrRes;
|
|
42
|
-
let reqBodySize = 0;
|
|
43
|
-
let resBodySize = 0;
|
|
44
|
-
clientReq.on('data', (chunk) => { reqBodySize += chunk.length; });
|
|
45
|
-
|
|
46
|
-
const parser = new HttpResponseParser((statusCode, headers) => {
|
|
47
|
-
if (captureId) {
|
|
48
|
-
captureStore.update(captureId, {
|
|
49
|
-
statusCode,
|
|
50
|
-
responseHeaders: headers,
|
|
51
|
-
requestBodySize: reqBodySize,
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
clientRes.writeHead(statusCode, headers);
|
|
55
|
-
});
|
|
56
|
-
parser.on('data', (chunk) => { resBodySize += chunk.length; });
|
|
57
|
-
parser.on('end', () => {
|
|
58
|
-
if (captureId) captureStore.update(captureId, { responseBodySize: resBodySize });
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
socket.pipe(parser);
|
|
62
|
-
parser.pipe(clientRes);
|
|
63
|
-
|
|
64
|
-
const firstLine = `${method} ${url} HTTP/1.1\r\n`;
|
|
65
|
-
const headers = Object.entries(clientReq.headers)
|
|
66
|
-
.filter(([k]) => !/^host$/i.test(k))
|
|
67
|
-
.map(([k, v]) => `${k}: ${v}`)
|
|
68
|
-
.join('\r\n');
|
|
69
|
-
socket.write(firstLine + 'Host: ' + targetHost + '\r\n' + headers + '\r\n\r\n');
|
|
70
|
-
clientReq.on('data', (chunk) => socket.write(chunk));
|
|
71
|
-
clientReq.on('end', () => socket.end());
|
|
72
|
-
|
|
73
|
-
socket.on('error', () => {});
|
|
74
|
-
clientReq.on('error', () => socket.destroy());
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
if (upstream.type === 'socks5') {
|
|
78
|
-
const [host, portStr] = targetHost.split(':');
|
|
79
|
-
const port = portStr ? parseInt(portStr, 10) : 80;
|
|
80
|
-
connectViaSocks(upstream, host, port, onConnect);
|
|
81
|
-
} else if (upstream.type === 'server') {
|
|
82
|
-
const token = typeof opts.getProxyToken === 'function' ? opts.getProxyToken() : null;
|
|
83
|
-
forwardViaServerHttp(upstream, fullUrl, clientReq, clientRes, captureId, captureStore, token);
|
|
84
|
-
} else {
|
|
85
|
-
clientRes.writeHead(502);
|
|
86
|
-
clientRes.end('Unknown upstream type');
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
server.on('connect', (req, clientSocket, head) => {
|
|
91
|
-
const target = req.url;
|
|
92
|
-
|
|
93
|
-
let captureId = null;
|
|
94
|
-
if (captureStore) {
|
|
95
|
-
captureId = captureStore.add({
|
|
96
|
-
time: Date.now(),
|
|
97
|
-
method: 'CONNECT',
|
|
98
|
-
url: target,
|
|
99
|
-
host: target,
|
|
100
|
-
requestHeaders: copyHeaders(req.headers),
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const onConnect = (err, result) => {
|
|
105
|
-
if (err) {
|
|
106
|
-
clientSocket.write('HTTP/1.1 502 Bad Gateway\r\n\r\n');
|
|
107
|
-
clientSocket.end();
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
if (captureId && result.statusCode) captureStore.update(captureId, { statusCode: result.statusCode });
|
|
111
|
-
clientSocket.write('HTTP/1.1 200 Connection Established\r\n\r\n');
|
|
112
|
-
clientSocket.write(head);
|
|
113
|
-
result.socket.pipe(clientSocket);
|
|
114
|
-
clientSocket.pipe(result.socket);
|
|
115
|
-
result.socket.on('error', () => {});
|
|
116
|
-
clientSocket.on('error', () => {});
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
if (upstream.type === 'socks5') {
|
|
120
|
-
const [host, portStr] = target.split(':');
|
|
121
|
-
const port = parseInt(portStr, 10) || 443;
|
|
122
|
-
connectViaSocks(upstream, host, port, (err, socket) => {
|
|
123
|
-
if (err) return onConnect(err);
|
|
124
|
-
onConnect(null, { socket, statusCode: 200 });
|
|
125
|
-
});
|
|
126
|
-
} else if (upstream.type === 'server') {
|
|
127
|
-
const token = typeof opts.getProxyToken === 'function' ? opts.getProxyToken() : null;
|
|
128
|
-
forwardViaServerConnect(upstream, target, clientSocket, head, captureId, captureStore, onConnect, token);
|
|
129
|
-
} else {
|
|
130
|
-
clientSocket.write('HTTP/1.1 502 Bad Gateway\r\n\r\n');
|
|
131
|
-
clientSocket.end();
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
server.listen(port);
|
|
136
|
-
return server;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
/** 通过远程 Server 转发 CONNECT(与 Server 建立 TCP 后发送 CONNECT,再隧道) */
|
|
141
|
-
function forwardViaServerConnect(upstream, target, clientSocket, head, captureId, captureStoreRef, callback, token) {
|
|
142
|
-
const u = upstream.parsed || new URL(upstream.url);
|
|
143
|
-
const port = parseInt(u.port, 10) || (u.protocol === 'https:' ? 443 : 80);
|
|
144
|
-
|
|
145
|
-
const socket = net.connect(port, u.hostname, () => {
|
|
146
|
-
let req = `CONNECT ${target} HTTP/1.1\r\nHost: ${target}\r\n`;
|
|
147
|
-
if (token) req += `Authorization: Bearer ${token}\r\n`;
|
|
148
|
-
req += '\r\n';
|
|
149
|
-
socket.write(req);
|
|
150
|
-
const chunks = [];
|
|
151
|
-
const onData = (chunk) => {
|
|
152
|
-
chunks.push(chunk);
|
|
153
|
-
const buf = Buffer.concat(chunks);
|
|
154
|
-
const idx = buf.indexOf('\r\n\r\n');
|
|
155
|
-
if (idx === -1) return;
|
|
156
|
-
socket.removeListener('data', onData);
|
|
157
|
-
const statusLine = buf.slice(0, buf.indexOf('\r\n')).toString();
|
|
158
|
-
const code = parseInt(statusLine.split(' ')[1], 10) || 200;
|
|
159
|
-
if (captureId && captureStoreRef) captureStoreRef.update(captureId, { statusCode: code });
|
|
160
|
-
if (code < 200 || code >= 300) {
|
|
161
|
-
socket.destroy();
|
|
162
|
-
return callback(new Error('Server CONNECT failed: ' + statusLine));
|
|
163
|
-
}
|
|
164
|
-
socket.write(head);
|
|
165
|
-
clientSocket.pipe(socket);
|
|
166
|
-
socket.pipe(clientSocket);
|
|
167
|
-
socket.on('error', () => {});
|
|
168
|
-
clientSocket.on('error', () => {});
|
|
169
|
-
callback(null, { socket, statusCode: code });
|
|
170
|
-
};
|
|
171
|
-
socket.on('data', onData);
|
|
172
|
-
});
|
|
173
|
-
socket.on('error', (err) => callback(err));
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/** 通过远程 Server 转发 HTTP 请求(把 Server 当 HTTP 代理用) */
|
|
177
|
-
function forwardViaServerHttp(upstream, fullUrl, clientReq, clientRes, captureId, captureStoreRef, token) {
|
|
178
|
-
const u = upstream.parsed || new URL(upstream.url);
|
|
179
|
-
const headers = { ...clientReq.headers, host: u.host };
|
|
180
|
-
if (token) headers.authorization = `Bearer ${token}`;
|
|
181
|
-
const opts = {
|
|
182
|
-
hostname: u.hostname,
|
|
183
|
-
port: u.port || (u.protocol === 'https:' ? 443 : 80),
|
|
184
|
-
path: fullUrl,
|
|
185
|
-
method: clientReq.method,
|
|
186
|
-
headers,
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
const proxyReq = http.request(opts, (proxyRes) => {
|
|
190
|
-
if (captureId && captureStoreRef) {
|
|
191
|
-
captureStoreRef.update(captureId, {
|
|
192
|
-
statusCode: proxyRes.statusCode,
|
|
193
|
-
responseHeaders: copyHeaders(proxyRes.headers),
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
let resBodySize = 0;
|
|
197
|
-
proxyRes.on('data', (chunk) => { resBodySize += chunk.length; });
|
|
198
|
-
proxyRes.on('end', () => { if (captureId && captureStoreRef) captureStoreRef.update(captureId, { responseBodySize: resBodySize }); });
|
|
199
|
-
clientRes.writeHead(proxyRes.statusCode, proxyRes.headers);
|
|
200
|
-
proxyRes.pipe(clientRes);
|
|
201
|
-
});
|
|
202
|
-
proxyReq.on('error', (err) => {
|
|
203
|
-
if (captureId && captureStoreRef) captureStoreRef.update(captureId, { statusCode: 502 });
|
|
204
|
-
clientRes.writeHead(502);
|
|
205
|
-
clientRes.end('Upstream failed: ' + err.message);
|
|
206
|
-
});
|
|
207
|
-
clientReq.pipe(proxyReq);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
function copyHeaders(h) {
|
|
211
|
-
const o = {};
|
|
212
|
-
for (const [k, v] of Object.entries(h)) o[k] = v;
|
|
213
|
-
return o;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
function connectViaSocks(upstream, host, port, callback) {
|
|
217
|
-
if (upstream.type !== 'socks5') return callback(new Error('unsupported upstream'));
|
|
218
|
-
SocksClient.createConnection({
|
|
219
|
-
proxy: { host: upstream.host, port: upstream.port, type: 5 },
|
|
220
|
-
command: 'connect',
|
|
221
|
-
destination: { host, port },
|
|
222
|
-
})
|
|
223
|
-
.then(({ socket }) => callback(null, socket))
|
|
224
|
-
.catch((err) => callback(err));
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/** 解析 HTTP 响应头,解析完后将 body 作为流输出 */
|
|
228
|
-
class HttpResponseParser extends stream.Transform {
|
|
229
|
-
constructor(onHeaders) {
|
|
230
|
-
super();
|
|
231
|
-
this._onHeaders = onHeaders;
|
|
232
|
-
this._done = false;
|
|
233
|
-
this._buf = '';
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
_transform(chunk, enc, cb) {
|
|
237
|
-
if (this._done) {
|
|
238
|
-
this.push(chunk);
|
|
239
|
-
return cb();
|
|
240
|
-
}
|
|
241
|
-
this._buf += chunk.toString('binary');
|
|
242
|
-
const idx = this._buf.indexOf('\r\n\r\n');
|
|
243
|
-
if (idx === -1) return cb();
|
|
244
|
-
|
|
245
|
-
const head = this._buf.slice(0, idx);
|
|
246
|
-
const body = this._buf.slice(idx + 4);
|
|
247
|
-
this._buf = '';
|
|
248
|
-
this._done = true;
|
|
249
|
-
|
|
250
|
-
const [statusLine, ...headerLines] = head.split('\r\n');
|
|
251
|
-
const statusCode = parseInt(statusLine.split(' ')[1], 10) || 200;
|
|
252
|
-
const headers = {};
|
|
253
|
-
for (const line of headerLines) {
|
|
254
|
-
const i = line.indexOf(':');
|
|
255
|
-
if (i > 0) headers[line.slice(0, i).trim()] = line.slice(i + 1).trim();
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
this._onHeaders(statusCode, headers);
|
|
259
|
-
this.push(Buffer.from(body, 'binary'));
|
|
260
|
-
cb();
|
|
261
|
-
}
|
|
262
|
-
}
|
package/src/sysproxy/index.js
DELETED
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
import { execSync } from 'node:child_process';
|
|
2
|
-
import { platform } from 'node:os';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* 应用系统代理
|
|
6
|
-
* @param {{ mode: 'pac', pacUrl: string } | { mode: 'global', proxyHost: string, proxyPort: number }} options
|
|
7
|
-
* @returns {{ ok: boolean, error?: string }}
|
|
8
|
-
*/
|
|
9
|
-
export function applySystemProxy(options) {
|
|
10
|
-
if (options.mode === 'global') {
|
|
11
|
-
const { proxyHost = '127.0.0.1', proxyPort } = options;
|
|
12
|
-
if (proxyPort == null) return { ok: false, error: '缺少 proxyPort' };
|
|
13
|
-
if (platform() === 'darwin') return applyMacGlobal(proxyHost, proxyPort);
|
|
14
|
-
if (platform() === 'win32') return applyWindowsGlobal(proxyHost, proxyPort);
|
|
15
|
-
return applyLinuxGlobal(proxyHost, proxyPort);
|
|
16
|
-
}
|
|
17
|
-
const { pacUrl } = options;
|
|
18
|
-
if (!pacUrl) return { ok: false, error: '缺少 pacUrl' };
|
|
19
|
-
if (platform() === 'darwin') return applyMacPac(pacUrl);
|
|
20
|
-
if (platform() === 'win32') return applyWindowsPac(pacUrl);
|
|
21
|
-
return applyLinuxPac(pacUrl);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* 清除系统代理
|
|
26
|
-
*/
|
|
27
|
-
export function clearSystemProxy() {
|
|
28
|
-
if (platform() === 'darwin') return clearMac();
|
|
29
|
-
if (platform() === 'win32') return clearWindows();
|
|
30
|
-
return clearLinux();
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* 读取当前系统代理状态(用于界面展示「当前配置」)
|
|
35
|
-
* @returns {{ mode: 'none'|'global'|'pac', proxyServer?: string, pacUrl?: string, error?: string }}
|
|
36
|
-
*/
|
|
37
|
-
export function getSystemProxyStatus() {
|
|
38
|
-
if (platform() === 'darwin') return getMacStatus();
|
|
39
|
-
if (platform() === 'win32') return getWindowsStatus();
|
|
40
|
-
return getLinuxStatus();
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function listMacServices() {
|
|
44
|
-
const list = execSync('networksetup -listallnetworkservices', { encoding: 'utf8' });
|
|
45
|
-
return list.split('\n').map((s) => s.trim()).filter((s) => s && !s.startsWith('*'));
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function getMacStatus() {
|
|
49
|
-
try {
|
|
50
|
-
const services = listMacServices();
|
|
51
|
-
const svc = services[0];
|
|
52
|
-
if (!svc) return { mode: 'none' };
|
|
53
|
-
let pacUrl = '';
|
|
54
|
-
let webEnabled = false;
|
|
55
|
-
let server = '';
|
|
56
|
-
let port = '';
|
|
57
|
-
try {
|
|
58
|
-
const pacOut = execSync(`networksetup -getautoproxyurl "${svc}"`, { encoding: 'utf8' });
|
|
59
|
-
const m = pacOut.match(/URL:\s*(.+)/);
|
|
60
|
-
pacUrl = (m && m[1] && m[1].trim()) || '';
|
|
61
|
-
} catch (_) {}
|
|
62
|
-
try {
|
|
63
|
-
const webOut = execSync(`networksetup -getwebproxy "${svc}"`, { encoding: 'utf8' });
|
|
64
|
-
webEnabled = /Enabled:\s*Yes/i.test(webOut);
|
|
65
|
-
const s = webOut.match(/Server:\s*(.+)/);
|
|
66
|
-
const p = webOut.match(/Port:\s*(\d+)/);
|
|
67
|
-
server = (s && s[1] && s[1].trim()) || '';
|
|
68
|
-
port = (p && p[1]) || '';
|
|
69
|
-
} catch (_) {}
|
|
70
|
-
if (pacUrl && pacUrl !== ' ') return { mode: 'pac', pacUrl };
|
|
71
|
-
if (webEnabled && server) return { mode: 'global', proxyServer: port ? `${server}:${port}` : server };
|
|
72
|
-
return { mode: 'none' };
|
|
73
|
-
} catch (e) {
|
|
74
|
-
return { mode: 'none', error: e.message || String(e) };
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function applyMacPac(pacUrl) {
|
|
79
|
-
try {
|
|
80
|
-
for (const svc of listMacServices()) {
|
|
81
|
-
try { execSync(`networksetup -setautoproxyurl "${svc}" "${pacUrl}"`, { encoding: 'utf8' }); } catch {}
|
|
82
|
-
}
|
|
83
|
-
for (const svc of listMacServices()) {
|
|
84
|
-
try {
|
|
85
|
-
execSync(`networksetup -setwebproxystate "${svc}" off`, { encoding: 'utf8' });
|
|
86
|
-
execSync(`networksetup -setsecurewebproxystate "${svc}" off`, { encoding: 'utf8' });
|
|
87
|
-
} catch {}
|
|
88
|
-
}
|
|
89
|
-
return { ok: true };
|
|
90
|
-
} catch (e) {
|
|
91
|
-
return { ok: false, error: e.message || String(e) };
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function applyMacGlobal(proxyHost, proxyPort) {
|
|
96
|
-
try {
|
|
97
|
-
for (const svc of listMacServices()) {
|
|
98
|
-
try { execSync(`networksetup -setautoproxyurl "${svc}" " "`, { encoding: 'utf8' }); } catch {}
|
|
99
|
-
}
|
|
100
|
-
for (const svc of listMacServices()) {
|
|
101
|
-
try {
|
|
102
|
-
execSync(`networksetup -setwebproxy "${svc}" "${proxyHost}" ${proxyPort}`, { encoding: 'utf8' });
|
|
103
|
-
execSync(`networksetup -setsecurewebproxy "${svc}" "${proxyHost}" ${proxyPort}`, { encoding: 'utf8' });
|
|
104
|
-
execSync(`networksetup -setwebproxystate "${svc}" on`, { encoding: 'utf8' });
|
|
105
|
-
execSync(`networksetup -setsecurewebproxystate "${svc}" on`, { encoding: 'utf8' });
|
|
106
|
-
} catch {}
|
|
107
|
-
}
|
|
108
|
-
return { ok: true };
|
|
109
|
-
} catch (e) {
|
|
110
|
-
return { ok: false, error: e.message || String(e) };
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function clearMac() {
|
|
115
|
-
try {
|
|
116
|
-
for (const svc of listMacServices()) {
|
|
117
|
-
try {
|
|
118
|
-
execSync(`networksetup -setautoproxyurl "${svc}" " "`, { encoding: 'utf8' });
|
|
119
|
-
execSync(`networksetup -setwebproxystate "${svc}" off`, { encoding: 'utf8' });
|
|
120
|
-
execSync(`networksetup -setsecurewebproxystate "${svc}" off`, { encoding: 'utf8' });
|
|
121
|
-
} catch {}
|
|
122
|
-
}
|
|
123
|
-
return { ok: true };
|
|
124
|
-
} catch (e) {
|
|
125
|
-
return { ok: false, error: e.message || String(e) };
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function applyWindowsPac(pacUrl) {
|
|
130
|
-
try {
|
|
131
|
-
const escaped = pacUrl.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
132
|
-
execSync(
|
|
133
|
-
`reg add "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" /v AutoConfigURL /t REG_SZ /d "${escaped}" /f`,
|
|
134
|
-
{ encoding: 'utf8', stdio: 'pipe' }
|
|
135
|
-
);
|
|
136
|
-
execSync(
|
|
137
|
-
'reg add "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" /v ProxyEnable /t REG_DWORD /d 0 /f',
|
|
138
|
-
{ encoding: 'utf8', stdio: 'pipe' }
|
|
139
|
-
);
|
|
140
|
-
return { ok: true };
|
|
141
|
-
} catch (e) {
|
|
142
|
-
return { ok: false, error: e.message || String(e) };
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function applyWindowsGlobal(proxyHost, proxyPort) {
|
|
147
|
-
try {
|
|
148
|
-
try {
|
|
149
|
-
execSync(
|
|
150
|
-
'reg delete "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" /v AutoConfigURL /f',
|
|
151
|
-
{ encoding: 'utf8', stdio: 'pipe' }
|
|
152
|
-
);
|
|
153
|
-
} catch (_) {}
|
|
154
|
-
const server = `${proxyHost}:${proxyPort}`;
|
|
155
|
-
execSync(
|
|
156
|
-
`reg add "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" /v ProxyServer /t REG_SZ /d "${server}" /f`,
|
|
157
|
-
{ encoding: 'utf8', stdio: 'pipe' }
|
|
158
|
-
);
|
|
159
|
-
execSync(
|
|
160
|
-
'reg add "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" /v ProxyEnable /t REG_DWORD /d 1 /f',
|
|
161
|
-
{ encoding: 'utf8', stdio: 'pipe' }
|
|
162
|
-
);
|
|
163
|
-
return { ok: true };
|
|
164
|
-
} catch (e) {
|
|
165
|
-
return { ok: false, error: e.message || String(e) };
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
function clearWindows() {
|
|
170
|
-
try {
|
|
171
|
-
execSync(
|
|
172
|
-
'reg delete "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" /v AutoConfigURL /f',
|
|
173
|
-
{ encoding: 'utf8', stdio: 'pipe' }
|
|
174
|
-
);
|
|
175
|
-
} catch (_) {}
|
|
176
|
-
try {
|
|
177
|
-
execSync(
|
|
178
|
-
'reg delete "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" /v ProxyServer /f',
|
|
179
|
-
{ encoding: 'utf8', stdio: 'pipe' }
|
|
180
|
-
);
|
|
181
|
-
} catch (_) {}
|
|
182
|
-
try {
|
|
183
|
-
execSync(
|
|
184
|
-
'reg add "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" /v ProxyEnable /t REG_DWORD /d 0 /f',
|
|
185
|
-
{ encoding: 'utf8', stdio: 'pipe' }
|
|
186
|
-
);
|
|
187
|
-
} catch (_) {}
|
|
188
|
-
return { ok: true };
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
function getWindowsStatus() {
|
|
192
|
-
try {
|
|
193
|
-
const key = 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings';
|
|
194
|
-
let proxyEnable = 0;
|
|
195
|
-
let proxyServer = '';
|
|
196
|
-
let autoConfigUrl = '';
|
|
197
|
-
try {
|
|
198
|
-
const out = execSync(`reg query "${key}" /v ProxyEnable`, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] });
|
|
199
|
-
const m = out.match(/ProxyEnable\s+REG_DWORD\s+0x(\d+)/);
|
|
200
|
-
if (m) proxyEnable = parseInt(m[1], 16);
|
|
201
|
-
} catch (_) {}
|
|
202
|
-
try {
|
|
203
|
-
const out = execSync(`reg query "${key}" /v ProxyServer`, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] });
|
|
204
|
-
const m = out.match(/ProxyServer\s+REG_SZ\s+(.+)/);
|
|
205
|
-
if (m) proxyServer = m[1].trim();
|
|
206
|
-
} catch (_) {}
|
|
207
|
-
try {
|
|
208
|
-
const out = execSync(`reg query "${key}" /v AutoConfigURL`, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] });
|
|
209
|
-
const m = out.match(/AutoConfigURL\s+REG_SZ\s+(.+)/);
|
|
210
|
-
if (m) autoConfigUrl = m[1].trim();
|
|
211
|
-
} catch (_) {}
|
|
212
|
-
if (autoConfigUrl) return { mode: 'pac', pacUrl: autoConfigUrl };
|
|
213
|
-
if (proxyEnable && proxyServer) return { mode: 'global', proxyServer };
|
|
214
|
-
return { mode: 'none' };
|
|
215
|
-
} catch (e) {
|
|
216
|
-
return { mode: 'none', error: e.message || String(e) };
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
function applyLinuxPac(pacUrl) {
|
|
221
|
-
try {
|
|
222
|
-
execSync('gsettings set org.gnome.system.proxy mode auto', { encoding: 'utf8', stdio: 'pipe' });
|
|
223
|
-
execSync(`gsettings set org.gnome.system.proxy autoconfig-url '${pacUrl}'`, { encoding: 'utf8', stdio: 'pipe' });
|
|
224
|
-
return { ok: true };
|
|
225
|
-
} catch (e) {
|
|
226
|
-
return { ok: false, error: e.message || '仅支持 GNOME 桌面;可手动设置环境变量 http_proxy' };
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
function applyLinuxGlobal(proxyHost, proxyPort) {
|
|
231
|
-
try {
|
|
232
|
-
execSync('gsettings set org.gnome.system.proxy mode manual', { encoding: 'utf8', stdio: 'pipe' });
|
|
233
|
-
execSync(`gsettings set org.gnome.system.proxy.http host '${proxyHost}'`, { encoding: 'utf8', stdio: 'pipe' });
|
|
234
|
-
execSync(`gsettings set org.gnome.system.proxy.http port ${proxyPort}`, { encoding: 'utf8', stdio: 'pipe' });
|
|
235
|
-
execSync(`gsettings set org.gnome.system.proxy.https host '${proxyHost}'`, { encoding: 'utf8', stdio: 'pipe' });
|
|
236
|
-
execSync(`gsettings set org.gnome.system.proxy.https port ${proxyPort}`, { encoding: 'utf8', stdio: 'pipe' });
|
|
237
|
-
return { ok: true };
|
|
238
|
-
} catch (e) {
|
|
239
|
-
return { ok: false, error: e.message || String(e) };
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
function clearLinux() {
|
|
244
|
-
try {
|
|
245
|
-
execSync('gsettings set org.gnome.system.proxy mode none', { encoding: 'utf8', stdio: 'pipe' });
|
|
246
|
-
return { ok: true };
|
|
247
|
-
} catch (e) {
|
|
248
|
-
return { ok: false, error: e.message || String(e) };
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
function getLinuxStatus() {
|
|
253
|
-
try {
|
|
254
|
-
const modeOut = execSync('gsettings get org.gnome.system.proxy mode', { encoding: 'utf8', stdio: 'pipe' });
|
|
255
|
-
const mode = (modeOut && modeOut.trim().replace(/^'|'$/g, '')) || 'none';
|
|
256
|
-
if (mode === 'auto') {
|
|
257
|
-
const urlOut = execSync('gsettings get org.gnome.system.proxy autoconfig-url', { encoding: 'utf8', stdio: 'pipe' });
|
|
258
|
-
const pacUrl = (urlOut && urlOut.trim().replace(/^'|'$/g, '')) || '';
|
|
259
|
-
if (pacUrl) return { mode: 'pac', pacUrl };
|
|
260
|
-
}
|
|
261
|
-
if (mode === 'manual') {
|
|
262
|
-
const h = execSync('gsettings get org.gnome.system.proxy.http host', { encoding: 'utf8', stdio: 'pipe' }).trim().replace(/^'|'$/g, '');
|
|
263
|
-
const p = execSync('gsettings get org.gnome.system.proxy.http port', { encoding: 'utf8', stdio: 'pipe' }).trim();
|
|
264
|
-
if (h) return { mode: 'global', proxyServer: p ? `${h}:${p}` : h };
|
|
265
|
-
}
|
|
266
|
-
return { mode: 'none' };
|
|
267
|
-
} catch (e) {
|
|
268
|
-
return { mode: 'none', error: e.message || String(e) };
|
|
269
|
-
}
|
|
270
|
-
}
|