diff --git a/src/main/config/profile.ts b/src/main/config/profile.ts index d10738d..35b6b79 100644 --- a/src/main/config/profile.ts +++ b/src/main/config/profile.ts @@ -1,6 +1,6 @@ import { getControledMihomoConfig } from './controledMihomo' import { profileConfigPath, profilePath } from '../utils/dirs' -import { restartCore } from '../core/manager' +import { startCore } from '../core/manager' import { getAppConfig } from './app' import { window } from '..' import axios from 'axios' @@ -28,7 +28,7 @@ export async function changeCurrentProfile(id: string): Promise { profileConfig.current = id getCurrentProfile(true) try { - restartCore() + await startCore() } catch (e) { profileConfig.current = oldId getCurrentProfile(true) @@ -38,7 +38,7 @@ export async function changeCurrentProfile(id: string): Promise { } } -export async function updateProfileItem(item: IProfileItem): Promise { +export function updateProfileItem(item: IProfileItem): void { const index = profileConfig.items.findIndex((i) => i.id === item.id) profileConfig.items[index] = item fs.writeFileSync(profileConfigPath(), yaml.stringify(profileConfig)) @@ -174,11 +174,11 @@ export function getProfileStr(id: string): string { return fs.readFileSync(profilePath(id), 'utf-8') } -export function setProfileStr(id: string, content: string): void { +export async function setProfileStr(id: string, content: string): Promise { fs.writeFileSync(profilePath(id), content, 'utf-8') if (id === getProfileConfig().current) { getCurrentProfile(true) - restartCore() + await startCore() } } diff --git a/src/main/core/manager.ts b/src/main/core/manager.ts index 7736a9e..7307192 100644 --- a/src/main/core/manager.ts +++ b/src/main/core/manager.ts @@ -1,4 +1,4 @@ -import { ChildProcess, execFileSync, execSync, spawn } from 'child_process' +import { ChildProcess, execFile, execSync, spawn } from 'child_process' import { logPath, mihomoCorePath, @@ -12,61 +12,97 @@ import { dialog, safeStorage } from 'electron' import fs from 'fs' let child: ChildProcess +let retry = 10 -export function startCore(): void { +export async function startCore(): Promise { const corePath = mihomoCorePath(getAppConfig().core ?? 'mihomo') grantCorePermition(corePath) generateProfile() - checkProfile() + await checkProfile() stopCore() - child = spawn(corePath, ['-d', mihomoWorkDir()]) - child.stdout?.on('data', (data) => { - fs.writeFileSync( - logPath(), - data - .toString() - .split('\n') - .map((line: string) => { - if (line) return `[Mihomo]: ${line}` - return '' - }) - .filter(Boolean) - .join('\n'), - { - flag: 'a' + return new Promise((resolve, reject) => { + child = spawn(corePath, ['-d', mihomoWorkDir()]) + child.stdout?.on('data', (data) => { + if (data.toString().includes('External controller listen error')) { + if (retry) { + retry-- + resolve(startCore()) + } else { + dialog.showErrorBox('External controller listen error', data.toString()) + reject('External controller listen error') + } } - ) - }) - child.on('close', (code, signal) => { - fs.writeFileSync(logPath(), `[Manager]: Core closed, code: ${code}, signal: ${signal}\n`, { - flag: 'a' + if (data.toString().includes('RESTful API listening at')) { + retry = 10 + resolve() + } + fs.writeFileSync( + logPath(), + data + .toString() + .split('\n') + .map((line: string) => { + if (line) return `[Mihomo]: ${line}` + return '' + }) + .filter(Boolean) + .join('\n'), + { + flag: 'a' + } + ) }) - fs.writeFileSync(logPath(), `[Manager]: Restart Core\n`, { - flag: 'a' + child.on('error', (err) => { + if (retry) { + retry-- + startCore() + } else { + dialog.showErrorBox('External controller listen error', err.toString()) + reject(err) + } + }) + child.on('close', async (code, signal) => { + fs.writeFileSync(logPath(), `[Manager]: Core closed, code: ${code}, signal: ${signal}\n`, { + flag: 'a' + }) + fs.writeFileSync(logPath(), `[Manager]: Restart Core\n`, { + flag: 'a' + }) + await startCore() }) - restartCore() }) } export function stopCore(): void { if (child) { child.removeAllListeners() - child.kill('SIGINT') + if (!child.kill('SIGINT')) { + stopCore() + } } } -export function restartCore(): void { - startCore() -} - -export function checkProfile(): void { +export function checkProfile(): Promise { const corePath = mihomoCorePath(getAppConfig().core ?? 'mihomo') - try { - execFileSync(corePath, ['-t', '-f', mihomoWorkConfigPath(), '-d', mihomoTestDir()]) - } catch (e) { - dialog.showErrorBox('Profile check failed', `${e}`) - throw new Error('Profile check failed') - } + return new Promise((resolve, reject) => { + const child = execFile(corePath, ['-t', '-f', mihomoWorkConfigPath(), '-d', mihomoTestDir()]) + child.stdout?.on('data', (data) => { + data + .toString() + .split('\n') + .forEach((line: string) => { + if (line.includes('level=error')) { + dialog.showErrorBox('Profile Check Failed', line.split('level=error')[1]) + reject(line) + } + }) + }) + child.on('close', (code) => { + if (code === 0) { + resolve() + } + }) + }) } export function grantCorePermition(corePath: string): void { diff --git a/src/main/utils/ipc.ts b/src/main/utils/ipc.ts index 7c7f88d..d5ca78c 100644 --- a/src/main/utils/ipc.ts +++ b/src/main/utils/ipc.ts @@ -29,7 +29,7 @@ import { setProfileStr, updateProfileItem } from '../config' -import { isEncryptionAvailable, restartCore } from '../core/manager' +import { isEncryptionAvailable, startCore } from '../core/manager' import { triggerSysProxy } from '../resolve/sysproxy' import { checkUpdate } from '../resolve/autoUpdater' @@ -62,7 +62,7 @@ export function registerIpcMainHandlers(): void { ipcMain.handle('changeCurrentProfile', (_e, id) => changeCurrentProfile(id)) ipcMain.handle('addProfileItem', (_e, item) => addProfileItem(item)) ipcMain.handle('removeProfileItem', (_e, id) => removeProfileItem(id)) - ipcMain.handle('restartCore', restartCore) + ipcMain.handle('restartCore', startCore) ipcMain.handle('triggerSysProxy', (_e, enable) => triggerSysProxy(enable)) ipcMain.handle('isEncryptionAvailable', isEncryptionAvailable) ipcMain.handle('encryptString', (_e, str) => safeStorage.encryptString(str)) diff --git a/src/renderer/src/components/sider/mihomo-core-card.tsx.tsx b/src/renderer/src/components/sider/mihomo-core-card.tsx.tsx index a4d01ae..3cc48e1 100644 --- a/src/renderer/src/components/sider/mihomo-core-card.tsx.tsx +++ b/src/renderer/src/components/sider/mihomo-core-card.tsx.tsx @@ -50,8 +50,12 @@ const MihomoCoreCard: React.FC = () => { size="sm" variant="light" color="default" - onPress={() => { - restartCore() + onPress={async () => { + await restartCore() + mutate() + setTimeout(() => { + mutate() + }, 2000) }} > diff --git a/src/renderer/src/pages/proxies.tsx b/src/renderer/src/pages/proxies.tsx index df38263..f3f67a0 100644 --- a/src/renderer/src/pages/proxies.tsx +++ b/src/renderer/src/pages/proxies.tsx @@ -18,10 +18,9 @@ const Proxies: React.FC = () => { const { data: proxies, mutate } = useSWR('mihomoProxies', mihomoProxies) const { appConfig, patchAppConfig } = useAppConfig() const { proxyDisplayMode = 'simple', proxyDisplayOrder = 'default' } = appConfig || {} - const groups = useMemo(() => { const groups: IMihomoGroup[] = [] - if (proxies) { + if (proxies && proxies.proxies && proxies.proxies['GLOBAL']) { const globalGroup = proxies.proxies['GLOBAL'] as IMihomoGroup for (const global of globalGroup.all) { if (isGroup(proxies.proxies[global])) {