Compare commits
No commits in common. "305210cb96ed846297e11f267bd978570856b244" and "c8d83f45ac49d352b4b1ef14a2181a450b9da079" have entirely different histories.
305210cb96
...
c8d83f45ac
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 32 KiB |
BIN
build/icon.icns
BIN
build/icon.ico
|
Before Width: | Height: | Size: 211 KiB After Width: | Height: | Size: 25 KiB |
BIN
build/icon.png
|
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 229 KiB After Width: | Height: | Size: 81 KiB |
@ -3,9 +3,7 @@
|
|||||||
### 新功能 (Feat)
|
### 新功能 (Feat)
|
||||||
- 增加关闭动画开关
|
- 增加关闭动画开关
|
||||||
- 增加订阅超时时间设置
|
- 增加订阅超时时间设置
|
||||||
|
-
|
||||||
### 优化 (Optimize)
|
|
||||||
- socket 管理防止内核通信失败
|
|
||||||
|
|
||||||
### 样式调整 (Sytle)
|
### 样式调整 (Sytle)
|
||||||
- 改进 logo 设计
|
- 改进 logo 设计
|
||||||
|
|||||||
@ -28,8 +28,7 @@ import {
|
|||||||
stopMihomoTraffic,
|
stopMihomoTraffic,
|
||||||
stopMihomoLogs,
|
stopMihomoLogs,
|
||||||
stopMihomoMemory,
|
stopMihomoMemory,
|
||||||
patchMihomoConfig,
|
patchMihomoConfig
|
||||||
getAxios
|
|
||||||
} from './mihomoApi'
|
} from './mihomoApi'
|
||||||
import chokidar from 'chokidar'
|
import chokidar from 'chokidar'
|
||||||
import { readFile, rm, writeFile } from 'fs/promises'
|
import { readFile, rm, writeFile } from 'fs/promises'
|
||||||
@ -53,26 +52,8 @@ chokidar.watch(path.join(mihomoCoreDir(), 'meta-update'), {}).on('unlinkDir', as
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 动态生成 IPC 路径
|
export const mihomoIpcPath =
|
||||||
export const getMihomoIpcPath = (): string => {
|
process.platform === 'win32' ? '\\\\.\\pipe\\MihomoParty\\mihomo' : '/tmp/mihomo-party.sock'
|
||||||
if (process.platform === 'win32') {
|
|
||||||
const isAdmin = getSessionAdminStatus()
|
|
||||||
const sessionId = process.env.SESSIONNAME || process.env.USERNAME || 'default'
|
|
||||||
const processId = process.pid
|
|
||||||
|
|
||||||
if (isAdmin) {
|
|
||||||
return `\\\\.\\pipe\\MihomoParty\\mihomo-admin-${sessionId}-${processId}`
|
|
||||||
} else {
|
|
||||||
return `\\\\.\\pipe\\MihomoParty\\mihomo-user-${sessionId}-${processId}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const uid = process.getuid?.() || 'unknown'
|
|
||||||
const processId = process.pid
|
|
||||||
|
|
||||||
return `/tmp/mihomo-party-${uid}-${processId}.sock`
|
|
||||||
}
|
|
||||||
|
|
||||||
const ctlParam = process.platform === 'win32' ? '-ext-ctl-pipe' : '-ext-ctl-unix'
|
const ctlParam = process.platform === 'win32' ? '-ext-ctl-pipe' : '-ext-ctl-unix'
|
||||||
|
|
||||||
let setPublicDNSTimer: NodeJS.Timeout | null = null
|
let setPublicDNSTimer: NodeJS.Timeout | null = null
|
||||||
@ -112,9 +93,6 @@ export async function startCore(detached = false): Promise<Promise<void>[]> {
|
|||||||
await generateProfile()
|
await generateProfile()
|
||||||
await checkProfile()
|
await checkProfile()
|
||||||
await stopCore()
|
await stopCore()
|
||||||
|
|
||||||
await cleanupSocketFile()
|
|
||||||
|
|
||||||
if (tun?.enable && autoSetDNS) {
|
if (tun?.enable && autoSetDNS) {
|
||||||
try {
|
try {
|
||||||
await setPublicDNS()
|
await setPublicDNS()
|
||||||
@ -122,15 +100,6 @@ export async function startCore(detached = false): Promise<Promise<void>[]> {
|
|||||||
await managerLogger.error('set dns failed', error)
|
await managerLogger.error('set dns failed', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取动态 IPC 路径
|
|
||||||
const dynamicIpcPath = getMihomoIpcPath()
|
|
||||||
await managerLogger.info(`Using IPC path: ${dynamicIpcPath}`)
|
|
||||||
|
|
||||||
if (process.platform === 'win32') {
|
|
||||||
await validateWindowsPipeAccess(dynamicIpcPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 内核日志输出到独立的 core-日期.log 文件
|
// 内核日志输出到独立的 core-日期.log 文件
|
||||||
const stdout = createWriteStream(coreLogPath(), { flags: 'a' })
|
const stdout = createWriteStream(coreLogPath(), { flags: 'a' })
|
||||||
const stderr = createWriteStream(coreLogPath(), { flags: 'a' })
|
const stderr = createWriteStream(coreLogPath(), { flags: 'a' })
|
||||||
@ -142,9 +111,9 @@ export async function startCore(detached = false): Promise<Promise<void>[]> {
|
|||||||
}
|
}
|
||||||
child = spawn(
|
child = spawn(
|
||||||
corePath,
|
corePath,
|
||||||
['-d', diffWorkDir ? mihomoProfileWorkDir(current) : mihomoWorkDir(), ctlParam, dynamicIpcPath],
|
['-d', diffWorkDir ? mihomoProfileWorkDir(current) : mihomoWorkDir(), ctlParam, mihomoIpcPath],
|
||||||
{
|
{
|
||||||
detached: detached,
|
detached: true,
|
||||||
stdio: detached ? 'ignore' : undefined,
|
stdio: detached ? 'ignore' : undefined,
|
||||||
env: env
|
env: env
|
||||||
}
|
}
|
||||||
@ -153,7 +122,6 @@ export async function startCore(detached = false): Promise<Promise<void>[]> {
|
|||||||
os.setPriority(child.pid, os.constants.priority[mihomoCpuPriority])
|
os.setPriority(child.pid, os.constants.priority[mihomoCpuPriority])
|
||||||
}
|
}
|
||||||
if (detached) {
|
if (detached) {
|
||||||
await managerLogger.info(`Core process detached successfully on ${process.platform}, PID: ${child.pid}`)
|
|
||||||
child.unref()
|
child.unref()
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
resolve([new Promise(() => {})])
|
resolve([new Promise(() => {})])
|
||||||
@ -184,18 +152,6 @@ export async function startCore(detached = false): Promise<Promise<void>[]> {
|
|||||||
if ((process.platform !== 'win32' && str.includes('External controller unix listen error')) ||
|
if ((process.platform !== 'win32' && str.includes('External controller unix listen error')) ||
|
||||||
(process.platform === 'win32' && str.includes('External controller pipe listen error'))
|
(process.platform === 'win32' && str.includes('External controller pipe listen error'))
|
||||||
) {
|
) {
|
||||||
await managerLogger.error('External controller listen error detected:', str)
|
|
||||||
|
|
||||||
if (process.platform === 'win32') {
|
|
||||||
await managerLogger.info('Attempting Windows pipe cleanup and retry...')
|
|
||||||
try {
|
|
||||||
await cleanupWindowsNamedPipes()
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 2000))
|
|
||||||
} catch (cleanupError) {
|
|
||||||
await managerLogger.error('Pipe cleanup failed:', cleanupError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
reject(i18next.t('mihomo.error.externalControllerListenError'))
|
reject(i18next.t('mihomo.error.externalControllerListenError'))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,11 +176,6 @@ export async function startCore(detached = false): Promise<Promise<void>[]> {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
])
|
])
|
||||||
|
|
||||||
await waitForCoreReady()
|
|
||||||
|
|
||||||
// 强制刷新 axios 实例以使用新的管道路径
|
|
||||||
await getAxios(true)
|
|
||||||
await startMihomoTraffic()
|
await startMihomoTraffic()
|
||||||
await startMihomoConnections()
|
await startMihomoConnections()
|
||||||
await startMihomoLogs()
|
await startMihomoLogs()
|
||||||
@ -252,118 +203,6 @@ export async function stopCore(force = false): Promise<void> {
|
|||||||
stopMihomoConnections()
|
stopMihomoConnections()
|
||||||
stopMihomoLogs()
|
stopMihomoLogs()
|
||||||
stopMihomoMemory()
|
stopMihomoMemory()
|
||||||
|
|
||||||
// 强制刷新 axios
|
|
||||||
try {
|
|
||||||
await getAxios(true)
|
|
||||||
} catch (error) {
|
|
||||||
await managerLogger.warn('Failed to refresh axios instance:', error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 清理 Socket 文件
|
|
||||||
await cleanupSocketFile()
|
|
||||||
}
|
|
||||||
async function cleanupSocketFile(): Promise<void> {
|
|
||||||
if (process.platform === 'win32') {
|
|
||||||
await cleanupWindowsNamedPipes()
|
|
||||||
} else {
|
|
||||||
await cleanupUnixSockets()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Windows 命名管道清理
|
|
||||||
async function cleanupWindowsNamedPipes(): Promise<void> {
|
|
||||||
try {
|
|
||||||
const execPromise = promisify(exec)
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { stdout } = await execPromise(
|
|
||||||
`powershell -Command "[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; Get-Process | Where-Object {$_.ProcessName -like '*mihomo*'} | Select-Object Id,ProcessName | ConvertTo-Json"`,
|
|
||||||
{ encoding: 'utf8' }
|
|
||||||
)
|
|
||||||
|
|
||||||
if (stdout.trim()) {
|
|
||||||
await managerLogger.info(`Found potential pipe-blocking processes: ${stdout}`)
|
|
||||||
|
|
||||||
try {
|
|
||||||
const processes = JSON.parse(stdout)
|
|
||||||
const processArray = Array.isArray(processes) ? processes : [processes]
|
|
||||||
|
|
||||||
for (const proc of processArray) {
|
|
||||||
const pid = proc.Id
|
|
||||||
if (pid && pid !== process.pid) {
|
|
||||||
try {
|
|
||||||
process.kill(pid, 'SIGTERM')
|
|
||||||
await managerLogger.info(`Terminated process ${pid} to free pipe`)
|
|
||||||
} catch (error) {
|
|
||||||
await managerLogger.warn(`Failed to terminate process ${pid}:`, error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (parseError) {
|
|
||||||
await managerLogger.warn('Failed to parse process list JSON:', parseError)
|
|
||||||
|
|
||||||
// 回退到文本解析
|
|
||||||
const lines = stdout.split('\n').filter(line => line.includes('mihomo'))
|
|
||||||
for (const line of lines) {
|
|
||||||
const match = line.match(/(\d+)/)
|
|
||||||
if (match) {
|
|
||||||
const pid = parseInt(match[1])
|
|
||||||
if (pid !== process.pid) {
|
|
||||||
try {
|
|
||||||
process.kill(pid, 'SIGTERM')
|
|
||||||
await managerLogger.info(`Terminated process ${pid} to free pipe`)
|
|
||||||
} catch (error) {
|
|
||||||
await managerLogger.warn(`Failed to terminate process ${pid}:`, error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
await managerLogger.warn('Failed to check mihomo processes:', error)
|
|
||||||
}
|
|
||||||
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
await managerLogger.error('Windows named pipe cleanup failed:', error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unix Socket 清理
|
|
||||||
async function cleanupUnixSockets(): Promise<void> {
|
|
||||||
try {
|
|
||||||
const socketPaths = [
|
|
||||||
'/tmp/mihomo-party.sock',
|
|
||||||
'/tmp/mihomo-party-admin.sock',
|
|
||||||
`/tmp/mihomo-party-${process.getuid?.() || 'user'}.sock`
|
|
||||||
]
|
|
||||||
|
|
||||||
for (const socketPath of socketPaths) {
|
|
||||||
try {
|
|
||||||
if (existsSync(socketPath)) {
|
|
||||||
await rm(socketPath)
|
|
||||||
await managerLogger.info(`Cleaned up socket file: ${socketPath}`)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
await managerLogger.warn(`Failed to cleanup socket file ${socketPath}:`, error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
await managerLogger.error('Unix socket cleanup failed:', error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Windows 命名管道访问验证
|
|
||||||
async function validateWindowsPipeAccess(pipePath: string): Promise<void> {
|
|
||||||
try {
|
|
||||||
await managerLogger.info(`Validating pipe access for: ${pipePath}`)
|
|
||||||
await managerLogger.info(`Pipe validation completed for: ${pipePath}`)
|
|
||||||
} catch (error) {
|
|
||||||
await managerLogger.error('Windows pipe validation failed:', error)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function restartCore(): Promise<void> {
|
export async function restartCore(): Promise<void> {
|
||||||
@ -379,7 +218,7 @@ export async function restartCore(): Promise<void> {
|
|||||||
|
|
||||||
export async function keepCoreAlive(): Promise<void> {
|
export async function keepCoreAlive(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
await startCore(true)
|
if (!child) await startCore(true)
|
||||||
if (child && child.pid) {
|
if (child && child.pid) {
|
||||||
await writeFile(path.join(dataDir(), 'core.pid'), child.pid.toString())
|
await writeFile(path.join(dataDir(), 'core.pid'), child.pid.toString())
|
||||||
}
|
}
|
||||||
@ -389,21 +228,8 @@ export async function keepCoreAlive(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function quitWithoutCore(): Promise<void> {
|
export async function quitWithoutCore(): Promise<void> {
|
||||||
await managerLogger.info(`Starting lightweight mode on platform: ${process.platform}`)
|
await keepCoreAlive()
|
||||||
|
|
||||||
try {
|
|
||||||
await startCore(true)
|
|
||||||
if (child && child.pid) {
|
|
||||||
await writeFile(path.join(dataDir(), 'core.pid'), child.pid.toString())
|
|
||||||
await managerLogger.info(`Core started in lightweight mode with PID: ${child.pid}`)
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
await managerLogger.error('Failed to start core in lightweight mode:', e)
|
|
||||||
safeShowErrorBox('mihomo.error.coreStartFailed', `${e}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
await startMonitor(true)
|
await startMonitor(true)
|
||||||
await managerLogger.info('Exiting main process, core will continue running in background')
|
|
||||||
app.exit()
|
app.exit()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -504,48 +330,6 @@ export async function grantTunPermissions(): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 在应用启动时检测一次权限
|
|
||||||
let sessionAdminStatus: boolean | null = null
|
|
||||||
|
|
||||||
export async function initAdminStatus(): Promise<void> {
|
|
||||||
if (process.platform === 'win32' && sessionAdminStatus === null) {
|
|
||||||
sessionAdminStatus = await checkAdminPrivileges().catch(() => false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getSessionAdminStatus(): boolean {
|
|
||||||
if (process.platform !== 'win32') {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return sessionAdminStatus ?? false
|
|
||||||
}
|
|
||||||
|
|
||||||
// 等待内核完全启动并创建管道
|
|
||||||
async function waitForCoreReady(): Promise<void> {
|
|
||||||
const maxRetries = 30
|
|
||||||
const retryInterval = 500
|
|
||||||
|
|
||||||
for (let i = 0; i < maxRetries; i++) {
|
|
||||||
try {
|
|
||||||
const axios = await getAxios(true)
|
|
||||||
await axios.get('/')
|
|
||||||
await managerLogger.info(`Core ready after ${i + 1} attempts (${(i + 1) * retryInterval}ms)`)
|
|
||||||
return
|
|
||||||
} catch (error) {
|
|
||||||
if (i === 0) {
|
|
||||||
await managerLogger.info('Waiting for core to be ready...')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i === maxRetries - 1) {
|
|
||||||
await managerLogger.warn(`Core not ready after ${maxRetries} attempts, proceeding anyway`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
await new Promise(resolve => setTimeout(resolve, retryInterval))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function checkAdminPrivileges(): Promise<boolean> {
|
export async function checkAdminPrivileges(): Promise<boolean> {
|
||||||
if (process.platform !== 'win32') {
|
if (process.platform !== 'win32') {
|
||||||
return true
|
return true
|
||||||
@ -554,22 +338,20 @@ export async function checkAdminPrivileges(): Promise<boolean> {
|
|||||||
const execPromise = promisify(exec)
|
const execPromise = promisify(exec)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// fltmc 检测管理员权限
|
// 首先尝试 fltmc 命令检测管理员权限
|
||||||
await execPromise('chcp 65001 >nul 2>&1 && fltmc', { encoding: 'utf8' })
|
await execPromise('fltmc')
|
||||||
await managerLogger.info('Admin privileges confirmed via fltmc')
|
await managerLogger.info('Admin privileges confirmed via fltmc')
|
||||||
return true
|
return true
|
||||||
} catch (fltmcError: any) {
|
} catch (fltmcError) {
|
||||||
const errorCode = fltmcError?.code || 0
|
await managerLogger.info('fltmc failed, trying net session as fallback', fltmcError)
|
||||||
await managerLogger.debug(`fltmc failed with code ${errorCode}, trying net session as fallback`)
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// net session 备用
|
// 如果 fltmc 失败,尝试 net session 命令作为备用检测方法
|
||||||
await execPromise('chcp 65001 >nul 2>&1 && net session', { encoding: 'utf8' })
|
await execPromise('net session')
|
||||||
await managerLogger.info('Admin privileges confirmed via net session')
|
await managerLogger.info('Admin privileges confirmed via net session')
|
||||||
return true
|
return true
|
||||||
} catch (netSessionError: any) {
|
} catch (netSessionError) {
|
||||||
const netErrorCode = netSessionError?.code || 0
|
await managerLogger.info('Both fltmc and net session failed, no admin privileges', netSessionError)
|
||||||
await managerLogger.debug(`Both fltmc and net session failed, no admin privileges. Error codes: fltmc=${errorCode}, net=${netErrorCode}`)
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -732,7 +514,7 @@ async function checkHighPrivilegeMihomoProcess(): Promise<boolean> {
|
|||||||
|
|
||||||
for (const executable of mihomoExecutables) {
|
for (const executable of mihomoExecutables) {
|
||||||
try {
|
try {
|
||||||
const { stdout } = await execPromise(`chcp 65001 >nul 2>&1 && tasklist /FI "IMAGENAME eq ${executable}" /FO CSV`, { encoding: 'utf8' })
|
const { stdout } = await execPromise(`tasklist /FI "IMAGENAME eq ${executable}" /FO CSV`)
|
||||||
const lines = stdout.split('\n').filter(line => line.includes(executable))
|
const lines = stdout.split('\n').filter(line => line.includes(executable))
|
||||||
|
|
||||||
if (lines.length > 0) {
|
if (lines.length > 0) {
|
||||||
@ -743,10 +525,7 @@ async function checkHighPrivilegeMihomoProcess(): Promise<boolean> {
|
|||||||
if (parts.length >= 2) {
|
if (parts.length >= 2) {
|
||||||
const pid = parts[1].replace(/"/g, '').trim()
|
const pid = parts[1].replace(/"/g, '').trim()
|
||||||
try {
|
try {
|
||||||
const { stdout: processInfo } = await execPromise(
|
const { stdout: processInfo } = await execPromise(`powershell -Command "Get-Process -Id ${pid} | Select-Object Name,Id,Path,CommandLine | ConvertTo-Json"`)
|
||||||
`powershell -Command "[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; Get-Process -Id ${pid} | Select-Object Name,Id,Path,CommandLine | ConvertTo-Json"`,
|
|
||||||
{ encoding: 'utf8' }
|
|
||||||
)
|
|
||||||
const processJson = JSON.parse(processInfo)
|
const processJson = JSON.parse(processInfo)
|
||||||
await managerLogger.info(`Process ${pid} info: ${processInfo.substring(0, 200)}`)
|
await managerLogger.info(`Process ${pid} info: ${processInfo.substring(0, 200)}`)
|
||||||
|
|
||||||
|
|||||||
@ -6,10 +6,9 @@ import { tray } from '../resolve/tray'
|
|||||||
import { calcTraffic } from '../utils/calc'
|
import { calcTraffic } from '../utils/calc'
|
||||||
import { getRuntimeConfig } from './factory'
|
import { getRuntimeConfig } from './factory'
|
||||||
import { floatingWindow } from '../resolve/floatingWindow'
|
import { floatingWindow } from '../resolve/floatingWindow'
|
||||||
import { getMihomoIpcPath } from './manager'
|
import { mihomoIpcPath } from './manager'
|
||||||
|
|
||||||
let axiosIns: AxiosInstance = null!
|
let axiosIns: AxiosInstance = null!
|
||||||
let currentIpcPath: string = ''
|
|
||||||
let mihomoTrafficWs: WebSocket | null = null
|
let mihomoTrafficWs: WebSocket | null = null
|
||||||
let trafficRetry = 10
|
let trafficRetry = 10
|
||||||
let mihomoMemoryWs: WebSocket | null = null
|
let mihomoMemoryWs: WebSocket | null = null
|
||||||
@ -20,19 +19,11 @@ let mihomoConnectionsWs: WebSocket | null = null
|
|||||||
let connectionsRetry = 10
|
let connectionsRetry = 10
|
||||||
|
|
||||||
export const getAxios = async (force: boolean = false): Promise<AxiosInstance> => {
|
export const getAxios = async (force: boolean = false): Promise<AxiosInstance> => {
|
||||||
const dynamicIpcPath = getMihomoIpcPath()
|
if (axiosIns && !force) return axiosIns
|
||||||
|
|
||||||
// 如路径改变 强制重新创建实例
|
|
||||||
if (axiosIns && !force && currentIpcPath === dynamicIpcPath) {
|
|
||||||
return axiosIns
|
|
||||||
}
|
|
||||||
|
|
||||||
currentIpcPath = dynamicIpcPath
|
|
||||||
console.log(`[mihomoApi] Creating axios instance with path: ${dynamicIpcPath}`)
|
|
||||||
|
|
||||||
axiosIns = axios.create({
|
axiosIns = axios.create({
|
||||||
baseURL: `http://localhost`,
|
baseURL: `http://localhost`,
|
||||||
socketPath: dynamicIpcPath,
|
socketPath: mihomoIpcPath,
|
||||||
timeout: 15000
|
timeout: 15000
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -41,12 +32,6 @@ export const getAxios = async (force: boolean = false): Promise<AxiosInstance> =
|
|||||||
return response.data
|
return response.data
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
if (error.code === 'ENOENT') {
|
|
||||||
console.debug(`[mihomoApi] Pipe not ready: ${error.config?.socketPath}`)
|
|
||||||
} else {
|
|
||||||
console.error(`[mihomoApi] Axios error with path ${dynamicIpcPath}:`, error.message)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error.response && error.response.data) {
|
if (error.response && error.response.data) {
|
||||||
return Promise.reject(error.response.data)
|
return Promise.reject(error.response.data)
|
||||||
}
|
}
|
||||||
@ -215,11 +200,7 @@ export const stopMihomoTraffic = (): void => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const mihomoTraffic = async (): Promise<void> => {
|
const mihomoTraffic = async (): Promise<void> => {
|
||||||
const dynamicIpcPath = getMihomoIpcPath()
|
mihomoTrafficWs = new WebSocket(`ws+unix:${mihomoIpcPath}:/traffic`)
|
||||||
const wsUrl = `ws+unix:${dynamicIpcPath}:/traffic`
|
|
||||||
|
|
||||||
console.log(`[mihomoApi] Creating traffic WebSocket with URL: ${wsUrl}`)
|
|
||||||
mihomoTrafficWs = new WebSocket(wsUrl)
|
|
||||||
|
|
||||||
mihomoTrafficWs.onmessage = async (e): Promise<void> => {
|
mihomoTrafficWs.onmessage = async (e): Promise<void> => {
|
||||||
const data = e.data as string
|
const data = e.data as string
|
||||||
@ -248,8 +229,7 @@ const mihomoTraffic = async (): Promise<void> => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mihomoTrafficWs.onerror = (error): void => {
|
mihomoTrafficWs.onerror = (): void => {
|
||||||
console.error(`[mihomoApi] Traffic WebSocket error:`, error)
|
|
||||||
if (mihomoTrafficWs) {
|
if (mihomoTrafficWs) {
|
||||||
mihomoTrafficWs.close()
|
mihomoTrafficWs.close()
|
||||||
mihomoTrafficWs = null
|
mihomoTrafficWs = null
|
||||||
@ -272,9 +252,7 @@ export const stopMihomoMemory = (): void => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const mihomoMemory = async (): Promise<void> => {
|
const mihomoMemory = async (): Promise<void> => {
|
||||||
const dynamicIpcPath = getMihomoIpcPath()
|
mihomoMemoryWs = new WebSocket(`ws+unix:${mihomoIpcPath}:/memory`)
|
||||||
const wsUrl = `ws+unix:${dynamicIpcPath}:/memory`
|
|
||||||
mihomoMemoryWs = new WebSocket(wsUrl)
|
|
||||||
|
|
||||||
mihomoMemoryWs.onmessage = (e): void => {
|
mihomoMemoryWs.onmessage = (e): void => {
|
||||||
const data = e.data as string
|
const data = e.data as string
|
||||||
@ -317,10 +295,8 @@ export const stopMihomoLogs = (): void => {
|
|||||||
|
|
||||||
const mihomoLogs = async (): Promise<void> => {
|
const mihomoLogs = async (): Promise<void> => {
|
||||||
const { 'log-level': logLevel = 'info' } = await getControledMihomoConfig()
|
const { 'log-level': logLevel = 'info' } = await getControledMihomoConfig()
|
||||||
const dynamicIpcPath = getMihomoIpcPath()
|
|
||||||
const wsUrl = `ws+unix:${dynamicIpcPath}:/logs?level=${logLevel}`
|
|
||||||
|
|
||||||
mihomoLogsWs = new WebSocket(wsUrl)
|
mihomoLogsWs = new WebSocket(`ws+unix:${mihomoIpcPath}:/logs?level=${logLevel}`)
|
||||||
|
|
||||||
mihomoLogsWs.onmessage = (e): void => {
|
mihomoLogsWs.onmessage = (e): void => {
|
||||||
const data = e.data as string
|
const data = e.data as string
|
||||||
@ -362,9 +338,7 @@ export const stopMihomoConnections = (): void => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const mihomoConnections = async (): Promise<void> => {
|
const mihomoConnections = async (): Promise<void> => {
|
||||||
const dynamicIpcPath = getMihomoIpcPath()
|
mihomoConnectionsWs = new WebSocket(`ws+unix:${mihomoIpcPath}:/connections`)
|
||||||
const wsUrl = `ws+unix:${dynamicIpcPath}:/connections`
|
|
||||||
mihomoConnectionsWs = new WebSocket(wsUrl)
|
|
||||||
|
|
||||||
mihomoConnectionsWs.onmessage = (e): void => {
|
mihomoConnectionsWs.onmessage = (e): void => {
|
||||||
const data = e.data as string
|
const data = e.data as string
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { registerIpcMainHandlers } from './utils/ipc'
|
|||||||
import windowStateKeeper from 'electron-window-state'
|
import windowStateKeeper from 'electron-window-state'
|
||||||
import { app, shell, BrowserWindow, Menu, dialog, Notification, powerMonitor } from 'electron'
|
import { app, shell, BrowserWindow, Menu, dialog, Notification, powerMonitor } from 'electron'
|
||||||
import { addProfileItem, getAppConfig, patchAppConfig } from './config'
|
import { addProfileItem, getAppConfig, patchAppConfig } from './config'
|
||||||
import { quitWithoutCore, startCore, stopCore, checkAdminRestartForTun, checkHighPrivilegeCore, restartAsAdmin, initAdminStatus } from './core/manager'
|
import { quitWithoutCore, startCore, stopCore, checkAdminRestartForTun, checkHighPrivilegeCore, restartAsAdmin } from './core/manager'
|
||||||
import { triggerSysProxy } from './sys/sysproxy'
|
import { triggerSysProxy } from './sys/sysproxy'
|
||||||
import icon from '../../resources/icon.png?asset'
|
import icon from '../../resources/icon.png?asset'
|
||||||
import { createTray, hideDockIcon, showDockIcon } from './resolve/tray'
|
import { createTray, hideDockIcon, showDockIcon } from './resolve/tray'
|
||||||
@ -223,8 +223,6 @@ app.whenReady().then(async () => {
|
|||||||
|
|
||||||
await checkHighPrivilegeCoreEarly()
|
await checkHighPrivilegeCoreEarly()
|
||||||
|
|
||||||
await initAdminStatus()
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await init()
|
await init()
|
||||||
|
|
||||||
|
|||||||
@ -1,73 +1,30 @@
|
|||||||
import React from 'react'
|
const SVGComponent = (props) => (
|
||||||
import { GenIcon } from 'react-icons'
|
<svg
|
||||||
|
viewBox="0 0 123 126.11"
|
||||||
function MihomoIcon(props: any): React.JSX.Element {
|
{...props}
|
||||||
return GenIcon({
|
>
|
||||||
tag: 'svg',
|
<path
|
||||||
attr: { viewBox: '0 0 120 150' },
|
className="cls-1"
|
||||||
child: [
|
d="M123,105.41c0,15.99-13.44,20.46-20.54,20.6h-29.1c5.33-1.74,8.97-5.24,9.18-5.44.05-.05.28-.27.55-.56h19.31c1.44-.04,14.6-.78,14.6-14.6,0-9.22-3.85-21.88-34.64-29.67l-1.47-6.53c28.67,6.6,42.11,18.22,42.11,36.2Z"
|
||||||
{
|
/>
|
||||||
tag: 'path',
|
<path
|
||||||
attr: {
|
className="cls-1"
|
||||||
d: 'M123,105.41c0,15.99-13.44,20.46-20.54,20.6h-29.1c5.33-1.74,8.97-5.24,9.18-5.44.05-.05.28-.27.55-.56h19.31c1.44-.04,14.6-.78,14.6-14.6,0-9.22-3.85-21.88-34.64-29.67l-1.47-6.53c28.67,6.6,42.11,18.22,42.11,36.2Z',
|
d="M81.1,37.6c-2.42.76-4.68,1.55-6.78,2.37l-1.33-5.92c2.05-.77,4.21-1.51,6.47-2.21.27,2.03.83,3.96,1.64,5.76Z"
|
||||||
fill: 'currentColor'
|
/>
|
||||||
},
|
<circle className="cls-1" cx={53.83} cy={33.01} r={3} />
|
||||||
child: []
|
<circle className="cls-1" cx={33.83} cy={33.01} r={3} />
|
||||||
},
|
<path
|
||||||
{
|
className="cls-1"
|
||||||
tag: 'path',
|
d="M67.53,126.01h-1.49c-1.83,0-3.4-1.54-3.19-3.36.17-1.52,1.4-2.64,2.98-2.64h1.7c4.3,0,8.3-1.9,11-5.3,2.7-3.3,3.6-7.6,2.7-11.8L60.13,8.91l-.4.4c-4.3,4.4-9.9,6.7-15.9,6.7s-11.6-2.3-15.8-6.6l-.5-.4L6.33,103.01c-.9,4.1,0,8.4,2.7,11.8s6.7,5.3,11,5.3h.59c1.83,0,3.4,1.54,3.19,3.36-.17,1.52-1.4,2.64-2.98,2.64h-.8c-6.1,0-11.8-2.7-15.7-7.5-3.8-4.9-5.2-11-3.8-17L22.93,2.31c.2-1.1,1-1.9,2.1-2.2,1-.3,2.2,0,3,.8l4.3,4.3c3,3.1,7.1,4.8,11.5,4.8s8.5-1.7,11.6-4.8l4.24-4.24c.37-.37.81-.68,1.31-.83,1.95-.58,3.56.68,3.86,2.16l22.3,99.3c1.3,5.9-.1,12.1-3.9,16.8-.4.5-1.4,1.5-1.4,1.5,0,0-6.26,6.1-14.3,6.1h0Z"
|
||||||
attr: {
|
/>
|
||||||
d: 'M81.1,37.6c-2.42.76-4.68,1.55-6.78,2.37l-1.33-5.92c2.05-.77,4.21-1.51,6.47-2.21.27,2.03.83,3.96,1.64,5.76Z',
|
<path
|
||||||
fill: 'currentColor'
|
className="cls-1"
|
||||||
},
|
d="M22.23,70.11c1.6-.3,3.2.7,3.5,2.4l9,45.1c.3,1.5,1.5,2.5,3,2.5,1.7,0,3-1.3,3-3v-34.1c0-1.7,1.3-3,3-3s3,1.3,3,3v34c0,1.6,1.4,3,3.1,3h1c1.5,0,2.7-1,3-2.5l9.1-45.1c.3-1.6,1.9-2.7,3.5-2.3,1.6.3,2.7,1.9,2.3,3.5l-9.1,45c-.7,4.3-4.5,7.4-8.9,7.4h-1c-2.3,0-4.5-.9-6.1-2.3-1.6,1.4-3.7,2.3-6,2.3-4.3,0-7.9-3-8.8-7.3l-9-45.1c-.3-1.6.8-3.2,2.4-3.5Z"
|
||||||
child: []
|
/>
|
||||||
},
|
<path
|
||||||
{
|
className="cls-1"
|
||||||
tag: 'circle',
|
d="M100.75,7.39c-11.85,0-21.5,9.64-21.5,21.5,0,1,.07,1.99.21,2.95.27,2.03.83,3.96,1.64,5.76,3.35,7.53,10.9,12.79,19.65,12.79,11.86,0,21.5-9.65,21.5-21.5s-9.64-21.5-21.5-21.5ZM100.75,45.39c-9.1,0-16.5-7.4-16.5-16.5s7.4-16.5,16.5-16.5,16.5,7.4,16.5,16.5-7.4,16.5-16.5,16.5Z"
|
||||||
attr: {
|
/>
|
||||||
cx: '53.83',
|
</svg>
|
||||||
cy: '33.01',
|
);
|
||||||
r: '6',
|
export default SVGComponent;
|
||||||
fill: 'currentColor'
|
|
||||||
},
|
|
||||||
child: []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: 'circle',
|
|
||||||
attr: {
|
|
||||||
cx: '33.83',
|
|
||||||
cy: '33.01',
|
|
||||||
r: '6',
|
|
||||||
fill: 'currentColor'
|
|
||||||
},
|
|
||||||
child: []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: 'path',
|
|
||||||
attr: {
|
|
||||||
d: 'M67.53,126.01h-1.49c-1.83,0-3.4-1.54-3.19-3.36.17-1.52,1.4-2.64,2.98-2.64h1.7c4.3,0,8.3-1.9,11-5.3,2.7-3.3,3.6-7.6,2.7-11.8L60.13,8.91l-.4.4c-4.3,4.4-9.9,6.7-15.9,6.7s-11.6-2.3-15.8-6.6l-.5-.4L6.33,103.01c-.9,4.1,0,8.4,2.7,11.8s6.7,5.3,11,5.3h.59c1.83,0,3.4,1.54,3.19,3.36-.17,1.52-1.4,2.64-2.98,2.64h-.8c-6.1,0-11.8-2.7-15.7-7.5-3.8-4.9-5.2-11-3.8-17L22.93,2.31c.2-1.1,1-1.9,2.1-2.2,1-.3,2.2,0,3,.8l4.3,4.3c3,3.1,7.1,4.8,11.5,4.8s8.5-1.7,11.6-4.8l4.24-4.24c.37-.37.81-.68,1.31-.83,1.95-.58,3.56.68,3.86,2.16l22.3,99.3c1.3,5.9-.1,12.1-3.9,16.8-.4.5-1.4,1.5-1.4,1.5,0,0-6.26,6.1-14.3,6.1h0Z',
|
|
||||||
fill: 'currentColor'
|
|
||||||
},
|
|
||||||
child: []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: 'path',
|
|
||||||
attr: {
|
|
||||||
d: 'M22.23,70.11c1.6-.3,3.2.7,3.5,2.4l9,45.1c.3,1.5,1.5,2.5,3,2.5,1.7,0,3-1.3,3-3v-34.1c0-1.7,1.3-3,3-3s3,1.3,3,3v34c0,1.6,1.4,3,3.1,3h1c1.5,0,2.7-1,3-2.5l9.1-45.1c.3-1.6,1.9-2.7,3.5-2.3,1.6.3,2.7,1.9,2.3,3.5l-9.1,45c-.7,4.3-4.5,7.4-8.9,7.4h-1c-2.3,0-4.5-.9-6.1-2.3-1.6,1.4-3.7,2.3-6,2.3-4.3,0-7.9-3-8.8-7.3l-9-45.1c-.3-1.6.8-3.2,2.4-3.5Z',
|
|
||||||
fill: 'currentColor'
|
|
||||||
},
|
|
||||||
child: []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: 'path',
|
|
||||||
attr: {
|
|
||||||
d: 'M100.75,7.39c-11.85,0-21.5,9.64-21.5,21.5,0,1,.07,1.99.21,2.95.27,2.03.83,3.96,1.64,5.76,3.35,7.53,10.9,12.79,19.65,12.79,11.86,0,21.5-9.65,21.5-21.5s-9.64-21.5-21.5-21.5ZM100.75,45.39c-9.1,0-16.5-7.4-16.5-16.5s7.4-16.5,16.5-16.5,16.5,7.4,16.5,16.5-7.4,16.5-16.5,16.5Z',
|
|
||||||
fill: 'currentColor'
|
|
||||||
},
|
|
||||||
child: []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})(props)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MihomoIcon
|
|
||||||
|
|||||||