mirror of
https://gh.catmak.name/https://github.com/mihomo-party-org/mihomo-party
synced 2026-02-10 19:50:28 +08:00
refactor: simplify ipc with proxy pattern and split startCore into smaller functions
This commit is contained in:
parent
a5d2114363
commit
075132397c
@ -4,7 +4,7 @@ import { promisify } from 'util'
|
||||
import path from 'path'
|
||||
import os from 'os'
|
||||
import { createWriteStream, existsSync } from 'fs'
|
||||
import chokidar from 'chokidar'
|
||||
import chokidar, { FSWatcher } from 'chokidar'
|
||||
import { app, ipcMain } from 'electron'
|
||||
import { mainWindow } from '../window'
|
||||
import {
|
||||
@ -69,15 +69,38 @@ export {
|
||||
export { getDefaultDevice } from './dns'
|
||||
|
||||
const execFilePromise = promisify(execFile)
|
||||
const ctlParam = process.platform === 'win32' ? '-ext-ctl-pipe' : '-ext-ctl-unix'
|
||||
|
||||
chokidar.watch(path.join(mihomoCoreDir(), 'meta-update'), {}).on('unlinkDir', async () => {
|
||||
// 核心进程状态
|
||||
let child: ChildProcess
|
||||
let retry = 10
|
||||
let isRestarting = false
|
||||
|
||||
// 文件监听器
|
||||
let coreWatcher: FSWatcher | null = null
|
||||
|
||||
// 初始化核心文件监听
|
||||
export function initCoreWatcher(): void {
|
||||
if (coreWatcher) return
|
||||
|
||||
coreWatcher = chokidar.watch(path.join(mihomoCoreDir(), 'meta-update'), {})
|
||||
coreWatcher.on('unlinkDir', async () => {
|
||||
try {
|
||||
await stopCore(true)
|
||||
await startCore()
|
||||
} catch (e) {
|
||||
safeShowErrorBox('mihomo.error.coreStartFailed', `${e}`)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 清理核心文件监听
|
||||
export function cleanupCoreWatcher(): void {
|
||||
if (coreWatcher) {
|
||||
coreWatcher.close()
|
||||
coreWatcher = null
|
||||
}
|
||||
}
|
||||
|
||||
// 动态生成 IPC 路径
|
||||
export const getMihomoIpcPath = (): string => {
|
||||
@ -96,14 +119,20 @@ export const getMihomoIpcPath = (): string => {
|
||||
return `/tmp/mihomo-party-${uid}-${processId}.sock`
|
||||
}
|
||||
|
||||
const ctlParam = process.platform === 'win32' ? '-ext-ctl-pipe' : '-ext-ctl-unix'
|
||||
// 核心配置接口
|
||||
interface CoreConfig {
|
||||
corePath: string
|
||||
workDir: string
|
||||
ipcPath: string
|
||||
logLevel: LogLevel
|
||||
tunEnabled: boolean
|
||||
autoSetDNS: boolean
|
||||
cpuPriority: string
|
||||
detached: boolean
|
||||
}
|
||||
|
||||
let child: ChildProcess
|
||||
let retry = 10
|
||||
let isRestarting = false
|
||||
|
||||
export async function startCore(detached = false): Promise<Promise<void>[]> {
|
||||
// 合并配置读取,避免多次 await
|
||||
// 准备核心配置
|
||||
async function prepareCore(detached: boolean): Promise<CoreConfig> {
|
||||
const [appConfig, mihomoConfig] = await Promise.all([
|
||||
getAppConfig(),
|
||||
getControledMihomoConfig()
|
||||
@ -116,7 +145,7 @@ export async function startCore(detached = false): Promise<Promise<void>[]> {
|
||||
mihomoCpuPriority = 'PRIORITY_NORMAL'
|
||||
} = appConfig
|
||||
|
||||
const { 'log-level': logLevel, tun } = mihomoConfig
|
||||
const { 'log-level': logLevel = 'info' as LogLevel, tun } = mihomoConfig
|
||||
|
||||
// 清理旧进程
|
||||
const pidPath = path.join(dataDir(), 'core.pid')
|
||||
@ -131,8 +160,6 @@ export async function startCore(detached = false): Promise<Promise<void>[]> {
|
||||
}
|
||||
}
|
||||
|
||||
const corePath = mihomoCorePath(core)
|
||||
|
||||
// 管理 Smart 内核覆写配置
|
||||
await manageSmartOverride()
|
||||
|
||||
@ -142,6 +169,7 @@ export async function startCore(detached = false): Promise<Promise<void>[]> {
|
||||
await stopCore()
|
||||
await cleanupSocketFile()
|
||||
|
||||
// 设置 DNS
|
||||
if (tun?.enable && autoSetDNS) {
|
||||
try {
|
||||
await setPublicDNS()
|
||||
@ -151,39 +179,57 @@ export async function startCore(detached = false): Promise<Promise<void>[]> {
|
||||
}
|
||||
|
||||
// 获取动态 IPC 路径
|
||||
const dynamicIpcPath = getMihomoIpcPath()
|
||||
managerLogger.info(`Using IPC path: ${dynamicIpcPath}`)
|
||||
const ipcPath = getMihomoIpcPath()
|
||||
managerLogger.info(`Using IPC path: ${ipcPath}`)
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
await validateWindowsPipeAccess(dynamicIpcPath)
|
||||
await validateWindowsPipeAccess(ipcPath)
|
||||
}
|
||||
|
||||
// 内核日志输出
|
||||
return {
|
||||
corePath: mihomoCorePath(core),
|
||||
workDir: diffWorkDir ? mihomoProfileWorkDir(current) : mihomoWorkDir(),
|
||||
ipcPath,
|
||||
logLevel,
|
||||
tunEnabled: tun?.enable ?? false,
|
||||
autoSetDNS,
|
||||
cpuPriority: mihomoCpuPriority,
|
||||
detached
|
||||
}
|
||||
}
|
||||
|
||||
// 启动核心进程
|
||||
function spawnCoreProcess(config: CoreConfig): ChildProcess {
|
||||
const { corePath, workDir, ipcPath, cpuPriority, detached } = config
|
||||
|
||||
const stdout = createWriteStream(coreLogPath(), { flags: 'a' })
|
||||
const stderr = createWriteStream(coreLogPath(), { flags: 'a' })
|
||||
|
||||
child = spawn(
|
||||
corePath,
|
||||
['-d', diffWorkDir ? mihomoProfileWorkDir(current) : mihomoWorkDir(), ctlParam, dynamicIpcPath],
|
||||
{
|
||||
const proc = spawn(corePath, ['-d', workDir, ctlParam, ipcPath], {
|
||||
detached,
|
||||
stdio: detached ? 'ignore' : undefined
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
if (process.platform === 'win32' && child.pid) {
|
||||
os.setPriority(child.pid, os.constants.priority[mihomoCpuPriority])
|
||||
if (process.platform === 'win32' && proc.pid) {
|
||||
os.setPriority(proc.pid, os.constants.priority[cpuPriority as keyof typeof os.constants.priority])
|
||||
}
|
||||
|
||||
if (detached) {
|
||||
managerLogger.info(
|
||||
`Core process detached successfully on ${process.platform}, PID: ${child.pid}`
|
||||
)
|
||||
child.unref()
|
||||
return [new Promise(() => {})]
|
||||
if (!detached) {
|
||||
proc.stdout?.pipe(stdout)
|
||||
proc.stderr?.pipe(stderr)
|
||||
}
|
||||
|
||||
child.on('close', async (code, signal) => {
|
||||
return proc
|
||||
}
|
||||
|
||||
// 设置核心进程事件监听
|
||||
function setupCoreListeners(
|
||||
proc: ChildProcess,
|
||||
logLevel: LogLevel,
|
||||
resolve: (value: Promise<void>[]) => void,
|
||||
reject: (reason: unknown) => void
|
||||
): void {
|
||||
proc.on('close', async (code, signal) => {
|
||||
managerLogger.info(`Core closed, code: ${code}, signal: ${signal}`)
|
||||
|
||||
if (isRestarting) {
|
||||
@ -200,20 +246,19 @@ export async function startCore(detached = false): Promise<Promise<void>[]> {
|
||||
}
|
||||
})
|
||||
|
||||
child.stdout?.pipe(stdout)
|
||||
child.stderr?.pipe(stderr)
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
child.stdout?.on('data', async (data) => {
|
||||
proc.stdout?.on('data', async (data) => {
|
||||
const str = data.toString()
|
||||
|
||||
// TUN 权限错误
|
||||
if (str.includes('configure tun interface: operation not permitted')) {
|
||||
patchControledMihomoConfig({ tun: { enable: false } })
|
||||
mainWindow?.webContents.send('controledMihomoConfigUpdated')
|
||||
ipcMain.emit('updateTrayMenu')
|
||||
reject(i18next.t('tun.error.tunPermissionDenied'))
|
||||
return
|
||||
}
|
||||
|
||||
// 控制器监听错误
|
||||
const isControllerError =
|
||||
(process.platform !== 'win32' && str.includes('External controller unix listen error')) ||
|
||||
(process.platform === 'win32' && str.includes('External controller pipe listen error'))
|
||||
@ -232,8 +277,10 @@ export async function startCore(detached = false): Promise<Promise<void>[]> {
|
||||
}
|
||||
|
||||
reject(i18next.t('mihomo.error.externalControllerListenError'))
|
||||
return
|
||||
}
|
||||
|
||||
// API 就绪
|
||||
const isApiReady =
|
||||
(process.platform !== 'win32' && str.includes('RESTful API unix listening at')) ||
|
||||
(process.platform === 'win32' && str.includes('RESTful API pipe listening at'))
|
||||
@ -241,7 +288,7 @@ export async function startCore(detached = false): Promise<Promise<void>[]> {
|
||||
if (isApiReady) {
|
||||
resolve([
|
||||
new Promise((innerResolve) => {
|
||||
child.stdout?.on('data', async (innerData) => {
|
||||
proc.stdout?.on('data', async (innerData) => {
|
||||
if (
|
||||
innerData.toString().toLowerCase().includes('start initial compatible provider default')
|
||||
) {
|
||||
@ -268,9 +315,27 @@ export async function startCore(detached = false): Promise<Promise<void>[]> {
|
||||
retry = 10
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 启动核心
|
||||
export async function startCore(detached = false): Promise<Promise<void>[]> {
|
||||
const config = await prepareCore(detached)
|
||||
child = spawnCoreProcess(config)
|
||||
|
||||
if (detached) {
|
||||
managerLogger.info(
|
||||
`Core process detached successfully on ${process.platform}, PID: ${child.pid}`
|
||||
)
|
||||
child.unref()
|
||||
return [new Promise(() => {})]
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
setupCoreListeners(child, config.logLevel, resolve, reject)
|
||||
})
|
||||
}
|
||||
|
||||
// 停止核心
|
||||
export async function stopCore(force = false): Promise<void> {
|
||||
try {
|
||||
if (!force) {
|
||||
@ -299,6 +364,7 @@ export async function stopCore(force = false): Promise<void> {
|
||||
await cleanupSocketFile()
|
||||
}
|
||||
|
||||
// 重启核心
|
||||
export async function restartCore(): Promise<void> {
|
||||
if (isRestarting) {
|
||||
managerLogger.info('Core restart already in progress, skipping duplicate request')
|
||||
@ -316,6 +382,7 @@ export async function restartCore(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
// 保持核心运行
|
||||
export async function keepCoreAlive(): Promise<void> {
|
||||
try {
|
||||
await startCore(true)
|
||||
@ -327,6 +394,7 @@ export async function keepCoreAlive(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
// 退出但保持核心运行
|
||||
export async function quitWithoutCore(): Promise<void> {
|
||||
managerLogger.info(`Starting lightweight mode on platform: ${process.platform}`)
|
||||
|
||||
@ -346,6 +414,7 @@ export async function quitWithoutCore(): Promise<void> {
|
||||
app.exit()
|
||||
}
|
||||
|
||||
// 检查配置文件
|
||||
async function checkProfile(
|
||||
current: string | undefined,
|
||||
core: string = 'mihomo',
|
||||
|
||||
@ -10,7 +10,8 @@ import {
|
||||
checkHighPrivilegeCore,
|
||||
restartAsAdmin,
|
||||
initAdminStatus,
|
||||
checkAdminPrivileges
|
||||
checkAdminPrivileges,
|
||||
initCoreWatcher
|
||||
} from './core/manager'
|
||||
import { createTray } from './resolve/tray'
|
||||
import { init, initBasic, safeShowErrorBox } from './utils/init'
|
||||
@ -149,6 +150,7 @@ app.whenReady().then(async () => {
|
||||
}
|
||||
|
||||
try {
|
||||
initCoreWatcher()
|
||||
const [startPromise] = await startCore()
|
||||
startPromise.then(async () => {
|
||||
await initProfileUpdater()
|
||||
|
||||
@ -3,7 +3,7 @@ import { promisify } from 'util'
|
||||
import { stat } from 'fs/promises'
|
||||
import { existsSync } from 'fs'
|
||||
import { app, powerMonitor } from 'electron'
|
||||
import { stopCore } from './core/manager'
|
||||
import { stopCore, cleanupCoreWatcher } from './core/manager'
|
||||
import { triggerSysProxy } from './sys/sysproxy'
|
||||
import { exePath } from './utils/dirs'
|
||||
|
||||
@ -56,12 +56,14 @@ export function setupPlatformSpecifics(): void {
|
||||
export function setupAppLifecycle(): void {
|
||||
app.on('before-quit', async (e) => {
|
||||
e.preventDefault()
|
||||
cleanupCoreWatcher()
|
||||
await triggerSysProxy(false)
|
||||
await stopCore()
|
||||
app.exit()
|
||||
})
|
||||
|
||||
powerMonitor.on('shutdown', async () => {
|
||||
cleanupCoreWatcher()
|
||||
triggerSysProxy(false)
|
||||
await stopCore()
|
||||
app.exit()
|
||||
|
||||
@ -12,189 +12,309 @@ async function invoke<T>(channel: string, ...args: unknown[]): Promise<T> {
|
||||
return checkIpcError<T>(response)
|
||||
}
|
||||
|
||||
// Mihomo API
|
||||
export const mihomoVersion = (): Promise<IMihomoVersion> => invoke('mihomoVersion')
|
||||
export const mihomoCloseConnection = (id: string): Promise<void> =>
|
||||
invoke('mihomoCloseConnection', id)
|
||||
export const mihomoCloseAllConnections = (): Promise<void> => invoke('mihomoCloseAllConnections')
|
||||
export const mihomoRules = (): Promise<IMihomoRulesInfo> => invoke('mihomoRules')
|
||||
export const mihomoProxies = (): Promise<IMihomoProxies> => invoke('mihomoProxies')
|
||||
export const mihomoGroups = (): Promise<IMihomoMixedGroup[]> => invoke('mihomoGroups')
|
||||
export const mihomoProxyProviders = (): Promise<IMihomoProxyProviders> =>
|
||||
invoke('mihomoProxyProviders')
|
||||
export const mihomoUpdateProxyProviders = (name: string): Promise<void> =>
|
||||
invoke('mihomoUpdateProxyProviders', name)
|
||||
export const mihomoRuleProviders = (): Promise<IMihomoRuleProviders> =>
|
||||
invoke('mihomoRuleProviders')
|
||||
export const mihomoUpdateRuleProviders = (name: string): Promise<void> =>
|
||||
invoke('mihomoUpdateRuleProviders', name)
|
||||
export const mihomoChangeProxy = (group: string, proxy: string): Promise<IMihomoProxy> =>
|
||||
invoke('mihomoChangeProxy', group, proxy)
|
||||
export const mihomoUnfixedProxy = (group: string): Promise<IMihomoProxy> =>
|
||||
invoke('mihomoUnfixedProxy', group)
|
||||
export const mihomoUpgradeGeo = (): Promise<void> => invoke('mihomoUpgradeGeo')
|
||||
export const mihomoUpgrade = (): Promise<void> => invoke('mihomoUpgrade')
|
||||
export const mihomoUpgradeUI = (): Promise<void> => invoke('mihomoUpgradeUI')
|
||||
export const mihomoUpgradeConfig = (): Promise<void> => invoke('mihomoUpgradeConfig')
|
||||
export const mihomoProxyDelay = (proxy: string, url?: string): Promise<IMihomoDelay> =>
|
||||
invoke('mihomoProxyDelay', proxy, url)
|
||||
export const mihomoGroupDelay = (group: string, url?: string): Promise<IMihomoGroupDelay> =>
|
||||
invoke('mihomoGroupDelay', group, url)
|
||||
export const patchMihomoConfig = (patch: Partial<IMihomoConfig>): Promise<void> =>
|
||||
invoke('patchMihomoConfig', patch)
|
||||
export const mihomoSmartGroupWeights = (groupName: string): Promise<Record<string, number>> =>
|
||||
invoke('mihomoSmartGroupWeights', groupName)
|
||||
export const mihomoSmartFlushCache = (configName?: string): Promise<void> =>
|
||||
invoke('mihomoSmartFlushCache', configName)
|
||||
export const getSmartOverrideContent = (): Promise<string | null> =>
|
||||
invoke('getSmartOverrideContent')
|
||||
// IPC API 类型定义
|
||||
interface IpcApi {
|
||||
// Mihomo API
|
||||
mihomoVersion: () => Promise<IMihomoVersion>
|
||||
mihomoCloseConnection: (id: string) => Promise<void>
|
||||
mihomoCloseAllConnections: () => Promise<void>
|
||||
mihomoRules: () => Promise<IMihomoRulesInfo>
|
||||
mihomoProxies: () => Promise<IMihomoProxies>
|
||||
mihomoGroups: () => Promise<IMihomoMixedGroup[]>
|
||||
mihomoProxyProviders: () => Promise<IMihomoProxyProviders>
|
||||
mihomoUpdateProxyProviders: (name: string) => Promise<void>
|
||||
mihomoRuleProviders: () => Promise<IMihomoRuleProviders>
|
||||
mihomoUpdateRuleProviders: (name: string) => Promise<void>
|
||||
mihomoChangeProxy: (group: string, proxy: string) => Promise<IMihomoProxy>
|
||||
mihomoUnfixedProxy: (group: string) => Promise<IMihomoProxy>
|
||||
mihomoUpgradeGeo: () => Promise<void>
|
||||
mihomoUpgrade: () => Promise<void>
|
||||
mihomoUpgradeUI: () => Promise<void>
|
||||
mihomoUpgradeConfig: () => Promise<void>
|
||||
mihomoProxyDelay: (proxy: string, url?: string) => Promise<IMihomoDelay>
|
||||
mihomoGroupDelay: (group: string, url?: string) => Promise<IMihomoGroupDelay>
|
||||
patchMihomoConfig: (patch: Partial<IMihomoConfig>) => Promise<void>
|
||||
mihomoSmartGroupWeights: (groupName: string) => Promise<Record<string, number>>
|
||||
mihomoSmartFlushCache: (configName?: string) => Promise<void>
|
||||
getSmartOverrideContent: () => Promise<string | null>
|
||||
// AutoRun
|
||||
checkAutoRun: () => Promise<boolean>
|
||||
enableAutoRun: () => Promise<void>
|
||||
disableAutoRun: () => Promise<void>
|
||||
// Config
|
||||
getAppConfig: (force?: boolean) => Promise<IAppConfig>
|
||||
patchAppConfig: (patch: Partial<IAppConfig>) => Promise<void>
|
||||
getControledMihomoConfig: (force?: boolean) => Promise<Partial<IMihomoConfig>>
|
||||
patchControledMihomoConfig: (patch: Partial<IMihomoConfig>) => Promise<void>
|
||||
resetAppConfig: () => Promise<void>
|
||||
// Profile
|
||||
getProfileConfig: (force?: boolean) => Promise<IProfileConfig>
|
||||
setProfileConfig: (config: IProfileConfig) => Promise<void>
|
||||
getCurrentProfileItem: () => Promise<IProfileItem>
|
||||
getProfileItem: (id: string | undefined) => Promise<IProfileItem>
|
||||
getProfileStr: (id: string) => Promise<string>
|
||||
setProfileStr: (id: string, str: string) => Promise<void>
|
||||
addProfileItem: (item: Partial<IProfileItem>) => Promise<void>
|
||||
removeProfileItem: (id: string) => Promise<void>
|
||||
updateProfileItem: (item: IProfileItem) => Promise<void>
|
||||
changeCurrentProfile: (id: string) => Promise<void>
|
||||
addProfileUpdater: (item: IProfileItem) => Promise<void>
|
||||
removeProfileUpdater: (id: string) => Promise<void>
|
||||
// Override
|
||||
getOverrideConfig: (force?: boolean) => Promise<IOverrideConfig>
|
||||
setOverrideConfig: (config: IOverrideConfig) => Promise<void>
|
||||
getOverrideItem: (id: string) => Promise<IOverrideItem | undefined>
|
||||
addOverrideItem: (item: Partial<IOverrideItem>) => Promise<void>
|
||||
removeOverrideItem: (id: string) => Promise<void>
|
||||
updateOverrideItem: (item: IOverrideItem) => Promise<void>
|
||||
getOverride: (id: string, ext: 'js' | 'yaml' | 'log') => Promise<string>
|
||||
setOverride: (id: string, ext: 'js' | 'yaml', str: string) => Promise<void>
|
||||
// File
|
||||
getFileStr: (path: string) => Promise<string>
|
||||
setFileStr: (path: string, str: string) => Promise<void>
|
||||
convertMrsRuleset: (path: string, behavior: string) => Promise<string>
|
||||
getRuntimeConfig: () => Promise<IMihomoConfig>
|
||||
getRuntimeConfigStr: () => Promise<string>
|
||||
getRuleStr: (id: string) => Promise<string>
|
||||
setRuleStr: (id: string, str: string) => Promise<void>
|
||||
getFilePath: (ext: string[]) => Promise<string[] | undefined>
|
||||
readTextFile: (filePath: string) => Promise<string>
|
||||
openFile: (type: 'profile' | 'override', id: string, ext?: 'yaml' | 'js') => Promise<void>
|
||||
// Core
|
||||
restartCore: () => Promise<void>
|
||||
startMonitor: () => Promise<void>
|
||||
quitWithoutCore: () => Promise<void>
|
||||
// System
|
||||
triggerSysProxy: (enable: boolean) => Promise<void>
|
||||
checkTunPermissions: () => Promise<boolean>
|
||||
grantTunPermissions: () => Promise<void>
|
||||
manualGrantCorePermition: () => Promise<void>
|
||||
checkAdminPrivileges: () => Promise<boolean>
|
||||
restartAsAdmin: () => Promise<void>
|
||||
checkMihomoCorePermissions: () => Promise<boolean>
|
||||
checkHighPrivilegeCore: () => Promise<boolean>
|
||||
showTunPermissionDialog: () => Promise<boolean>
|
||||
showErrorDialog: (title: string, message: string) => Promise<void>
|
||||
openUWPTool: () => Promise<void>
|
||||
setupFirewall: () => Promise<void>
|
||||
getInterfaces: () => Promise<Record<string, NetworkInterfaceInfo[]>>
|
||||
setNativeTheme: (theme: 'system' | 'light' | 'dark') => Promise<void>
|
||||
copyEnv: (type: 'bash' | 'cmd' | 'powershell') => Promise<void>
|
||||
// Update
|
||||
checkUpdate: () => Promise<IAppVersion | undefined>
|
||||
downloadAndInstallUpdate: (version: string) => Promise<void>
|
||||
getVersion: () => Promise<string>
|
||||
platform: () => Promise<NodeJS.Platform>
|
||||
fetchMihomoTags: (
|
||||
forceRefresh?: boolean
|
||||
) => Promise<{ name: string; zipball_url: string; tarball_url: string }[]>
|
||||
installSpecificMihomoCore: (version: string) => Promise<void>
|
||||
clearMihomoVersionCache: () => Promise<void>
|
||||
// Backup
|
||||
webdavBackup: () => Promise<boolean>
|
||||
webdavRestore: (filename: string) => Promise<void>
|
||||
listWebdavBackups: () => Promise<string[]>
|
||||
webdavDelete: (filename: string) => Promise<void>
|
||||
reinitWebdavBackupScheduler: () => Promise<void>
|
||||
exportLocalBackup: () => Promise<boolean>
|
||||
importLocalBackup: () => Promise<boolean>
|
||||
// SubStore
|
||||
startSubStoreFrontendServer: () => Promise<void>
|
||||
stopSubStoreFrontendServer: () => Promise<void>
|
||||
startSubStoreBackendServer: () => Promise<void>
|
||||
stopSubStoreBackendServer: () => Promise<void>
|
||||
downloadSubStore: () => Promise<void>
|
||||
subStorePort: () => Promise<number>
|
||||
subStoreFrontendPort: () => Promise<number>
|
||||
subStoreSubs: () => Promise<ISubStoreSub[]>
|
||||
subStoreCollections: () => Promise<ISubStoreSub[]>
|
||||
// Theme
|
||||
resolveThemes: () => Promise<{ key: string; label: string; content: string }[]>
|
||||
fetchThemes: () => Promise<void>
|
||||
importThemes: (files: string[]) => Promise<void>
|
||||
readTheme: (theme: string) => Promise<string>
|
||||
writeTheme: (theme: string, css: string) => Promise<void>
|
||||
// Tray
|
||||
showTrayIcon: () => Promise<void>
|
||||
closeTrayIcon: () => Promise<void>
|
||||
updateTrayIcon: () => Promise<void>
|
||||
// Window
|
||||
showMainWindow: () => Promise<void>
|
||||
closeMainWindow: () => Promise<void>
|
||||
triggerMainWindow: () => Promise<void>
|
||||
showFloatingWindow: () => Promise<void>
|
||||
closeFloatingWindow: () => Promise<void>
|
||||
showContextMenu: () => Promise<void>
|
||||
setAlwaysOnTop: (alwaysOnTop: boolean) => Promise<void>
|
||||
isAlwaysOnTop: () => Promise<boolean>
|
||||
openDevTools: () => Promise<void>
|
||||
createHeapSnapshot: () => Promise<void>
|
||||
// Shortcut
|
||||
registerShortcut: (oldShortcut: string, newShortcut: string, action: string) => Promise<boolean>
|
||||
// Misc
|
||||
getGistUrl: () => Promise<string>
|
||||
getImageDataURL: (url: string) => Promise<string>
|
||||
relaunchApp: () => Promise<void>
|
||||
quitApp: () => Promise<void>
|
||||
}
|
||||
|
||||
// AutoRun
|
||||
export const checkAutoRun = (): Promise<boolean> => invoke('checkAutoRun')
|
||||
export const enableAutoRun = (): Promise<void> => invoke('enableAutoRun')
|
||||
export const disableAutoRun = (): Promise<void> => invoke('disableAutoRun')
|
||||
// 使用 Proxy 自动生成 IPC 调用
|
||||
const ipc = new Proxy({} as IpcApi, {
|
||||
get:
|
||||
<K extends keyof IpcApi>(_: IpcApi, channel: K) =>
|
||||
(...args: Parameters<IpcApi[K]>) =>
|
||||
invoke(channel, ...args)
|
||||
})
|
||||
|
||||
// Config
|
||||
export const getAppConfig = (force = false): Promise<IAppConfig> => invoke('getAppConfig', force)
|
||||
export const patchAppConfig = (patch: Partial<IAppConfig>): Promise<void> =>
|
||||
invoke('patchAppConfig', patch)
|
||||
export const getControledMihomoConfig = (force = false): Promise<Partial<IMihomoConfig>> =>
|
||||
invoke('getControledMihomoConfig', force)
|
||||
export const patchControledMihomoConfig = (patch: Partial<IMihomoConfig>): Promise<void> =>
|
||||
invoke('patchControledMihomoConfig', patch)
|
||||
export const resetAppConfig = (): Promise<void> => invoke('resetAppConfig')
|
||||
// 导出所有 IPC 方法
|
||||
export const {
|
||||
// Mihomo API
|
||||
mihomoVersion,
|
||||
mihomoCloseConnection,
|
||||
mihomoCloseAllConnections,
|
||||
mihomoRules,
|
||||
mihomoProxies,
|
||||
mihomoGroups,
|
||||
mihomoProxyProviders,
|
||||
mihomoUpdateProxyProviders,
|
||||
mihomoRuleProviders,
|
||||
mihomoUpdateRuleProviders,
|
||||
mihomoChangeProxy,
|
||||
mihomoUnfixedProxy,
|
||||
mihomoUpgradeGeo,
|
||||
mihomoUpgrade,
|
||||
mihomoUpgradeUI,
|
||||
mihomoUpgradeConfig,
|
||||
mihomoProxyDelay,
|
||||
mihomoGroupDelay,
|
||||
patchMihomoConfig,
|
||||
mihomoSmartGroupWeights,
|
||||
mihomoSmartFlushCache,
|
||||
getSmartOverrideContent,
|
||||
// AutoRun
|
||||
checkAutoRun,
|
||||
enableAutoRun,
|
||||
disableAutoRun,
|
||||
// Config
|
||||
getAppConfig,
|
||||
patchAppConfig,
|
||||
getControledMihomoConfig,
|
||||
patchControledMihomoConfig,
|
||||
resetAppConfig,
|
||||
// Profile
|
||||
getProfileConfig,
|
||||
setProfileConfig,
|
||||
getCurrentProfileItem,
|
||||
getProfileItem,
|
||||
getProfileStr,
|
||||
setProfileStr,
|
||||
addProfileItem,
|
||||
removeProfileItem,
|
||||
updateProfileItem,
|
||||
changeCurrentProfile,
|
||||
addProfileUpdater,
|
||||
removeProfileUpdater,
|
||||
// Override
|
||||
getOverrideConfig,
|
||||
setOverrideConfig,
|
||||
getOverrideItem,
|
||||
addOverrideItem,
|
||||
removeOverrideItem,
|
||||
updateOverrideItem,
|
||||
getOverride,
|
||||
setOverride,
|
||||
// File
|
||||
getFileStr,
|
||||
setFileStr,
|
||||
convertMrsRuleset,
|
||||
getRuntimeConfig,
|
||||
getRuntimeConfigStr,
|
||||
getRuleStr,
|
||||
setRuleStr,
|
||||
getFilePath,
|
||||
readTextFile,
|
||||
openFile,
|
||||
// Core
|
||||
restartCore,
|
||||
startMonitor,
|
||||
quitWithoutCore,
|
||||
// System
|
||||
triggerSysProxy,
|
||||
checkTunPermissions,
|
||||
grantTunPermissions,
|
||||
manualGrantCorePermition,
|
||||
checkAdminPrivileges,
|
||||
restartAsAdmin,
|
||||
checkMihomoCorePermissions,
|
||||
checkHighPrivilegeCore,
|
||||
showTunPermissionDialog,
|
||||
showErrorDialog,
|
||||
openUWPTool,
|
||||
setupFirewall,
|
||||
getInterfaces,
|
||||
setNativeTheme,
|
||||
copyEnv,
|
||||
// Update
|
||||
checkUpdate,
|
||||
downloadAndInstallUpdate,
|
||||
getVersion,
|
||||
fetchMihomoTags,
|
||||
installSpecificMihomoCore,
|
||||
clearMihomoVersionCache,
|
||||
// Backup
|
||||
webdavBackup,
|
||||
webdavRestore,
|
||||
listWebdavBackups,
|
||||
webdavDelete,
|
||||
reinitWebdavBackupScheduler,
|
||||
exportLocalBackup,
|
||||
importLocalBackup,
|
||||
// SubStore
|
||||
startSubStoreFrontendServer,
|
||||
stopSubStoreFrontendServer,
|
||||
startSubStoreBackendServer,
|
||||
stopSubStoreBackendServer,
|
||||
downloadSubStore,
|
||||
subStorePort,
|
||||
subStoreFrontendPort,
|
||||
subStoreSubs,
|
||||
subStoreCollections,
|
||||
// Theme
|
||||
resolveThemes,
|
||||
fetchThemes,
|
||||
importThemes,
|
||||
readTheme,
|
||||
writeTheme,
|
||||
// Tray
|
||||
showTrayIcon,
|
||||
closeTrayIcon,
|
||||
updateTrayIcon,
|
||||
// Window
|
||||
showMainWindow,
|
||||
closeMainWindow,
|
||||
triggerMainWindow,
|
||||
showFloatingWindow,
|
||||
closeFloatingWindow,
|
||||
showContextMenu,
|
||||
setAlwaysOnTop,
|
||||
isAlwaysOnTop,
|
||||
openDevTools,
|
||||
createHeapSnapshot,
|
||||
// Shortcut
|
||||
registerShortcut,
|
||||
// Misc
|
||||
getGistUrl,
|
||||
getImageDataURL,
|
||||
relaunchApp,
|
||||
quitApp
|
||||
} = ipc
|
||||
|
||||
// Profile
|
||||
export const getProfileConfig = (force = false): Promise<IProfileConfig> =>
|
||||
invoke('getProfileConfig', force)
|
||||
export const setProfileConfig = (config: IProfileConfig): Promise<void> =>
|
||||
invoke('setProfileConfig', config)
|
||||
export const getCurrentProfileItem = (): Promise<IProfileItem> => invoke('getCurrentProfileItem')
|
||||
export const getProfileItem = (id: string | undefined): Promise<IProfileItem> =>
|
||||
invoke('getProfileItem', id)
|
||||
export const getProfileStr = (id: string): Promise<string> => invoke('getProfileStr', id)
|
||||
export const setProfileStr = (id: string, str: string): Promise<void> =>
|
||||
invoke('setProfileStr', id, str)
|
||||
export const addProfileItem = (item: Partial<IProfileItem>): Promise<void> =>
|
||||
invoke('addProfileItem', item)
|
||||
export const removeProfileItem = (id: string): Promise<void> => invoke('removeProfileItem', id)
|
||||
export const updateProfileItem = (item: IProfileItem): Promise<void> =>
|
||||
invoke('updateProfileItem', item)
|
||||
export const changeCurrentProfile = (id: string): Promise<void> =>
|
||||
invoke('changeCurrentProfile', id)
|
||||
export const addProfileUpdater = (item: IProfileItem): Promise<void> =>
|
||||
invoke('addProfileUpdater', item)
|
||||
export const removeProfileUpdater = (id: string): Promise<void> =>
|
||||
invoke('removeProfileUpdater', id)
|
||||
// platform 需要重命名导出
|
||||
export const getPlatform = ipc.platform
|
||||
|
||||
// Override
|
||||
export const getOverrideConfig = (force = false): Promise<IOverrideConfig> =>
|
||||
invoke('getOverrideConfig', force)
|
||||
export const setOverrideConfig = (config: IOverrideConfig): Promise<void> =>
|
||||
invoke('setOverrideConfig', config)
|
||||
export const getOverrideItem = (id: string): Promise<IOverrideItem | undefined> =>
|
||||
invoke('getOverrideItem', id)
|
||||
export const addOverrideItem = (item: Partial<IOverrideItem>): Promise<void> =>
|
||||
invoke('addOverrideItem', item)
|
||||
export const removeOverrideItem = (id: string): Promise<void> => invoke('removeOverrideItem', id)
|
||||
export const updateOverrideItem = (item: IOverrideItem): Promise<void> =>
|
||||
invoke('updateOverrideItem', item)
|
||||
export const getOverride = (id: string, ext: 'js' | 'yaml' | 'log'): Promise<string> =>
|
||||
invoke('getOverride', id, ext)
|
||||
export const setOverride = (id: string, ext: 'js' | 'yaml', str: string): Promise<void> =>
|
||||
invoke('setOverride', id, ext, str)
|
||||
|
||||
// File
|
||||
export const getFileStr = (path: string): Promise<string> => invoke('getFileStr', path)
|
||||
export const setFileStr = (path: string, str: string): Promise<void> =>
|
||||
invoke('setFileStr', path, str)
|
||||
export const convertMrsRuleset = (path: string, behavior: string): Promise<string> =>
|
||||
invoke('convertMrsRuleset', path, behavior)
|
||||
export const getRuntimeConfig = (): Promise<IMihomoConfig> => invoke('getRuntimeConfig')
|
||||
export const getRuntimeConfigStr = (): Promise<string> => invoke('getRuntimeConfigStr')
|
||||
export const getRuleStr = (id: string): Promise<string> => invoke('getRuleStr', id)
|
||||
export const setRuleStr = (id: string, str: string): Promise<void> => invoke('setRuleStr', id, str)
|
||||
export const getFilePath = (ext: string[]): Promise<string[] | undefined> =>
|
||||
invoke('getFilePath', ext)
|
||||
export const readTextFile = (filePath: string): Promise<string> => invoke('readTextFile', filePath)
|
||||
export const openFile = (
|
||||
type: 'profile' | 'override',
|
||||
id: string,
|
||||
ext?: 'yaml' | 'js'
|
||||
): Promise<void> => invoke('openFile', type, id, ext)
|
||||
|
||||
// Core
|
||||
export const restartCore = (): Promise<void> => invoke('restartCore')
|
||||
export const startMonitor = (): Promise<void> => invoke('startMonitor')
|
||||
export const quitWithoutCore = (): Promise<void> => invoke('quitWithoutCore')
|
||||
|
||||
// System
|
||||
export const triggerSysProxy = (enable: boolean): Promise<void> => invoke('triggerSysProxy', enable)
|
||||
export const checkTunPermissions = (): Promise<boolean> => invoke('checkTunPermissions')
|
||||
export const grantTunPermissions = (): Promise<void> => invoke('grantTunPermissions')
|
||||
export const manualGrantCorePermition = (): Promise<void> => invoke('manualGrantCorePermition')
|
||||
export const checkAdminPrivileges = (): Promise<boolean> => invoke('checkAdminPrivileges')
|
||||
export const restartAsAdmin = (): Promise<void> => invoke('restartAsAdmin')
|
||||
export const checkMihomoCorePermissions = (): Promise<boolean> =>
|
||||
invoke('checkMihomoCorePermissions')
|
||||
export const checkHighPrivilegeCore = (): Promise<boolean> => invoke('checkHighPrivilegeCore')
|
||||
export const showTunPermissionDialog = (): Promise<boolean> => invoke('showTunPermissionDialog')
|
||||
export const showErrorDialog = (title: string, message: string): Promise<void> =>
|
||||
invoke('showErrorDialog', title, message)
|
||||
export const openUWPTool = (): Promise<void> => invoke('openUWPTool')
|
||||
export const setupFirewall = (): Promise<void> => invoke('setupFirewall')
|
||||
export const getInterfaces = (): Promise<Record<string, NetworkInterfaceInfo[]>> =>
|
||||
invoke('getInterfaces')
|
||||
export const setNativeTheme = (theme: 'system' | 'light' | 'dark'): Promise<void> =>
|
||||
invoke('setNativeTheme', theme)
|
||||
export const copyEnv = (type: 'bash' | 'cmd' | 'powershell'): Promise<void> =>
|
||||
invoke('copyEnv', type)
|
||||
|
||||
// Update
|
||||
export const checkUpdate = (): Promise<IAppVersion | undefined> => invoke('checkUpdate')
|
||||
export const downloadAndInstallUpdate = (version: string): Promise<void> =>
|
||||
invoke('downloadAndInstallUpdate', version)
|
||||
export const getVersion = (): Promise<string> => invoke('getVersion')
|
||||
export const getPlatform = (): Promise<NodeJS.Platform> => invoke('platform')
|
||||
export const fetchMihomoTags = (
|
||||
forceRefresh = false
|
||||
): Promise<{ name: string; zipball_url: string; tarball_url: string }[]> =>
|
||||
invoke('fetchMihomoTags', forceRefresh)
|
||||
export const installSpecificMihomoCore = (version: string): Promise<void> =>
|
||||
invoke('installSpecificMihomoCore', version)
|
||||
export const clearMihomoVersionCache = (): Promise<void> => invoke('clearMihomoVersionCache')
|
||||
|
||||
// Backup
|
||||
export const webdavBackup = (): Promise<boolean> => invoke('webdavBackup')
|
||||
export const webdavRestore = (filename: string): Promise<void> => invoke('webdavRestore', filename)
|
||||
export const listWebdavBackups = (): Promise<string[]> => invoke('listWebdavBackups')
|
||||
export const webdavDelete = (filename: string): Promise<void> => invoke('webdavDelete', filename)
|
||||
export const reinitWebdavBackupScheduler = (): Promise<void> =>
|
||||
invoke('reinitWebdavBackupScheduler')
|
||||
export const exportLocalBackup = (): Promise<boolean> => invoke('exportLocalBackup')
|
||||
export const importLocalBackup = (): Promise<boolean> => invoke('importLocalBackup')
|
||||
|
||||
// SubStore
|
||||
export const startSubStoreFrontendServer = (): Promise<void> =>
|
||||
invoke('startSubStoreFrontendServer')
|
||||
export const stopSubStoreFrontendServer = (): Promise<void> => invoke('stopSubStoreFrontendServer')
|
||||
export const startSubStoreBackendServer = (): Promise<void> => invoke('startSubStoreBackendServer')
|
||||
export const stopSubStoreBackendServer = (): Promise<void> => invoke('stopSubStoreBackendServer')
|
||||
export const downloadSubStore = (): Promise<void> => invoke('downloadSubStore')
|
||||
export const subStorePort = (): Promise<number> => invoke('subStorePort')
|
||||
export const subStoreFrontendPort = (): Promise<number> => invoke('subStoreFrontendPort')
|
||||
export const subStoreSubs = (): Promise<ISubStoreSub[]> => invoke('subStoreSubs')
|
||||
export const subStoreCollections = (): Promise<ISubStoreSub[]> => invoke('subStoreCollections')
|
||||
|
||||
// Theme
|
||||
export const resolveThemes = (): Promise<{ key: string; label: string; content: string }[]> =>
|
||||
invoke('resolveThemes')
|
||||
export const fetchThemes = (): Promise<void> => invoke('fetchThemes')
|
||||
export const importThemes = (files: string[]): Promise<void> => invoke('importThemes', files)
|
||||
export const readTheme = (theme: string): Promise<string> => invoke('readTheme', theme)
|
||||
export const writeTheme = (theme: string, css: string): Promise<void> =>
|
||||
invoke('writeTheme', theme, css)
|
||||
// 需要特殊处理的函数
|
||||
|
||||
// applyTheme: 防抖处理,避免频繁调用
|
||||
let applyThemeRunning = false
|
||||
let pendingTheme: string | null = null
|
||||
|
||||
@ -216,21 +336,7 @@ export async function applyTheme(theme: string): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
// Tray
|
||||
export const showTrayIcon = (): Promise<void> => invoke('showTrayIcon')
|
||||
export const closeTrayIcon = (): Promise<void> => invoke('closeTrayIcon')
|
||||
export const updateTrayIcon = (): Promise<void> => invoke('updateTrayIcon')
|
||||
export function updateTrayIconImmediate(sysProxyEnabled: boolean, tunEnabled: boolean): void {
|
||||
window.electron.ipcRenderer.invoke('updateTrayIconImmediate', sysProxyEnabled, tunEnabled)
|
||||
}
|
||||
|
||||
// Window
|
||||
export const showMainWindow = (): Promise<void> => invoke('showMainWindow')
|
||||
export const closeMainWindow = (): Promise<void> => invoke('closeMainWindow')
|
||||
export const triggerMainWindow = (): Promise<void> => invoke('triggerMainWindow')
|
||||
export const showFloatingWindow = (): Promise<void> => invoke('showFloatingWindow')
|
||||
export const closeFloatingWindow = (): Promise<void> => invoke('closeFloatingWindow')
|
||||
export const showContextMenu = (): Promise<void> => invoke('showContextMenu')
|
||||
// setTitleBarOverlay: 需要静默处理不支持的平台
|
||||
export async function setTitleBarOverlay(overlay: TitleBarOverlayOptions): Promise<void> {
|
||||
try {
|
||||
await invoke<void>('setTitleBarOverlay', overlay)
|
||||
@ -238,21 +344,8 @@ export async function setTitleBarOverlay(overlay: TitleBarOverlayOptions): Promi
|
||||
// Not supported on this platform
|
||||
}
|
||||
}
|
||||
export const setAlwaysOnTop = (alwaysOnTop: boolean): Promise<void> =>
|
||||
invoke('setAlwaysOnTop', alwaysOnTop)
|
||||
export const isAlwaysOnTop = (): Promise<boolean> => invoke('isAlwaysOnTop')
|
||||
export const openDevTools = (): Promise<void> => invoke('openDevTools')
|
||||
export const createHeapSnapshot = (): Promise<void> => invoke('createHeapSnapshot')
|
||||
|
||||
// Shortcut
|
||||
export const registerShortcut = (
|
||||
oldShortcut: string,
|
||||
newShortcut: string,
|
||||
action: string
|
||||
): Promise<boolean> => invoke('registerShortcut', oldShortcut, newShortcut, action)
|
||||
|
||||
// Misc
|
||||
export const getGistUrl = (): Promise<string> => invoke('getGistUrl')
|
||||
export const getImageDataURL = (url: string): Promise<string> => invoke('getImageDataURL', url)
|
||||
export const relaunchApp = (): Promise<void> => invoke('relaunchApp')
|
||||
export const quitApp = (): Promise<void> => invoke('quitApp')
|
||||
// updateTrayIconImmediate: 同步调用,不等待结果
|
||||
export function updateTrayIconImmediate(sysProxyEnabled: boolean, tunEnabled: boolean): void {
|
||||
window.electron.ipcRenderer.invoke('updateTrayIconImmediate', sysProxyEnabled, tunEnabled)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user