feat: integrate with socket reconstruction mechanism

This commit is contained in:
ezequielnick 2025-06-07 22:45:38 +08:00
parent 78f9211ebe
commit 555130001b

View File

@ -7,6 +7,7 @@ import path from 'path'
import { resourcesFilesDir } from '../utils/dirs' import { resourcesFilesDir } from '../utils/dirs'
import { net } from 'electron' import { net } from 'electron'
import axios from 'axios' import axios from 'axios'
import fs from 'fs'
let defaultBypass: string[] let defaultBypass: string[]
let triggerSysProxyTimer: NodeJS.Timeout | null = null let triggerSysProxyTimer: NodeJS.Timeout | null = null
@ -82,13 +83,15 @@ async function enableSysProxy(): Promise<void> {
triggerAutoProxy(true, `http://${host || '127.0.0.1'}:${pacPort}/pac`) triggerAutoProxy(true, `http://${host || '127.0.0.1'}:${pacPort}/pac`)
} }
} else if (process.platform === 'darwin') { } else if (process.platform === 'darwin') {
await axios.post( await helperRequest(() =>
axios.post(
'http://localhost/pac', 'http://localhost/pac',
{ url: `http://${host || '127.0.0.1'}:${pacPort}/pac` }, { url: `http://${host || '127.0.0.1'}:${pacPort}/pac` },
{ {
socketPath: helperSocketPath socketPath: helperSocketPath
} }
) )
)
} else { } else {
triggerAutoProxy(true, `http://${host || '127.0.0.1'}:${pacPort}/pac`) triggerAutoProxy(true, `http://${host || '127.0.0.1'}:${pacPort}/pac`)
} }
@ -108,13 +111,15 @@ async function enableSysProxy(): Promise<void> {
triggerManualProxy(true, host || '127.0.0.1', port, bypass.join(',')) triggerManualProxy(true, host || '127.0.0.1', port, bypass.join(','))
} }
} else if (process.platform === 'darwin') { } else if (process.platform === 'darwin') {
await axios.post( await helperRequest(() =>
axios.post(
'http://localhost/global', 'http://localhost/global',
{ host: host || '127.0.0.1', port: port.toString(), bypass: bypass.join(',') }, { host: host || '127.0.0.1', port: port.toString(), bypass: bypass.join(',') },
{ {
socketPath: helperSocketPath socketPath: helperSocketPath
} }
) )
)
} else { } else {
triggerManualProxy(true, host || '127.0.0.1', port, bypass.join(',')) triggerManualProxy(true, host || '127.0.0.1', port, bypass.join(','))
} }
@ -134,11 +139,84 @@ async function disableSysProxy(): Promise<void> {
triggerManualProxy(false, '', 0, '') triggerManualProxy(false, '', 0, '')
} }
} else if (process.platform === 'darwin') { } else if (process.platform === 'darwin') {
await axios.get('http://localhost/off', { await helperRequest(() =>
axios.get('http://localhost/off', {
socketPath: helperSocketPath socketPath: helperSocketPath
}) })
)
} else { } else {
triggerAutoProxy(false, '') triggerAutoProxy(false, '')
triggerManualProxy(false, '', 0, '') 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<void> {
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<unknown>, maxRetries = 1): Promise<unknown> {
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
}