Skip to content

Commit 46d2509

Browse files
authored
✨ feat: add network proxy for desktop (#7848)
* add network proxy * update network proxy * refactor network proxy * support network proxy * fix types * fix lint * fix lint
1 parent 88c8617 commit 46d2509

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+2316
-27
lines changed

apps/desktop/.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
lockfile=false
22
shamefully-hoist=true
3+
34
electron_mirror=https://npmmirror.com/mirrors/electron/
45
electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/

apps/desktop/package.json

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "lobehub-desktop-dev",
3-
"version": "0.0.10",
3+
"version": "0.0.0",
44
"description": "LobeHub Desktop Application",
55
"homepage": "https://lobehub.com",
66
"repository": {
@@ -24,6 +24,7 @@
2424
"lint": "eslint --cache ",
2525
"pg-server": "bun run scripts/pglite-server.ts",
2626
"start": "electron-vite preview",
27+
"test": "vite --run",
2728
"typecheck": "tsgo --noEmit -p tsconfig.json"
2829
},
2930
"dependencies": {
@@ -45,10 +46,10 @@
4546
"@types/resolve": "^1.20.6",
4647
"@types/semver": "^7.7.0",
4748
"@types/set-cookie-parser": "^2.4.10",
48-
"@typescript/native-preview": "latest",
49+
"@typescript/native-preview": "7.0.0-dev.20250711.1",
4950
"consola": "^3.1.0",
5051
"cookie": "^1.0.2",
51-
"electron": "^37.2.0",
52+
"electron": "~37.1.0",
5253
"electron-builder": "^26.0.12",
5354
"electron-is": "^3.0.0",
5455
"electron-log": "^5.3.3",
@@ -58,17 +59,19 @@
5859
"fix-path": "^4.0.0",
5960
"just-diff": "^6.0.2",
6061
"lodash": "^4.17.21",
61-
"pglite-server": "^0.1.4",
62+
"lodash-es": "^4.17.21",
6263
"resolve": "^1.22.8",
6364
"semver": "^7.5.4",
6465
"set-cookie-parser": "^2.7.1",
6566
"tsx": "^4.19.3",
6667
"typescript": "^5.7.3",
68+
"undici": "^7.9.0",
6769
"vite": "^6.2.5"
6870
},
6971
"pnpm": {
7072
"onlyBuiltDependencies": [
71-
"electron"
73+
"electron",
74+
"electron-builder"
7275
]
7376
}
7477
}

apps/desktop/scripts/pglite-server.ts

Lines changed: 0 additions & 14 deletions
This file was deleted.

