UNPKG

@lobehub/chat

Version:

Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.

139 lines (121 loc) 3.95 kB
import { NetworkProxySettings } from '@lobechat/electron-client-ipc'; import { SocksProxies, socksDispatcher } from 'fetch-socks'; import { Agent, ProxyAgent, getGlobalDispatcher, setGlobalDispatcher } from 'undici'; import { createLogger } from '@/utils/logger'; import { ProxyUrlBuilder } from './urlBuilder'; // Create logger const logger = createLogger('modules:networkProxy:dispatcher'); /** * 代理管理器 */ export class ProxyDispatcherManager { private static isChanging = false; private static changeQueue: Array<() => Promise<void>> = []; /** * 应用代理设置(带并发控制) */ static async applyProxySettings(config: NetworkProxySettings): Promise<void> { return new Promise((resolve, reject) => { const operation = async () => { try { await this.doApplyProxySettings(config); resolve(); } catch (error) { reject(error); } }; if (this.isChanging) { // 如果正在切换,加入队列 this.changeQueue.push(operation); } else { // 立即执行 operation(); } }); } /** * 执行代理设置应用 */ private static async doApplyProxySettings(config: NetworkProxySettings): Promise<void> { this.isChanging = true; try { const currentDispatcher = getGlobalDispatcher(); // 禁用代理,恢复默认连接 if (!config.enableProxy) { await this.safeDestroyDispatcher(currentDispatcher); // 创建一个新的默认 Agent 来替代代理 setGlobalDispatcher(new Agent()); logger.debug('Proxy disabled, reset to direct connection mode'); return; } // 构建代理 URL const proxyUrl = ProxyUrlBuilder.build(config); // 创建代理 agent const agent = this.createProxyAgent(config.proxyType, proxyUrl); // 切换代理前销毁旧 dispatcher await this.safeDestroyDispatcher(currentDispatcher); setGlobalDispatcher(agent); logger.info( `Proxy settings applied: ${config.proxyType}://${config.proxyServer}:${config.proxyPort}`, ); logger.debug( 'Global request proxy set, all Node.js network requests will go through this proxy', ); } finally { this.isChanging = false; // 处理队列中的下一个操作 if (this.changeQueue.length > 0) { const nextOperation = this.changeQueue.shift(); if (nextOperation) { setTimeout(() => nextOperation(), 0); } } } } /** * 创建代理 agent */ static createProxyAgent(proxyType: string, proxyUrl: string) { try { if (proxyType === 'socks5') { // 解析 SOCKS5 代理 URL const url = new URL(proxyUrl); const socksProxies: SocksProxies = [ { host: url.hostname, port: parseInt(url.port, 10), type: 5, ...(url.username && url.password ? { password: url.password, userId: url.username, } : {}), }, ]; // 使用 fetch-socks 处理 SOCKS5 代理 return socksDispatcher(socksProxies); } else { // undici 的 ProxyAgent 支持 http, https return new ProxyAgent({ uri: proxyUrl }); } } catch (error) { logger.error(`Failed to create proxy agent for ${proxyType}:`, error); throw new Error( `Failed to create proxy agent: ${error instanceof Error ? error.message : 'Unknown error'}`, ); } } /** * 安全销毁 dispatcher */ private static async safeDestroyDispatcher(dispatcher: any): Promise<void> { try { if (dispatcher && typeof dispatcher.destroy === 'function') { await dispatcher.destroy(); } } catch (error) { logger.warn('Failed to destroy dispatcher:', error); } } }