From 555130001b1e0af38278676eedfcd786ad7142c9 Mon Sep 17 00:00:00 2001 From: ezequielnick <107352853+ezequielnick@users.noreply.github.com> Date: Sat, 7 Jun 2025 22:45:38 +0800 Subject: [PATCH] feat: integrate with socket reconstruction mechanism --- src/main/sys/sysproxy.ts | 108 +++++++++++++++++++++++++++++++++------ 1 file changed, 93 insertions(+), 15 deletions(-) diff --git a/src/main/sys/sysproxy.ts b/src/main/sys/sysproxy.ts index e8ac1dc..ea098cd 100644 --- a/src/main/sys/sysproxy.ts +++ b/src/main/sys/sysproxy.ts @@ -7,6 +7,7 @@ import path from 'path' import { resourcesFilesDir } from '../utils/dirs' import { net } from 'electron' import axios from 'axios' +import fs from 'fs' let defaultBypass: string[] let triggerSysProxyTimer: NodeJS.Timeout | null = null @@ -82,12 +83,14 @@ async function enableSysProxy(): Promise { triggerAutoProxy(true, `http://${host || '127.0.0.1'}:${pacPort}/pac`) } } else if (process.platform === 'darwin') { - await axios.post( - 'http://localhost/pac', - { url: `http://${host || '127.0.0.1'}:${pacPort}/pac` }, - { - socketPath: helperSocketPath - } + await helperRequest(() => + axios.post( + 'http://localhost/pac', + { url: `http://${host || '127.0.0.1'}:${pacPort}/pac` }, + { + socketPath: helperSocketPath + } + ) ) } else { triggerAutoProxy(true, `http://${host || '127.0.0.1'}:${pacPort}/pac`) @@ -108,12 +111,14 @@ async function enableSysProxy(): Promise { triggerManualProxy(true, host || '127.0.0.1', port, bypass.join(',')) } } else if (process.platform === 'darwin') { - await axios.post( - 'http://localhost/global', - { host: host || '127.0.0.1', port: port.toString(), bypass: bypass.join(',') }, - { - socketPath: helperSocketPath - } + await helperRequest(() => + axios.post( + 'http://localhost/global', + { host: host || '127.0.0.1', port: port.toString(), bypass: bypass.join(',') }, + { + socketPath: helperSocketPath + } + ) ) } else { triggerManualProxy(true, host || '127.0.0.1', port, bypass.join(',')) @@ -134,11 +139,84 @@ async function disableSysProxy(): Promise { triggerManualProxy(false, '', 0, '') } } else if (process.platform === 'darwin') { - await axios.get('http://localhost/off', { - socketPath: helperSocketPath - }) + await helperRequest(() => + axios.get('http://localhost/off', { + socketPath: helperSocketPath + }) + ) } else { triggerAutoProxy(false, '') triggerManualProxy(false, '', 0, '') } } + +// Helper function to check if socket file exists +function isSocketFileExists(): boolean { + try { + return fs.existsSync(helperSocketPath) + } catch { + return false + } +} + +// Helper function to send signal to recreate socket +async function requestSocketRecreation(): Promise { + try { + // Send SIGUSR1 signal to helper process to recreate socket + const { exec } = require('child_process') + const { promisify } = require('util') + const execPromise = promisify(exec) + + // Use osascript with administrator privileges (same pattern as manualGrantCorePermition) + const shell = `pkill -USR1 -f party.mihomo.helper` + const command = `do shell script "${shell}" with administrator privileges` + await execPromise(`osascript -e '${command}'`) + + // Wait a bit for socket recreation + await new Promise(resolve => setTimeout(resolve, 1000)) + } catch (error) { + console.log('Failed to send signal to helper:', error) + throw error + } +} + +// Wrapper function for helper requests with auto-retry on socket issues +async function helperRequest(requestFn: () => Promise, maxRetries = 1): Promise { + let lastError: Error | null = null + + for (let attempt = 0; attempt <= maxRetries; attempt++) { + try { + return await requestFn() + } catch (error) { + lastError = error as Error + + // Check if it's a connection error and socket file doesn't exist + if (attempt < maxRetries && + ((error as NodeJS.ErrnoException).code === 'ECONNREFUSED' || + (error as NodeJS.ErrnoException).code === 'ENOENT' || + (error as Error).message?.includes('connect ECONNREFUSED') || + (error as Error).message?.includes('ENOENT'))) { + + console.log(`Helper request failed (attempt ${attempt + 1}), checking socket file...`) + + if (!isSocketFileExists()) { + console.log('Socket file missing, requesting recreation...') + try { + await requestSocketRecreation() + console.log('Socket recreation requested, retrying...') + continue + } catch (signalError) { + console.log('Failed to request socket recreation:', signalError) + } + } + } + + // If not a connection error or we've exhausted retries, throw the error + if (attempt === maxRetries) { + throw lastError + } + } + } + + throw lastError +}