apps/desktop/src/main/appBrowsers.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ export const appBrowsers = {
3636
autoHideMenuBar: true,
3737
height: 800,
3838
identifier: 'settings',
39-
keepAlive: true,
4039
minWidth: 600,
4140
parentIdentifier: 'chat',
4241
path: '/settings',

apps/desktop/src/main/const/store.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
/**
22
* 应用设置存储相关常量
33
*/
4+
import { NetworkProxySettings } from '@lobechat/electron-client-ipc';
5+
46
import { appStorageDir } from '@/const/dir';
57
import { DEFAULT_SHORTCUTS_CONFIG } from '@/shortcuts';
68
import { ElectronMainStore } from '@/types/store';
@@ -10,13 +12,23 @@ import { ElectronMainStore } from '@/types/store';
1012
*/
1113
export const STORE_NAME = 'lobehub-settings';
1214

15+
export const defaultProxySettings: NetworkProxySettings = {
16+
enableProxy: false,
17+
proxyBypass: 'localhost, 127.0.0.1, ::1',
18+
proxyPort: '',
19+
proxyRequireAuth: false,
20+
proxyServer: '',
21+
proxyType: 'http',
22+
};
23+
1324
/**
1425
* 存储默认值
1526
*/
1627
export const STORE_DEFAULTS: ElectronMainStore = {
1728
dataSyncConfig: { storageMode: 'local' },
1829
encryptedTokens: {},
1930
locale: 'auto',
31+
networkProxy: defaultProxySettings,
2032
shortcuts: DEFAULT_SHORTCUTS_CONFIG,
2133
storagePath: appStorageDir,
2234
};
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
import { NetworkProxySettings } from '@lobechat/electron-client-ipc';
2+
import { merge } from 'lodash';
3+
import { isEqual } from 'lodash-es';
4+
5+
import { defaultProxySettings } from '@/const/store';
6+
import { createLogger } from '@/utils/logger';
7+
8+
import {
9+
ProxyConfigValidator,
10+
ProxyConnectionTester,
11+
ProxyDispatcherManager,
12+
ProxyTestResult,
13+
} from '../modules/networkProxy';
14+
import { ControllerModule, ipcClientEvent } from './index';
15+
16+
// Create logger
17+
const logger = createLogger('controllers:NetworkProxyCtr');
18+
19+
/**
20+
* 网络代理控制器
21+
* 处理桌面应用的网络代理相关功能
22+
*/
23+
export default class NetworkProxyCtr extends ControllerModule {
24+
/**
25+
* 获取代理设置
26+
*/
27+
@ipcClientEvent('getProxySettings')
28+
async getDesktopSettings(): Promise<NetworkProxySettings> {
29+
try {
30+
const settings = this.app.storeManager.get(
31+
'networkProxy',
32+
defaultProxySettings,
33+
) as NetworkProxySettings;
34+
logger.debug('Retrieved proxy settings:', {
35+
enableProxy: settings.enableProxy,
36+
proxyType: settings.proxyType,
37+
});
38+
return settings;
39+
} catch (error) {
40+
logger.error('Failed to get proxy settings:', error);
41+
return defaultProxySettings;
42+
}
43+
}
44+
45+
/**
46+
* 设置代理配置
47+
*/
48+
@ipcClientEvent('setProxySettings')
49+
async setProxySettings(config: NetworkProxySettings): Promise<void> {
50+
try {
51+
// 验证配置
52+
const validation = ProxyConfigValidator.validate(config);
53+
if (!validation.isValid) {
54+
const errorMessage = `Invalid proxy configuration: ${validation.errors.join(', ')}`;
55+
logger.error(errorMessage);
56+
throw new Error(errorMessage);
57+
}
58+
59+
// 获取当前配置
60+
const currentConfig = this.app.storeManager.get(
61+
'networkProxy',
62+
defaultProxySettings,
63+
) as NetworkProxySettings;
64+
65+
// 检查是否有变化
66+
if (isEqual(currentConfig, config)) {
67+
logger.debug('Proxy settings unchanged, skipping update');
68+
return;
69+
}
70+
71+
// 合并配置
72+
const newConfig = merge({}, currentConfig, config);
73+
74+
// 应用代理设置
75+
await ProxyDispatcherManager.applyProxySettings(newConfig);
76+
77+
// 保存到存储
78+
this.app.storeManager.set('networkProxy', newConfig);
79+
80+
logger.info('Proxy settings updated successfully', {
81+
enableProxy: newConfig.enableProxy,
82+
proxyPort: newConfig.proxyPort,
83+
proxyServer: newConfig.proxyServer,
84+
proxyType: newConfig.proxyType,
85+
});
86+
} catch (error) {
87+
logger.error('Failed to update proxy settings:', error);
88+
throw error;
89+
}
90+
}
91+
92+
/**
93+
* 测试代理连接
94+
*/
95+
@ipcClientEvent('testProxyConnection')
96+
async testProxyConnection(url: string): Promise<{ message?: string; success: boolean }> {
97+
try {
98+
const result = await ProxyConnectionTester.testConnection(url);
99+
100+
if (result.success) {
101+
return { success: true };
102+
} else {
103+
throw new Error(result.message || 'Connection test failed');
104+
}
105+
} catch (error) {
106+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
107+
logger.error('Proxy connection test failed:', errorMessage);
108+
throw new Error(`Connection failed: ${errorMessage}`);
109+
}
110+
}
111+
112+
/**
113+
* 测试指定代理配置
114+
*/
115+
@ipcClientEvent('testProxyConfig')
116+
async testProxyConfig({
117+
config,
118+
testUrl,
119+
}: {
120+
config: NetworkProxySettings;
121+
testUrl?: string;
122+
}): Promise<ProxyTestResult> {
123+
try {
124+
return await ProxyConnectionTester.testProxyConfig(config, testUrl);
125+
} catch (error) {
126+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
127+
logger.error('Proxy config test failed:', errorMessage);
128+
return {
129+
message: `Proxy config test failed: ${errorMessage}`,
130+
success: false,
131+
};
132+
}
133+
}
134+
135+
/**
136+
* 应用初始代理设置
137+
*/
138+
async afterAppReady(): Promise<void> {
139+
try {
140+
// 获取存储的代理设置
141+
const networkProxy = this.app.storeManager.get(
142+
'networkProxy',
143+
defaultProxySettings,
144+
) as NetworkProxySettings;
145+
146+
// 验证配置
147+
const validation = ProxyConfigValidator.validate(networkProxy);
148+
if (!validation.isValid) {
149+
logger.warn('Invalid stored proxy configuration, using defaults:', validation.errors);
150+
await ProxyDispatcherManager.applyProxySettings(defaultProxySettings);
151+
return;
152+
}
153+
154+
// 应用代理设置
155+
await ProxyDispatcherManager.applyProxySettings(networkProxy);
156+
157+
logger.info('Initial proxy settings applied successfully', {
158+
enableProxy: networkProxy.enableProxy,
159+
proxyType: networkProxy.proxyType,
160+
});
161+
} catch (error) {
162+
logger.error('Failed to apply initial proxy settings:', error);
163+
// 出错时使用默认设置
164+
try {
165+
await ProxyDispatcherManager.applyProxySettings(defaultProxySettings);
166+
logger.info('Fallback to default proxy settings');
167+
} catch (fallbackError) {
168+
logger.error('Failed to apply fallback proxy settings:', fallbackError);
169+
}
170+
}
171+
}
172+
}

0 commit comments

Comments
 (0)