Skip to content

Commit 5977e24

Browse files
committed
feat: 更新网络代理配置
1 parent aa51dc3 commit 5977e24

13 files changed

Lines changed: 68 additions & 64 deletions

File tree

electron/main/apis/netease/core/ncbl.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { randomBytes, randomUUID as cryptoRandomUUID } from "node:crypto";
22
import * as zlib from "node:zlib";
3-
import { net } from "electron";
43
import { CLIENT_LOG3_DOMAIN } from "./config";
4+
import { fetchWithProxy } from "@main/utils/proxy";
55

66
interface NeteaseLogRecord {
77
time: number;
@@ -438,7 +438,7 @@ export const doUpload = async (
438438
): Promise<UploadResult> => {
439439
const payload = encryptNCBL(metaJson, body);
440440
const multipart = buildMultipart(payload);
441-
const resp = await net.fetch(
441+
const resp = await fetchWithProxy(
442442
`${CLIENT_LOG3_DOMAIN}/api/clientlog/encrypt/upload?multiupload=true`,
443443
{
444444
method: "POST",

electron/main/apis/netease/core/request.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@
22
* Netease API 请求层
33
*
44
* 核心职责:根据加密方式(weapi / linuxapi / eapi / api / xeapi)构造 URL、headers、form body,
5-
* 处理 cookie 合并、响应解密、状态码归一化。使用 Node 原生 fetch。
5+
* 处理 cookie 合并、响应解密、状态码归一化
66
*/
77

88
import { randomBytes } from "node:crypto";
9-
import { net } from "electron";
109
import {
1110
API_DOMAIN,
1211
DOMAIN,
@@ -21,6 +20,7 @@ import { cookieObjToString, cookieToJson } from "./cookie";
2120
import * as encrypt from "./crypto";
2221
import { getAnonymousToken, getDeviceId } from "./device";
2322
import { ensureXeapiKey, getXeapiSession, updateXeapiSession } from "./xeapi";
23+
import { fetchWithProxy } from "@main/utils/proxy";
2424

2525
/** 调用方传入的可选参数 */
2626
export interface RequestOptions {
@@ -267,7 +267,7 @@ export const createRequest = async (
267267

268268
let res: Response;
269269
try {
270-
res = await net.fetch(url, {
270+
res = await fetchWithProxy(url, {
271271
method: "POST",
272272
headers,
273273
body,
@@ -279,7 +279,7 @@ export const createRequest = async (
279279
throw new NeteaseRequestError(answer);
280280
}
281281

282-
// 收集 set-cookie(Node fetch 通过 headers.getSetCookie 暴露原始多值头)
282+
// 收集 set-cookie
283283
const setCookie =
284284
(res.headers as unknown as { getSetCookie?: () => string[] }).getSetCookie?.() ??
285285
(res.headers.get("set-cookie") ? [res.headers.get("set-cookie") as string] : []);

electron/main/apis/netease/core/xeapi.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
* 拉取 X25519 公钥包(缓存于进程),首次请求后服务端经响应头下发会话密钥,后续请求复用。
66
*/
77

8-
import { net } from "electron";
98
import { API_DOMAIN, UA_MAP } from "./config";
109
import { xeapiSign, xeapiDecryptPublicKey, type XeapiPublicKey } from "./crypto";
10+
import { fetchWithProxy } from "@main/utils/proxy";
1111

1212
let publicKeyState: XeapiPublicKey | null = null;
1313
let sessionId = "";
@@ -41,7 +41,7 @@ const fetchPublicKey = async (
4141
uid: "",
4242
};
4343

44-
const res = await net.fetch(`${API_DOMAIN}/api/gorilla/anti/crawler/security/key/get`, {
44+
const res = await fetchWithProxy(`${API_DOMAIN}/api/gorilla/anti/crawler/security/key/get`, {
4545
method: "POST",
4646
headers: {
4747
"User-Agent": UA_MAP.api.android,

electron/main/core/index.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import {
2323
dispose as disposePlaybackBridge,
2424
} from "@main/plugins/playbackBridge";
2525
import { registerCacheScheme, handleCacheProtocol } from "@main/utils/protocol";
26-
import { applyNetworkProxy } from "@main/utils/proxy";
2726
import { startServer, stopServer } from "@main/server";
2827
import { initUpdater, disposeUpdater } from "@main/services/updater";
2928
import { coreLog, initLogger } from "@main/utils/logger";
@@ -102,8 +101,6 @@ export const initApp = (): void => {
102101
electronApp.setAppUserModelId("top.imsyy.splayer-next");
103102
// 注册 cache:// 协议处理
104103
handleCacheProtocol();
105-
// 应用持久化网络代理
106-
void applyNetworkProxy();
107104
app.on("browser-window-created", (_, window) => {
108105
optimizer.watchWindowShortcuts(window);
109106
});

electron/main/ipc/config.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ import { isWin } from "@main/utils/config";
3131
import { startServer, stopServer } from "@main/server";
3232
import { setOrpheusProtocolRegistered } from "@main/services/orpheus";
3333
import { setTaskbarThumbnailEnabled } from "@main/services/thumbnail";
34-
import { applyNetworkProxy } from "@main/utils/proxy";
3534

3635
/** 配置写入后的副作用 */
3736
const applyConfigChange = (keyPath: string, value: unknown): void => {
@@ -71,11 +70,6 @@ const applyConfigChange = (keyPath: string, value: unknown): void => {
7170
case "system.registerOrpheusProtocol":
7271
setOrpheusProtocolRegistered(value as boolean);
7372
break;
74-
case "system.networkProxy.protocol":
75-
case "system.networkProxy.host":
76-
case "system.networkProxy.port":
77-
void applyNetworkProxy();
78-
break;
7973
case "externalApi.enabled":
8074
void (value ? startServer() : stopServer());
8175
break;

electron/main/utils/proxy.ts

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,52 @@
1-
import { net, session } from "electron";
2-
import type { ProxyConfig } from "electron";
31
import { store } from "@main/store";
42
import { systemLog } from "@main/utils/logger";
3+
import { fetch as undiciFetch, ProxyAgent, Socks5ProxyAgent } from "undici";
4+
import type { Dispatcher } from "undici";
55

6-
const MAIN_PARTITION = "persist:main";
76
const PROXY_TEST_URL = "https://www.baidu.com";
87

9-
/**
10-
* 将网络代理配置转换为 Electron ProxyConfig。
11-
* system 模式使用 { mode: "system" } 跟随系统代理;off 直连。
12-
*/
13-
const buildProxyConfig = (): ProxyConfig => {
8+
let proxyAgent: Dispatcher | null = null;
9+
let proxyAgentUrl = "";
10+
11+
const isManualProxyProtocol = (value: string): value is "http" | "https" | "socks5" =>
12+
value === "http" || value === "https" || value === "socks5";
13+
14+
/** 当前手动代理地址;off 或配置无效时返回 null,保持原生直连行为 */
15+
export const getNetworkProxyUrl = (): string | null => {
1416
const config = store.get("system.networkProxy");
15-
if (config.protocol === "system") return { mode: "system" };
16-
if (config.protocol === "off") return { mode: "direct" };
17+
if (!isManualProxyProtocol(config.protocol)) return null;
1718
const host = config.host.trim();
1819
const port = Number(config.port);
19-
if (!host || !Number.isInteger(port) || port < 1 || port > 65535) return { mode: "direct" };
20-
return { proxyRules: `${config.protocol}://${host}:${port}` };
20+
if (!host || !Number.isInteger(port) || port < 1 || port > 65535) return null;
21+
return `${config.protocol}://${host}:${port}`;
2122
};
2223

23-
/** 应用网络代理配置到 Electron 会话 */
24-
export const applyNetworkProxy = async (): Promise<void> => {
25-
const proxyConfig = buildProxyConfig();
26-
try {
27-
await Promise.all([
28-
session.defaultSession.setProxy(proxyConfig),
29-
session.fromPartition(MAIN_PARTITION).setProxy(proxyConfig),
30-
]);
31-
systemLog.info(
32-
`[proxy] mode=${proxyConfig.mode ?? "fixed_servers"}${proxyConfig.proxyRules ? ` rules=${proxyConfig.proxyRules}` : ""}`,
33-
);
34-
} catch (err) {
35-
systemLog.warn("[proxy] apply failed", err);
24+
const getProxyDispatcher = (): Dispatcher | undefined => {
25+
const url = getNetworkProxyUrl();
26+
if (!url) return undefined;
27+
if (!proxyAgent || proxyAgentUrl !== url) {
28+
proxyAgent?.close().catch(() => {});
29+
proxyAgent = url.startsWith("socks5://") ? new Socks5ProxyAgent(url) : new ProxyAgent(url);
30+
proxyAgentUrl = url;
31+
systemLog.info(`[proxy] node fetch proxy=${url}`);
3632
}
33+
return proxyAgent;
34+
};
35+
36+
/** Node fetch 包装:关闭代理时完全等价于原生 fetch,开启代理时才注入 dispatcher */
37+
export const fetchWithProxy = (input: string | URL, init?: RequestInit): Promise<Response> => {
38+
const dispatcher = getProxyDispatcher();
39+
if (!dispatcher) return fetch(input, init);
40+
return undiciFetch(input, { ...(init as RequestInit), dispatcher } as Parameters<
41+
typeof undiciFetch
42+
>[1]) as unknown as Promise<Response>;
3743
};
3844

3945
/** 测试当前代理是否可用 */
4046
export const testNetworkProxy = async (): Promise<boolean> => {
41-
const proxyConfig = buildProxyConfig();
42-
if (proxyConfig.mode === "system") return true;
43-
if (proxyConfig.mode === "direct") return false;
47+
if (!getNetworkProxyUrl()) return false;
4448
try {
45-
await applyNetworkProxy();
46-
const res = await net.fetch(PROXY_TEST_URL, { signal: AbortSignal.timeout(8000) });
49+
const res = await fetchWithProxy(PROXY_TEST_URL, { signal: AbortSignal.timeout(8000) });
4750
return res.ok;
4851
} catch (err) {
4952
systemLog.warn("[proxy] test failed", err);

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"license": "AGPL-3.0",
2424
"license-file": "LICENSE",
2525
"engines": {
26-
"node": ">=22",
26+
"node": ">=22.19.0",
2727
"npm": ">=11",
2828
"pnpm": ">=10"
2929
},
@@ -57,6 +57,7 @@
5757
"electron-updater": "^6.8.3",
5858
"font-list": "^2.0.2",
5959
"hono": "^4.12.25",
60+
"undici": "^8.7.0",
6061
"ws": "^8.20.1"
6162
},
6263
"devDependencies": {

pnpm-lock.yaml

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

shared/defaults/settings.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,9 @@ export const defaultSystemConfig: SystemConfig = {
146146
onboardingCompleted: false,
147147
neteaseRealIp: false,
148148
networkProxy: {
149-
protocol: "system",
149+
protocol: "off",
150150
host: "127.0.0.1",
151-
port: 80,
151+
port: 7890,
152152
},
153153
neteaseScrobbleEnabled: false,
154154
neteaseScrobbleMode: "ncbl",

shared/types/settings.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,11 +227,11 @@ export interface ExternalApiSettings {
227227
}
228228

229229
/** 网络代理协议 */
230-
export type NetworkProxyProtocol = "system" | "off" | "http" | "https";
230+
export type NetworkProxyProtocol = "off" | "http" | "https" | "socks5";
231231

232232
/** 网络代理配置 */
233233
export interface NetworkProxySettings {
234-
/** 代理协议;system 跟随系统,off 直连 */
234+
/** 代理协议;off 时不改变原始请求路径 */
235235
protocol: NetworkProxyProtocol;
236236
/** 代理服务器地址 */
237237
host: string;

0 commit comments

Comments
 (0)