refactor: simplify main process IPC handler registration

This commit is contained in:
xmk23333 2025-12-28 12:22:28 +08:00
parent 98c8280d48
commit 573be5501e

View File

@ -126,23 +126,31 @@ import { addProfileUpdater, removeProfileUpdater } from '../core/profileUpdater'
import { readFile, writeFile } from 'fs/promises' import { readFile, writeFile } from 'fs/promises'
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
function wrapAsync<T extends (...args: any[]) => Promise<any>>( type AsyncFn = (...args: any[]) => Promise<any>
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type SyncFn = (...args: any[]) => any
function wrapAsync<T extends AsyncFn>(
fn: T fn: T
): (...args: Parameters<T>) => Promise<ReturnType<T> | { invokeError: unknown }> { ): (...args: Parameters<T>) => Promise<ReturnType<T> | { invokeError: unknown }> {
return async (...args) => { return async (...args) => {
try { try {
return await fn(...args) return await fn(...args)
} catch (e) { } catch (e) {
if (e && typeof e === 'object') { if (e && typeof e === 'object' && 'message' in e) {
if ('message' in e) { return { invokeError: e.message }
return { invokeError: e.message }
}
return { invokeError: JSON.stringify(e) }
} }
if (typeof e === 'string') { return { invokeError: typeof e === 'string' ? e : 'Unknown Error' }
return { invokeError: e } }
} }
return { invokeError: 'Unknown Error' } }
function registerHandlers(handlers: Record<string, AsyncFn | SyncFn>, async = true): void {
for (const [channel, handler] of Object.entries(handlers)) {
if (async) {
ipcMain.handle(channel, (_e, ...args) => wrapAsync(handler as AsyncFn)(...args))
} else {
ipcMain.handle(channel, (_e, ...args) => (handler as SyncFn)(...args))
} }
} }
} }
@ -190,232 +198,160 @@ async function setTitleBarOverlay(overlay: Electron.TitleBarOverlayOptions): Pro
} }
} }
export function registerIpcMainHandlers(): void { const asyncHandlers: Record<string, AsyncFn> = {
// Mihomo API // Mihomo API
ipcMain.handle('mihomoVersion', wrapAsync(mihomoVersion)) mihomoVersion,
ipcMain.handle('mihomoCloseConnection', (_e, id: string) => wrapAsync(mihomoCloseConnection)(id)) mihomoCloseConnection,
ipcMain.handle('mihomoCloseAllConnections', wrapAsync(mihomoCloseAllConnections)) mihomoCloseAllConnections,
ipcMain.handle('mihomoRules', wrapAsync(mihomoRules)) mihomoRules,
ipcMain.handle('mihomoProxies', wrapAsync(mihomoProxies)) mihomoProxies,
ipcMain.handle('mihomoGroups', wrapAsync(mihomoGroups)) mihomoGroups,
ipcMain.handle('mihomoProxyProviders', wrapAsync(mihomoProxyProviders)) mihomoProxyProviders,
ipcMain.handle('mihomoUpdateProxyProviders', (_e, name: string) => mihomoUpdateProxyProviders,
wrapAsync(mihomoUpdateProxyProviders)(name) mihomoRuleProviders,
) mihomoUpdateRuleProviders,
ipcMain.handle('mihomoRuleProviders', wrapAsync(mihomoRuleProviders)) mihomoChangeProxy,
ipcMain.handle('mihomoUpdateRuleProviders', (_e, name: string) => mihomoUnfixedProxy,
wrapAsync(mihomoUpdateRuleProviders)(name) mihomoUpgradeGeo,
) mihomoUpgrade,
ipcMain.handle('mihomoChangeProxy', (_e, group: string, proxy: string) => mihomoUpgradeUI,
wrapAsync(mihomoChangeProxy)(group, proxy) mihomoUpgradeConfig,
) mihomoProxyDelay,
ipcMain.handle('mihomoUnfixedProxy', (_e, group: string) => wrapAsync(mihomoUnfixedProxy)(group)) mihomoGroupDelay,
ipcMain.handle('mihomoUpgradeGeo', wrapAsync(mihomoUpgradeGeo)) patchMihomoConfig,
ipcMain.handle('mihomoUpgrade', wrapAsync(mihomoUpgrade)) mihomoSmartGroupWeights,
ipcMain.handle('mihomoUpgradeUI', wrapAsync(mihomoUpgradeUI)) mihomoSmartFlushCache,
ipcMain.handle('mihomoUpgradeConfig', wrapAsync(mihomoUpgradeConfig))
ipcMain.handle('mihomoProxyDelay', (_e, proxy: string, url?: string) =>
wrapAsync(mihomoProxyDelay)(proxy, url)
)
ipcMain.handle('mihomoGroupDelay', (_e, group: string, url?: string) =>
wrapAsync(mihomoGroupDelay)(group, url)
)
ipcMain.handle('patchMihomoConfig', (_e, patch: Partial<IMihomoConfig>) =>
wrapAsync(patchMihomoConfig)(patch)
)
ipcMain.handle('mihomoSmartGroupWeights', (_e, groupName: string) =>
wrapAsync(mihomoSmartGroupWeights)(groupName)
)
ipcMain.handle('mihomoSmartFlushCache', (_e, configName?: string) =>
wrapAsync(mihomoSmartFlushCache)(configName)
)
// AutoRun // AutoRun
ipcMain.handle('checkAutoRun', wrapAsync(checkAutoRun)) checkAutoRun,
ipcMain.handle('enableAutoRun', wrapAsync(enableAutoRun)) enableAutoRun,
ipcMain.handle('disableAutoRun', wrapAsync(disableAutoRun)) disableAutoRun,
// Config // Config
ipcMain.handle('getAppConfig', (_e, force?: boolean) => wrapAsync(getAppConfig)(force)) getAppConfig,
ipcMain.handle('patchAppConfig', (_e, config: Partial<IAppConfig>) => patchAppConfig,
wrapAsync(patchAppConfig)(config) getControledMihomoConfig,
) patchControledMihomoConfig,
ipcMain.handle('getControledMihomoConfig', (_e, force?: boolean) =>
wrapAsync(getControledMihomoConfig)(force)
)
ipcMain.handle('patchControledMihomoConfig', (_e, config: Partial<IMihomoConfig>) =>
wrapAsync(patchControledMihomoConfig)(config)
)
ipcMain.handle('resetAppConfig', () => resetAppConfig())
// Profile // Profile
ipcMain.handle('getProfileConfig', (_e, force?: boolean) => wrapAsync(getProfileConfig)(force)) getProfileConfig,
ipcMain.handle('setProfileConfig', (_e, config: IProfileConfig) => setProfileConfig,
wrapAsync(setProfileConfig)(config) getCurrentProfileItem,
) getProfileItem,
ipcMain.handle('getCurrentProfileItem', wrapAsync(getCurrentProfileItem)) getProfileStr,
ipcMain.handle('getProfileItem', (_e, id?: string) => wrapAsync(getProfileItem)(id)) setProfileStr,
ipcMain.handle('getProfileStr', (_e, id: string) => wrapAsync(getProfileStr)(id)) addProfileItem,
ipcMain.handle('setProfileStr', (_e, id: string, str: string) => removeProfileItem,
wrapAsync(setProfileStr)(id, str) updateProfileItem,
) changeCurrentProfile,
ipcMain.handle('addProfileItem', (_e, item: Partial<IProfileItem>) => addProfileUpdater,
wrapAsync(addProfileItem)(item) removeProfileUpdater,
)
ipcMain.handle('removeProfileItem', (_e, id: string) => wrapAsync(removeProfileItem)(id))
ipcMain.handle('updateProfileItem', (_e, item: IProfileItem) => wrapAsync(updateProfileItem)(item))
ipcMain.handle('changeCurrentProfile', (_e, id: string) => wrapAsync(changeCurrentProfile)(id))
ipcMain.handle('addProfileUpdater', (_e, item: IProfileItem) => wrapAsync(addProfileUpdater)(item))
ipcMain.handle('removeProfileUpdater', (_e, id: string) => wrapAsync(removeProfileUpdater)(id))
// Override // Override
ipcMain.handle('getOverrideConfig', (_e, force?: boolean) => wrapAsync(getOverrideConfig)(force)) getOverrideConfig,
ipcMain.handle('setOverrideConfig', (_e, config: IOverrideConfig) => setOverrideConfig,
wrapAsync(setOverrideConfig)(config) getOverrideItem,
) addOverrideItem,
ipcMain.handle('getOverrideItem', (_e, id: string) => wrapAsync(getOverrideItem)(id)) removeOverrideItem,
ipcMain.handle('addOverrideItem', (_e, item: Partial<IOverrideItem>) => updateOverrideItem,
wrapAsync(addOverrideItem)(item) getOverride,
) setOverride,
ipcMain.handle('removeOverrideItem', (_e, id: string) => wrapAsync(removeOverrideItem)(id))
ipcMain.handle('updateOverrideItem', (_e, item: IOverrideItem) =>
wrapAsync(updateOverrideItem)(item)
)
ipcMain.handle('getOverride', (_e, id: string, ext: 'js' | 'yaml' | 'log') =>
wrapAsync(getOverride)(id, ext)
)
ipcMain.handle('setOverride', (_e, id: string, ext: 'js' | 'yaml', str: string) =>
wrapAsync(setOverride)(id, ext, str)
)
// File // File
ipcMain.handle('getFileStr', (_e, filePath: string) => wrapAsync(getFileStr)(filePath)) getFileStr,
ipcMain.handle('setFileStr', (_e, filePath: string, str: string) => setFileStr,
wrapAsync(setFileStr)(filePath, str) convertMrsRuleset,
) getRuntimeConfig,
ipcMain.handle('convertMrsRuleset', (_e, filePath: string, behavior: string) => getRuntimeConfigStr,
wrapAsync(convertMrsRuleset)(filePath, behavior) getSmartOverrideContent,
) getRuleStr,
ipcMain.handle('getRuntimeConfig', wrapAsync(getRuntimeConfig)) setRuleStr,
ipcMain.handle('getRuntimeConfigStr', wrapAsync(getRuntimeConfigStr)) readTextFile,
ipcMain.handle('getSmartOverrideContent', wrapAsync(getSmartOverrideContent))
ipcMain.handle('getRuleStr', (_e, id: string) => wrapAsync(getRuleStr)(id))
ipcMain.handle('setRuleStr', (_e, id: string, str: string) => wrapAsync(setRuleStr)(id, str))
ipcMain.handle('getFilePath', (_e, ext: string[]) => getFilePath(ext))
ipcMain.handle('readTextFile', (_e, filePath: string) => wrapAsync(readTextFile)(filePath))
ipcMain.handle('openFile', (_e, type: 'profile' | 'override', id: string, ext?: 'yaml' | 'js') =>
openFile(type, id, ext)
)
// Core // Core
ipcMain.handle('restartCore', wrapAsync(restartCore)) restartCore,
ipcMain.handle('startMonitor', (_e, detached?: boolean) => wrapAsync(startMonitor)(detached)) startMonitor,
ipcMain.handle('quitWithoutCore', wrapAsync(quitWithoutCore)) quitWithoutCore,
// System // System
ipcMain.handle('triggerSysProxy', (_e, enable: boolean) => wrapAsync(triggerSysProxy)(enable)) triggerSysProxy,
ipcMain.handle('checkTunPermissions', wrapAsync(checkTunPermissions)) checkTunPermissions,
ipcMain.handle('grantTunPermissions', wrapAsync(grantTunPermissions)) grantTunPermissions,
ipcMain.handle('manualGrantCorePermition', wrapAsync(manualGrantCorePermition)) manualGrantCorePermition,
ipcMain.handle('checkAdminPrivileges', wrapAsync(checkAdminPrivileges)) checkAdminPrivileges,
ipcMain.handle('restartAsAdmin', (_e, forTun?: boolean) => wrapAsync(restartAsAdmin)(forTun)) restartAsAdmin,
ipcMain.handle('checkMihomoCorePermissions', wrapAsync(checkMihomoCorePermissions)) checkMihomoCorePermissions,
ipcMain.handle('requestTunPermissions', wrapAsync(requestTunPermissions)) requestTunPermissions,
ipcMain.handle('checkHighPrivilegeCore', wrapAsync(checkHighPrivilegeCore)) checkHighPrivilegeCore,
ipcMain.handle('showTunPermissionDialog', wrapAsync(showTunPermissionDialog)) showTunPermissionDialog,
ipcMain.handle('showErrorDialog', (_e, title: string, message: string) => showErrorDialog,
wrapAsync(showErrorDialog)(title, message) openUWPTool,
) setupFirewall,
ipcMain.handle('openUWPTool', wrapAsync(openUWPTool)) copyEnv,
ipcMain.handle('setupFirewall', wrapAsync(setupFirewall))
ipcMain.handle('getInterfaces', getInterfaces)
ipcMain.handle('setNativeTheme', (_e, theme: 'system' | 'light' | 'dark') => setNativeTheme(theme))
ipcMain.handle('copyEnv', (_e, type: 'bash' | 'cmd' | 'powershell') => wrapAsync(copyEnv)(type))
// Update // Update
ipcMain.handle('checkUpdate', wrapAsync(checkUpdate)) checkUpdate,
ipcMain.handle('downloadAndInstallUpdate', (_e, version: string) => downloadAndInstallUpdate,
wrapAsync(downloadAndInstallUpdate)(version) fetchMihomoTags,
) installSpecificMihomoCore,
ipcMain.handle('getVersion', () => app.getVersion()) clearMihomoVersionCache,
ipcMain.handle('platform', () => process.platform)
ipcMain.handle('fetchMihomoTags', (_e, forceRefresh?: boolean) =>
wrapAsync(fetchMihomoTags)(forceRefresh)
)
ipcMain.handle('installSpecificMihomoCore', (_e, version: string) =>
wrapAsync(installSpecificMihomoCore)(version)
)
ipcMain.handle('clearMihomoVersionCache', wrapAsync(clearMihomoVersionCache))
// Backup // Backup
ipcMain.handle('webdavBackup', wrapAsync(webdavBackup)) webdavBackup,
ipcMain.handle('webdavRestore', (_e, filename: string) => wrapAsync(webdavRestore)(filename)) webdavRestore,
ipcMain.handle('listWebdavBackups', wrapAsync(listWebdavBackups)) listWebdavBackups,
ipcMain.handle('webdavDelete', (_e, filename: string) => wrapAsync(webdavDelete)(filename)) webdavDelete,
ipcMain.handle('reinitWebdavBackupScheduler', wrapAsync(reinitScheduler)) reinitWebdavBackupScheduler: reinitScheduler,
ipcMain.handle('exportLocalBackup', wrapAsync(exportLocalBackup)) exportLocalBackup,
ipcMain.handle('importLocalBackup', wrapAsync(importLocalBackup)) importLocalBackup,
// SubStore // SubStore
ipcMain.handle('startSubStoreFrontendServer', wrapAsync(startSubStoreFrontendServer)) startSubStoreFrontendServer,
ipcMain.handle('stopSubStoreFrontendServer', wrapAsync(stopSubStoreFrontendServer)) stopSubStoreFrontendServer,
ipcMain.handle('startSubStoreBackendServer', wrapAsync(startSubStoreBackendServer)) startSubStoreBackendServer,
ipcMain.handle('stopSubStoreBackendServer', wrapAsync(stopSubStoreBackendServer)) stopSubStoreBackendServer,
ipcMain.handle('downloadSubStore', wrapAsync(downloadSubStore)) downloadSubStore,
ipcMain.handle('subStorePort', () => subStorePort) subStoreSubs,
ipcMain.handle('subStoreFrontendPort', () => subStoreFrontendPort) subStoreCollections,
ipcMain.handle('subStoreSubs', wrapAsync(subStoreSubs))
ipcMain.handle('subStoreCollections', wrapAsync(subStoreCollections))
// Theme // Theme
ipcMain.handle('resolveThemes', wrapAsync(resolveThemes)) resolveThemes,
ipcMain.handle('fetchThemes', wrapAsync(fetchThemes)) fetchThemes,
ipcMain.handle('importThemes', (_e, files: string[]) => wrapAsync(importThemes)(files)) importThemes,
ipcMain.handle('readTheme', (_e, theme: string) => wrapAsync(readTheme)(theme)) readTheme,
ipcMain.handle('writeTheme', (_e, theme: string, css: string) => writeTheme,
wrapAsync(writeTheme)(theme, css) applyTheme,
)
ipcMain.handle('applyTheme', (_e, theme: string) => wrapAsync(applyTheme)(theme))
// Tray // Tray
ipcMain.handle('showTrayIcon', wrapAsync(showTrayIcon)) showTrayIcon,
ipcMain.handle('closeTrayIcon', wrapAsync(closeTrayIcon)) closeTrayIcon,
ipcMain.handle('updateTrayIcon', wrapAsync(updateTrayIcon)) updateTrayIcon,
ipcMain.handle('updateTrayIconImmediate', (_e, sysProxyEnabled: boolean, tunEnabled: boolean) => // Floating Window
updateTrayIconImmediate(sysProxyEnabled, tunEnabled) showFloatingWindow,
) closeFloatingWindow,
showContextMenu,
// Window
ipcMain.handle('showMainWindow', showMainWindow)
ipcMain.handle('closeMainWindow', closeMainWindow)
ipcMain.handle('triggerMainWindow', (_e, force?: boolean) => triggerMainWindow(force))
ipcMain.handle('showFloatingWindow', wrapAsync(showFloatingWindow))
ipcMain.handle('closeFloatingWindow', wrapAsync(closeFloatingWindow))
ipcMain.handle('showContextMenu', wrapAsync(showContextMenu))
ipcMain.handle('setTitleBarOverlay', (_e, overlay: Electron.TitleBarOverlayOptions) =>
wrapAsync(setTitleBarOverlay)(overlay)
)
ipcMain.handle('setAlwaysOnTop', (_e, alwaysOnTop: boolean) =>
mainWindow?.setAlwaysOnTop(alwaysOnTop)
)
ipcMain.handle('isAlwaysOnTop', () => mainWindow?.isAlwaysOnTop())
ipcMain.handle('openDevTools', () => mainWindow?.webContents.openDevTools())
ipcMain.handle('createHeapSnapshot', () =>
v8.writeHeapSnapshot(path.join(logDir(), `${Date.now()}.heapsnapshot`))
)
// Shortcut
ipcMain.handle('registerShortcut', (_e, oldShortcut: string, newShortcut: string, action: string) =>
wrapAsync(registerShortcut)(oldShortcut, newShortcut, action)
)
// Misc // Misc
ipcMain.handle('getGistUrl', wrapAsync(getGistUrl)) getGistUrl,
ipcMain.handle('getImageDataURL', (_e, url: string) => wrapAsync(getImageDataURL)(url)) getImageDataURL,
ipcMain.handle('relaunchApp', () => { changeLanguage,
setTitleBarOverlay,
registerShortcut
}
const syncHandlers: Record<string, SyncFn> = {
resetAppConfig,
getFilePath,
openFile,
getInterfaces,
setNativeTheme,
getVersion: () => app.getVersion(),
platform: () => process.platform,
subStorePort: () => subStorePort,
subStoreFrontendPort: () => subStoreFrontendPort,
updateTrayIconImmediate,
showMainWindow,
closeMainWindow,
triggerMainWindow,
setAlwaysOnTop: (alwaysOnTop: boolean) => mainWindow?.setAlwaysOnTop(alwaysOnTop),
isAlwaysOnTop: () => mainWindow?.isAlwaysOnTop(),
openDevTools: () => mainWindow?.webContents.openDevTools(),
createHeapSnapshot: () => v8.writeHeapSnapshot(path.join(logDir(), `${Date.now()}.heapsnapshot`)),
relaunchApp: () => {
app.relaunch() app.relaunch()
app.quit() app.quit()
}) },
ipcMain.handle('quitApp', () => app.quit()) quitApp: () => app.quit()
ipcMain.handle('changeLanguage', (_e, lng: string) => wrapAsync(changeLanguage)(lng)) }
export function registerIpcMainHandlers(): void {
registerHandlers(asyncHandlers, true)
registerHandlers(syncHandlers, false)
} }