mirror of
https://gh.catmak.name/https://github.com/mihomo-party-org/mihomo-party
synced 2025-12-27 05:00:30 +08:00
use async and optimize error handling
This commit is contained in:
parent
2b62a2f1e1
commit
2c5aa1a482
@ -1,22 +1,23 @@
|
|||||||
|
import { readFile, writeFile } from 'fs/promises'
|
||||||
import { appConfigPath } from '../utils/dirs'
|
import { appConfigPath } from '../utils/dirs'
|
||||||
import yaml from 'yaml'
|
import yaml from 'yaml'
|
||||||
import fs from 'fs'
|
|
||||||
|
|
||||||
export let appConfig: IAppConfig // config.yaml
|
let appConfig: IAppConfig // config.yaml
|
||||||
|
|
||||||
export function getAppConfig(force = false): IAppConfig {
|
export async function getAppConfig(force = false): Promise<IAppConfig> {
|
||||||
if (force || !appConfig) {
|
if (force || !appConfig) {
|
||||||
appConfig = yaml.parse(fs.readFileSync(appConfigPath(), 'utf-8'))
|
const data = await readFile(appConfigPath(), 'utf-8')
|
||||||
|
appConfig = yaml.parse(data)
|
||||||
}
|
}
|
||||||
return appConfig
|
return appConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setAppConfig(patch: Partial<IAppConfig>): void {
|
export async function patchAppConfig(patch: Partial<IAppConfig>): Promise<void> {
|
||||||
if (patch.sysProxy) {
|
if (patch.sysProxy) {
|
||||||
const oldSysProxy = appConfig.sysProxy || {}
|
const oldSysProxy = appConfig.sysProxy || {}
|
||||||
const newSysProxy = Object.assign(oldSysProxy, patch.sysProxy)
|
const newSysProxy = Object.assign(oldSysProxy, patch.sysProxy)
|
||||||
patch.sysProxy = newSysProxy
|
patch.sysProxy = newSysProxy
|
||||||
}
|
}
|
||||||
appConfig = Object.assign(appConfig, patch)
|
appConfig = Object.assign(appConfig, patch)
|
||||||
fs.writeFileSync(appConfigPath(), yaml.stringify(appConfig))
|
await writeFile(appConfigPath(), yaml.stringify(appConfig))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,21 +1,22 @@
|
|||||||
import { controledMihomoConfigPath } from '../utils/dirs'
|
import { controledMihomoConfigPath } from '../utils/dirs'
|
||||||
|
import { readFile, writeFile } from 'fs/promises'
|
||||||
import yaml from 'yaml'
|
import yaml from 'yaml'
|
||||||
import fs from 'fs'
|
|
||||||
import { getAxios, startMihomoMemory, startMihomoTraffic } from '../core/mihomoApi'
|
import { getAxios, startMihomoMemory, startMihomoTraffic } from '../core/mihomoApi'
|
||||||
import { generateProfile } from '../resolve/factory'
|
import { generateProfile } from '../resolve/factory'
|
||||||
import { getAppConfig } from './app'
|
import { getAppConfig } from './app'
|
||||||
|
|
||||||
export let controledMihomoConfig: Partial<IMihomoConfig> // mihomo.yaml
|
let controledMihomoConfig: Partial<IMihomoConfig> // mihomo.yaml
|
||||||
|
|
||||||
export function getControledMihomoConfig(force = false): Partial<IMihomoConfig> {
|
export async function getControledMihomoConfig(force = false): Promise<Partial<IMihomoConfig>> {
|
||||||
if (force || !controledMihomoConfig) {
|
if (force || !controledMihomoConfig) {
|
||||||
controledMihomoConfig = yaml.parse(fs.readFileSync(controledMihomoConfigPath(), 'utf-8'))
|
const data = await readFile(controledMihomoConfigPath(), 'utf-8')
|
||||||
|
controledMihomoConfig = yaml.parse(data)
|
||||||
}
|
}
|
||||||
return controledMihomoConfig
|
return controledMihomoConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setControledMihomoConfig(patch: Partial<IMihomoConfig>): void {
|
export async function patchControledMihomoConfig(patch: Partial<IMihomoConfig>): Promise<void> {
|
||||||
const { useNameserverPolicy } = getAppConfig()
|
const { useNameserverPolicy } = await getAppConfig()
|
||||||
if (patch.tun) {
|
if (patch.tun) {
|
||||||
const oldTun = controledMihomoConfig.tun || {}
|
const oldTun = controledMihomoConfig.tun || {}
|
||||||
const newTun = Object.assign(oldTun, patch.tun)
|
const newTun = Object.assign(oldTun, patch.tun)
|
||||||
@ -35,11 +36,12 @@ export function setControledMihomoConfig(patch: Partial<IMihomoConfig>): void {
|
|||||||
patch.sniffer = newSniffer
|
patch.sniffer = newSniffer
|
||||||
}
|
}
|
||||||
controledMihomoConfig = Object.assign(controledMihomoConfig, patch)
|
controledMihomoConfig = Object.assign(controledMihomoConfig, patch)
|
||||||
|
|
||||||
if (patch['external-controller'] || patch.secret) {
|
if (patch['external-controller'] || patch.secret) {
|
||||||
getAxios(true)
|
await getAxios(true)
|
||||||
startMihomoMemory()
|
await startMihomoMemory()
|
||||||
startMihomoTraffic()
|
await startMihomoTraffic()
|
||||||
}
|
}
|
||||||
generateProfile()
|
await generateProfile()
|
||||||
fs.writeFileSync(controledMihomoConfigPath(), yaml.stringify(controledMihomoConfig))
|
await writeFile(controledMihomoConfigPath(), yaml.stringify(controledMihomoConfig), 'utf-8')
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
export { getAppConfig, setAppConfig } from './app'
|
export { getAppConfig, patchAppConfig } from './app'
|
||||||
export { getControledMihomoConfig, setControledMihomoConfig } from './controledMihomo'
|
export { getControledMihomoConfig, patchControledMihomoConfig } from './controledMihomo'
|
||||||
export {
|
export {
|
||||||
getProfile,
|
getProfile,
|
||||||
getCurrentProfileItem,
|
getCurrentProfileItem,
|
||||||
|
|||||||
@ -1,47 +1,56 @@
|
|||||||
import { overrideConfigPath, overridePath } from '../utils/dirs'
|
import { overrideConfigPath, overridePath } from '../utils/dirs'
|
||||||
import yaml from 'yaml'
|
|
||||||
import fs from 'fs'
|
|
||||||
import { dialog } from 'electron'
|
|
||||||
import axios from 'axios'
|
|
||||||
import { getControledMihomoConfig } from './controledMihomo'
|
import { getControledMihomoConfig } from './controledMihomo'
|
||||||
|
import { readFile, writeFile, rm } from 'fs/promises'
|
||||||
|
import { existsSync } from 'fs'
|
||||||
|
import axios from 'axios'
|
||||||
|
import yaml from 'yaml'
|
||||||
|
|
||||||
let overrideConfig: IOverrideConfig // override.yaml
|
let overrideConfig: IOverrideConfig // override.yaml
|
||||||
|
|
||||||
export function getOverrideConfig(force = false): IOverrideConfig {
|
export async function getOverrideConfig(force = false): Promise<IOverrideConfig> {
|
||||||
if (force || !overrideConfig) {
|
if (force || !overrideConfig) {
|
||||||
overrideConfig = yaml.parse(fs.readFileSync(overrideConfigPath(), 'utf-8'))
|
const data = await readFile(overrideConfigPath(), 'utf-8')
|
||||||
|
overrideConfig = yaml.parse(data)
|
||||||
}
|
}
|
||||||
return overrideConfig
|
return overrideConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setOverrideConfig(config: IOverrideConfig): void {
|
export async function setOverrideConfig(config: IOverrideConfig): Promise<void> {
|
||||||
overrideConfig = config
|
overrideConfig = config
|
||||||
fs.writeFileSync(overrideConfigPath(), yaml.stringify(overrideConfig))
|
await writeFile(overrideConfigPath(), yaml.stringify(overrideConfig), 'utf-8')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getOverrideItem(id: string): IOverrideItem | undefined {
|
export async function getOverrideItem(id: string | undefined): Promise<IOverrideItem | undefined> {
|
||||||
return overrideConfig.items.find((item) => item.id === id)
|
const { items } = await getOverrideConfig()
|
||||||
|
return items.find((item) => item.id === id)
|
||||||
}
|
}
|
||||||
export function updateOverrideItem(item: IOverrideItem): void {
|
|
||||||
const index = overrideConfig.items.findIndex((i) => i.id === item.id)
|
export async function updateOverrideItem(item: IOverrideItem): Promise<void> {
|
||||||
overrideConfig.items[index] = item
|
const config = await getOverrideConfig()
|
||||||
fs.writeFileSync(overrideConfigPath(), yaml.stringify(overrideConfig))
|
const index = config.items.findIndex((i) => i.id === item.id)
|
||||||
|
if (index === -1) {
|
||||||
|
throw new Error('Override not found')
|
||||||
|
}
|
||||||
|
config.items[index] = item
|
||||||
|
await setOverrideConfig(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function addOverrideItem(item: Partial<IOverrideItem>): Promise<void> {
|
export async function addOverrideItem(item: Partial<IOverrideItem>): Promise<void> {
|
||||||
|
const config = await getOverrideConfig()
|
||||||
const newItem = await createOverride(item)
|
const newItem = await createOverride(item)
|
||||||
if (overrideConfig.items.find((i) => i.id === newItem.id)) {
|
if (await getOverrideItem(item.id)) {
|
||||||
updateOverrideItem(newItem)
|
updateOverrideItem(newItem)
|
||||||
} else {
|
} else {
|
||||||
overrideConfig.items.push(newItem)
|
config.items.push(newItem)
|
||||||
}
|
}
|
||||||
fs.writeFileSync(overrideConfigPath(), yaml.stringify(overrideConfig))
|
await setOverrideConfig(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function removeOverrideItem(id: string): void {
|
export async function removeOverrideItem(id: string): Promise<void> {
|
||||||
overrideConfig.items = overrideConfig.items?.filter((item) => item.id !== id)
|
const config = await getOverrideConfig()
|
||||||
fs.writeFileSync(overrideConfigPath(), yaml.stringify(overrideConfig))
|
config.items = config.items?.filter((item) => item.id !== id)
|
||||||
fs.rmSync(overridePath(id))
|
await setOverrideConfig(config)
|
||||||
|
await rm(overridePath(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createOverride(item: Partial<IOverrideItem>): Promise<IOverrideItem> {
|
export async function createOverride(item: Partial<IOverrideItem>): Promise<IOverrideItem> {
|
||||||
@ -55,31 +64,21 @@ export async function createOverride(item: Partial<IOverrideItem>): Promise<IOve
|
|||||||
} as IOverrideItem
|
} as IOverrideItem
|
||||||
switch (newItem.type) {
|
switch (newItem.type) {
|
||||||
case 'remote': {
|
case 'remote': {
|
||||||
if (!item.url) {
|
const { 'mixed-port': mixedPort = 7890 } = await getControledMihomoConfig()
|
||||||
throw new Error('URL is required for remote script')
|
if (!item.url) throw new Error('Empty URL')
|
||||||
}
|
const res = await axios.get(item.url, {
|
||||||
try {
|
proxy: {
|
||||||
const res = await axios.get(item.url, {
|
protocol: 'http',
|
||||||
proxy: {
|
host: '127.0.0.1',
|
||||||
protocol: 'http',
|
port: mixedPort
|
||||||
host: '127.0.0.1',
|
}
|
||||||
port: getControledMihomoConfig()['mixed-port'] || 7890
|
})
|
||||||
},
|
const data = res.data
|
||||||
responseType: 'text'
|
await setOverride(id, data)
|
||||||
})
|
|
||||||
const data = res.data
|
|
||||||
setOverride(id, data)
|
|
||||||
} catch (e) {
|
|
||||||
dialog.showErrorBox('Failed to fetch remote script', `${e}\nurl: ${item.url}`)
|
|
||||||
throw new Error(`Failed to fetch remote script ${e}`)
|
|
||||||
}
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'local': {
|
case 'local': {
|
||||||
if (!item.file) {
|
const data = item.file || ''
|
||||||
throw new Error('File is required for local script')
|
|
||||||
}
|
|
||||||
const data = item.file
|
|
||||||
setOverride(id, data)
|
setOverride(id, data)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -88,13 +87,13 @@ export async function createOverride(item: Partial<IOverrideItem>): Promise<IOve
|
|||||||
return newItem
|
return newItem
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getOverride(id: string): string {
|
export async function getOverride(id: string): Promise<string> {
|
||||||
if (!fs.existsSync(overridePath(id))) {
|
if (!existsSync(overridePath(id))) {
|
||||||
return `function main(config){ return config }`
|
return `function main(config){ return config }`
|
||||||
}
|
}
|
||||||
return fs.readFileSync(overridePath(id), 'utf-8')
|
return await readFile(overridePath(id), 'utf-8')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setOverride(id: string, content: string): void {
|
export async function setOverride(id: string, content: string): Promise<void> {
|
||||||
fs.writeFileSync(overridePath(id), content, 'utf-8')
|
await writeFile(overridePath(id), content, 'utf-8')
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,87 +1,168 @@
|
|||||||
import { getControledMihomoConfig } from './controledMihomo'
|
import { getControledMihomoConfig } from './controledMihomo'
|
||||||
import { profileConfigPath, profilePath } from '../utils/dirs'
|
import { profileConfigPath, profilePath } from '../utils/dirs'
|
||||||
|
import { addProfileUpdater } from '../core/profileUpdater'
|
||||||
|
import { readFile, rm, writeFile } from 'fs/promises'
|
||||||
import { restartCore } from '../core/manager'
|
import { restartCore } from '../core/manager'
|
||||||
import { getAppConfig } from './app'
|
import { getAppConfig } from './app'
|
||||||
import { window } from '..'
|
import { mainWindow } from '..'
|
||||||
|
import { existsSync } from 'fs'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import yaml from 'yaml'
|
import yaml from 'yaml'
|
||||||
import fs from 'fs'
|
import { defaultProfile } from '../utils/template'
|
||||||
import { dialog } from 'electron'
|
|
||||||
import { addProfileUpdater } from '../core/profileUpdater'
|
|
||||||
|
|
||||||
let profileConfig: IProfileConfig // profile.yaml
|
let profileConfig: IProfileConfig // profile.yaml
|
||||||
|
|
||||||
export function getProfileConfig(force = false): IProfileConfig {
|
export async function getProfileConfig(force = false): Promise<IProfileConfig> {
|
||||||
if (force || !profileConfig) {
|
if (force || !profileConfig) {
|
||||||
profileConfig = yaml.parse(fs.readFileSync(profileConfigPath(), 'utf-8'))
|
const data = await readFile(profileConfigPath(), 'utf-8')
|
||||||
|
profileConfig = yaml.parse(data)
|
||||||
}
|
}
|
||||||
return profileConfig
|
return profileConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setProfileConfig(config: IProfileConfig): void {
|
export async function setProfileConfig(config: IProfileConfig): Promise<void> {
|
||||||
profileConfig = config
|
profileConfig = config
|
||||||
window?.webContents.send('profileConfigUpdated')
|
mainWindow?.webContents.send('profileConfigUpdated')
|
||||||
fs.writeFileSync(profileConfigPath(), yaml.stringify(profileConfig))
|
await writeFile(profileConfigPath(), yaml.stringify(config), 'utf-8')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getProfileItem(id: string | undefined): IProfileItem {
|
export async function getProfileItem(id: string | undefined): Promise<IProfileItem | undefined> {
|
||||||
const items = getProfileConfig().items
|
const { items } = await getProfileConfig()
|
||||||
return items?.find((item) => item.id === id) || { id: 'default', type: 'local', name: '空白订阅' }
|
if (!id || id === 'default') return { id: 'default', type: 'local', name: '空白订阅' }
|
||||||
|
return items.find((item) => item.id === id)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function changeCurrentProfile(id: string): Promise<void> {
|
export async function changeCurrentProfile(id: string): Promise<void> {
|
||||||
const oldId = getProfileConfig().current
|
const config = await getProfileConfig()
|
||||||
profileConfig.current = id
|
const current = config.current
|
||||||
|
config.current = id
|
||||||
|
await setProfileConfig(config)
|
||||||
try {
|
try {
|
||||||
await restartCore()
|
await restartCore()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
profileConfig.current = oldId
|
config.current = current
|
||||||
|
throw e
|
||||||
} finally {
|
} finally {
|
||||||
window?.webContents.send('profileConfigUpdated')
|
await setProfileConfig(config)
|
||||||
fs.writeFileSync(profileConfigPath(), yaml.stringify(profileConfig))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateProfileItem(item: IProfileItem): void {
|
export async function updateProfileItem(item: IProfileItem): Promise<void> {
|
||||||
const index = profileConfig.items.findIndex((i) => i.id === item.id)
|
const config = await getProfileConfig()
|
||||||
profileConfig.items[index] = item
|
const index = config.items.findIndex((i) => i.id === item.id)
|
||||||
addProfileUpdater(item.id)
|
if (index === -1) {
|
||||||
fs.writeFileSync(profileConfigPath(), yaml.stringify(profileConfig))
|
throw new Error('Profile not found')
|
||||||
window?.webContents.send('profileConfigUpdated')
|
}
|
||||||
|
config.items[index] = item
|
||||||
|
await setProfileConfig(config)
|
||||||
|
await addProfileUpdater(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function addProfileItem(item: Partial<IProfileItem>): Promise<void> {
|
export async function addProfileItem(item: Partial<IProfileItem>): Promise<void> {
|
||||||
const newItem = await createProfile(item)
|
const newItem = await createProfile(item)
|
||||||
if (profileConfig.items.find((i) => i.id === newItem.id)) {
|
const config = await getProfileConfig()
|
||||||
updateProfileItem(newItem)
|
if (await getProfileItem(item.id)) {
|
||||||
|
await updateProfileItem(newItem)
|
||||||
} else {
|
} else {
|
||||||
profileConfig.items.push(newItem)
|
config.items.push(newItem)
|
||||||
}
|
}
|
||||||
|
await setProfileConfig(config)
|
||||||
|
|
||||||
if (!getProfileConfig().current) {
|
if (!config.current) {
|
||||||
changeCurrentProfile(newItem.id)
|
await changeCurrentProfile(newItem.id)
|
||||||
}
|
}
|
||||||
addProfileUpdater(newItem.id)
|
await addProfileUpdater(newItem)
|
||||||
fs.writeFileSync(profileConfigPath(), yaml.stringify(profileConfig))
|
|
||||||
window?.webContents.send('profileConfigUpdated')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function removeProfileItem(id: string): void {
|
export async function removeProfileItem(id: string): Promise<void> {
|
||||||
profileConfig.items = profileConfig.items?.filter((item) => item.id !== id)
|
const config = await getProfileConfig()
|
||||||
if (profileConfig.current === id) {
|
config.items = config.items?.filter((item) => item.id !== id)
|
||||||
if (profileConfig.items.length > 0) {
|
if (config.current === id) {
|
||||||
profileConfig.current = profileConfig.items[0]?.id
|
if (config.items.length > 0) {
|
||||||
|
config.current = config.items[0].id
|
||||||
} else {
|
} else {
|
||||||
profileConfig.current = undefined
|
config.current = undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fs.writeFileSync(profileConfigPath(), yaml.stringify(profileConfig))
|
await setProfileConfig(config)
|
||||||
fs.rmSync(profilePath(id))
|
if (existsSync(profilePath(id))) {
|
||||||
window?.webContents.send('profileConfigUpdated')
|
await rm(profilePath(id))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCurrentProfileItem(): IProfileItem {
|
export async function getCurrentProfileItem(): Promise<IProfileItem> {
|
||||||
return getProfileItem(getProfileConfig().current)
|
const { current } = await getProfileConfig()
|
||||||
|
return (await getProfileItem(current)) || { id: 'default', type: 'local', name: '空白订阅' }
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createProfile(item: Partial<IProfileItem>): Promise<IProfileItem> {
|
||||||
|
const id = item.id || new Date().getTime().toString(16)
|
||||||
|
const newItem = {
|
||||||
|
id,
|
||||||
|
name: item.name || (item.type === 'remote' ? 'Remote File' : 'Local File'),
|
||||||
|
type: item.type,
|
||||||
|
url: item.url,
|
||||||
|
interval: item.interval || 0,
|
||||||
|
updated: new Date().getTime()
|
||||||
|
} as IProfileItem
|
||||||
|
switch (newItem.type) {
|
||||||
|
case 'remote': {
|
||||||
|
const { userAgent = 'clash-meta' } = await getAppConfig()
|
||||||
|
const { 'mixed-port': mixedPort = 7890 } = await getControledMihomoConfig()
|
||||||
|
if (!item.url) throw new Error('Empty URL')
|
||||||
|
const res = await axios.get(item.url, {
|
||||||
|
proxy: {
|
||||||
|
protocol: 'http',
|
||||||
|
host: '127.0.0.1',
|
||||||
|
port: mixedPort
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
'User-Agent': userAgent
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const data = res.data
|
||||||
|
const headers = res.headers
|
||||||
|
if (headers['content-disposition'] && newItem.name === 'Remote File') {
|
||||||
|
newItem.name = parseFilename(headers['content-disposition'])
|
||||||
|
}
|
||||||
|
if (headers['profile-web-page-url']) {
|
||||||
|
newItem.home = headers['profile-web-page-url']
|
||||||
|
}
|
||||||
|
if (headers['profile-update-interval']) {
|
||||||
|
newItem.interval = parseInt(headers['profile-update-interval']) * 60
|
||||||
|
}
|
||||||
|
if (headers['subscription-userinfo']) {
|
||||||
|
newItem.extra = parseSubinfo(headers['subscription-userinfo'])
|
||||||
|
}
|
||||||
|
await setProfileStr(id, data)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'local': {
|
||||||
|
const data = item.file || ''
|
||||||
|
await setProfileStr(id, data)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newItem
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getProfileStr(id: string | undefined): Promise<string> {
|
||||||
|
if (existsSync(profilePath(id || 'default'))) {
|
||||||
|
return await readFile(profilePath(id || 'default'), 'utf-8')
|
||||||
|
} else {
|
||||||
|
return yaml.stringify(defaultProfile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setProfileStr(id: string, content: string): Promise<void> {
|
||||||
|
const { current } = await getProfileConfig()
|
||||||
|
await writeFile(profilePath(id), content, 'utf-8')
|
||||||
|
if (current === id) await restartCore()
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getProfile(id: string | undefined): Promise<IMihomoConfig> {
|
||||||
|
const profile = await getProfileStr(id)
|
||||||
|
return yaml.parse(profile)
|
||||||
}
|
}
|
||||||
|
|
||||||
// attachment;filename=xxx.yaml; filename*=UTF-8''%xx%xx%xx
|
// attachment;filename=xxx.yaml; filename*=UTF-8''%xx%xx%xx
|
||||||
@ -105,80 +186,3 @@ function parseSubinfo(str: string): ISubscriptionUserInfo {
|
|||||||
})
|
})
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createProfile(item: Partial<IProfileItem>): Promise<IProfileItem> {
|
|
||||||
const id = item.id || new Date().getTime().toString(16)
|
|
||||||
const newItem = {
|
|
||||||
id,
|
|
||||||
name: item.name || (item.type === 'remote' ? 'Remote File' : 'Local File'),
|
|
||||||
type: item.type,
|
|
||||||
url: item.url,
|
|
||||||
interval: item.interval || 0,
|
|
||||||
updated: new Date().getTime()
|
|
||||||
} as IProfileItem
|
|
||||||
switch (newItem.type) {
|
|
||||||
case 'remote': {
|
|
||||||
if (!item.url) {
|
|
||||||
throw new Error('URL is required for remote profile')
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const ua = getAppConfig().userAgent || 'clash-meta'
|
|
||||||
const res = await axios.get(item.url, {
|
|
||||||
proxy: {
|
|
||||||
protocol: 'http',
|
|
||||||
host: '127.0.0.1',
|
|
||||||
port: getControledMihomoConfig()['mixed-port'] || 7890
|
|
||||||
},
|
|
||||||
headers: {
|
|
||||||
'User-Agent': ua
|
|
||||||
},
|
|
||||||
responseType: 'text'
|
|
||||||
})
|
|
||||||
const data = res.data
|
|
||||||
const headers = res.headers
|
|
||||||
if (headers['content-disposition'] && newItem.name === 'Remote File') {
|
|
||||||
newItem.name = parseFilename(headers['content-disposition'])
|
|
||||||
}
|
|
||||||
if (headers['profile-web-page-url']) {
|
|
||||||
newItem.home = headers['profile-web-page-url']
|
|
||||||
}
|
|
||||||
if (headers['profile-update-interval']) {
|
|
||||||
newItem.interval = parseInt(headers['profile-update-interval']) * 60
|
|
||||||
}
|
|
||||||
if (headers['subscription-userinfo']) {
|
|
||||||
newItem.extra = parseSubinfo(headers['subscription-userinfo'])
|
|
||||||
}
|
|
||||||
await setProfileStr(id, data)
|
|
||||||
} catch (e) {
|
|
||||||
dialog.showErrorBox('Failed to fetch remote profile', `${e}\nurl: ${item.url}`)
|
|
||||||
throw new Error(`Failed to fetch remote profile ${e}`)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case 'local': {
|
|
||||||
if (!item.file) {
|
|
||||||
throw new Error('File is required for local profile')
|
|
||||||
}
|
|
||||||
const data = item.file
|
|
||||||
await setProfileStr(id, data)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return newItem
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getProfileStr(id: string): string {
|
|
||||||
return fs.readFileSync(profilePath(id), 'utf-8')
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function setProfileStr(id: string, content: string): Promise<void> {
|
|
||||||
fs.writeFileSync(profilePath(id), content, 'utf-8')
|
|
||||||
if (id === getProfileConfig().current) {
|
|
||||||
await restartCore()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getProfile(id: string | undefined): IMihomoConfig {
|
|
||||||
return yaml.parse(getProfileStr(id || 'default'))
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { ChildProcess, execFile, execSync, spawn } from 'child_process'
|
import { ChildProcess, exec, execFile, spawn } from 'child_process'
|
||||||
import {
|
import {
|
||||||
logPath,
|
logPath,
|
||||||
mihomoCorePath,
|
mihomoCorePath,
|
||||||
@ -7,29 +7,44 @@ import {
|
|||||||
mihomoWorkDir
|
mihomoWorkDir
|
||||||
} from '../utils/dirs'
|
} from '../utils/dirs'
|
||||||
import { generateProfile } from '../resolve/factory'
|
import { generateProfile } from '../resolve/factory'
|
||||||
import { getAppConfig, setAppConfig } from '../config'
|
import { getAppConfig, patchAppConfig } from '../config'
|
||||||
import { dialog, safeStorage } from 'electron'
|
import { dialog, safeStorage } from 'electron'
|
||||||
import fs from 'fs'
|
|
||||||
import { pauseWebsockets } from './mihomoApi'
|
import { pauseWebsockets } from './mihomoApi'
|
||||||
|
import { writeFile } from 'fs/promises'
|
||||||
|
import { promisify } from 'util'
|
||||||
|
|
||||||
let child: ChildProcess
|
let child: ChildProcess
|
||||||
let retry = 10
|
let retry = 10
|
||||||
|
|
||||||
export async function startCore(): Promise<void> {
|
export async function startCore(): Promise<void> {
|
||||||
const corePath = mihomoCorePath(getAppConfig().core ?? 'mihomo')
|
const { core = 'mihomo' } = await getAppConfig()
|
||||||
grantCorePermition(corePath)
|
const corePath = mihomoCorePath(core)
|
||||||
generateProfile()
|
await grantCorePermition(corePath)
|
||||||
|
await generateProfile()
|
||||||
await checkProfile()
|
await checkProfile()
|
||||||
stopCore()
|
stopCore()
|
||||||
|
child = spawn(corePath, ['-d', mihomoWorkDir()])
|
||||||
|
child.on('close', async (code, signal) => {
|
||||||
|
await writeFile(logPath(), `[Manager]: Core closed, code: ${code}, signal: ${signal}\n`, {
|
||||||
|
flag: 'a'
|
||||||
|
})
|
||||||
|
if (retry) {
|
||||||
|
await writeFile(logPath(), `[Manager]: Try Restart Core\n`, { flag: 'a' })
|
||||||
|
retry--
|
||||||
|
await restartCore()
|
||||||
|
} else {
|
||||||
|
stopCore()
|
||||||
|
}
|
||||||
|
})
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
child = spawn(corePath, ['-d', mihomoWorkDir()])
|
child.stdout?.on('data', async (data) => {
|
||||||
child.stdout?.on('data', (data) => {
|
|
||||||
if (data.toString().includes('External controller listen error')) {
|
if (data.toString().includes('External controller listen error')) {
|
||||||
if (retry) {
|
if (retry) {
|
||||||
retry--
|
retry--
|
||||||
resolve(startCore())
|
resolve(await startCore())
|
||||||
} else {
|
} else {
|
||||||
dialog.showErrorBox('External controller listen error', data.toString())
|
dialog.showErrorBox('内核连接失败', '请尝试更改外部控制端口后重启内核')
|
||||||
|
stopCore()
|
||||||
reject('External controller listen error')
|
reject('External controller listen error')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -37,36 +52,7 @@ export async function startCore(): Promise<void> {
|
|||||||
retry = 10
|
retry = 10
|
||||||
resolve()
|
resolve()
|
||||||
}
|
}
|
||||||
fs.writeFileSync(
|
await writeFile(logPath(), data, { flag: 'a' })
|
||||||
logPath(),
|
|
||||||
data
|
|
||||||
.toString()
|
|
||||||
.split('\n')
|
|
||||||
.map((line: string) => {
|
|
||||||
if (line) return `[Mihomo]: ${line}`
|
|
||||||
return ''
|
|
||||||
})
|
|
||||||
.filter(Boolean)
|
|
||||||
.join('\n'),
|
|
||||||
{
|
|
||||||
flag: 'a'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
})
|
|
||||||
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'
|
|
||||||
})
|
|
||||||
if (retry) {
|
|
||||||
retry--
|
|
||||||
await restartCore()
|
|
||||||
} else {
|
|
||||||
dialog.showErrorBox('Mihomo Core Closed', `Core closed, code: ${code}, signal: ${signal}`)
|
|
||||||
stopCore()
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -84,44 +70,49 @@ export async function restartCore(): Promise<void> {
|
|||||||
recover()
|
recover()
|
||||||
}
|
}
|
||||||
|
|
||||||
export function checkProfile(): Promise<void> {
|
async function checkProfile(): Promise<void> {
|
||||||
const corePath = mihomoCorePath(getAppConfig().core ?? 'mihomo')
|
const { core = 'mihomo' } = await getAppConfig()
|
||||||
return new Promise((resolve, reject) => {
|
const corePath = mihomoCorePath(core)
|
||||||
const child = execFile(corePath, ['-t', '-f', mihomoWorkConfigPath(), '-d', mihomoTestDir()])
|
const execFilePromise = promisify(execFile)
|
||||||
child.stdout?.on('data', (data) => {
|
try {
|
||||||
data
|
await execFilePromise(corePath, ['-t', '-f', mihomoWorkConfigPath(), '-d', mihomoTestDir()])
|
||||||
.toString()
|
} catch (error) {
|
||||||
|
if (error instanceof Error && 'stdout' in error) {
|
||||||
|
const { stdout } = error as { stdout: string }
|
||||||
|
const errorLines = stdout
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.forEach((line: string) => {
|
.filter((line) => line.includes('level=error'))
|
||||||
if (line.includes('level=error')) {
|
.map((line) => line.split('level=error')[1])
|
||||||
dialog.showErrorBox('Profile Check Failed', line.split('level=error')[1])
|
throw new Error(`Profile Check Failed:\n${errorLines.join('\n')}`)
|
||||||
reject(line)
|
} else {
|
||||||
}
|
throw error
|
||||||
})
|
}
|
||||||
})
|
}
|
||||||
child.on('close', (code) => {
|
|
||||||
if (code === 0) {
|
|
||||||
resolve()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function grantCorePermition(corePath: string): void {
|
export async function grantCorePermition(corePath: string): Promise<void> {
|
||||||
if (getAppConfig().encryptedPassword && isEncryptionAvailable()) {
|
const { encryptedPassword } = await getAppConfig()
|
||||||
const password = safeStorage.decryptString(Buffer.from(getAppConfig().encryptedPassword ?? []))
|
const execPromise = promisify(exec)
|
||||||
try {
|
if (encryptedPassword && isEncryptionAvailable()) {
|
||||||
if (process.platform === 'linux') {
|
const password = safeStorage.decryptString(Buffer.from(encryptedPassword))
|
||||||
execSync(
|
if (process.platform === 'linux') {
|
||||||
|
try {
|
||||||
|
await execPromise(
|
||||||
`echo "${password}" | sudo -S setcap cap_net_bind_service,cap_net_admin,cap_sys_ptrace,cap_dac_read_search,cap_dac_override,cap_net_raw=+ep ${corePath}`
|
`echo "${password}" | sudo -S setcap cap_net_bind_service,cap_net_admin,cap_sys_ptrace,cap_dac_read_search,cap_dac_override,cap_net_raw=+ep ${corePath}`
|
||||||
)
|
)
|
||||||
|
} catch (error) {
|
||||||
|
patchAppConfig({ encryptedPassword: undefined })
|
||||||
|
throw error
|
||||||
}
|
}
|
||||||
if (process.platform === 'darwin') {
|
}
|
||||||
execSync(`echo "${password}" | sudo -S chown root:admin ${corePath}`)
|
if (process.platform === 'darwin') {
|
||||||
execSync(`echo "${password}" | sudo -S chmod +sx ${corePath}`)
|
try {
|
||||||
|
await execPromise(`echo "${password}" | sudo -S chown root:admin ${corePath}`)
|
||||||
|
await execPromise(`echo "${password}" | sudo -S chmod +sx ${corePath}`)
|
||||||
|
} catch (error) {
|
||||||
|
patchAppConfig({ encryptedPassword: undefined })
|
||||||
|
throw error
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
setAppConfig({ encryptedPassword: undefined })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import axios, { AxiosInstance } from 'axios'
|
import axios, { AxiosInstance } from 'axios'
|
||||||
import { getAppConfig, getControledMihomoConfig } from '../config'
|
import { getAppConfig, getControledMihomoConfig } from '../config'
|
||||||
|
import { mainWindow } from '..'
|
||||||
import WebSocket from 'ws'
|
import WebSocket from 'ws'
|
||||||
import { window } from '..'
|
|
||||||
|
|
||||||
let axiosIns: AxiosInstance = null!
|
let axiosIns: AxiosInstance = null!
|
||||||
let mihomoTrafficWs: WebSocket | null = null
|
let mihomoTrafficWs: WebSocket | null = null
|
||||||
@ -15,9 +15,9 @@ let connectionsRetry = 10
|
|||||||
|
|
||||||
export const getAxios = async (force: boolean = false): Promise<AxiosInstance> => {
|
export const getAxios = async (force: boolean = false): Promise<AxiosInstance> => {
|
||||||
if (axiosIns && !force) return axiosIns
|
if (axiosIns && !force) return axiosIns
|
||||||
|
const controledMihomoConfig = await getControledMihomoConfig()
|
||||||
let server = getControledMihomoConfig()['external-controller']
|
let server = controledMihomoConfig['external-controller']
|
||||||
const secret = getControledMihomoConfig().secret ?? ''
|
const secret = controledMihomoConfig.secret ?? ''
|
||||||
if (server?.startsWith(':')) server = `127.0.0.1${server}`
|
if (server?.startsWith(':')) server = `127.0.0.1${server}`
|
||||||
|
|
||||||
axiosIns = axios.create({
|
axiosIns = axios.create({
|
||||||
@ -26,138 +26,127 @@ export const getAxios = async (force: boolean = false): Promise<AxiosInstance> =
|
|||||||
headers: secret ? { Authorization: `Bearer ${secret}` } : {},
|
headers: secret ? { Authorization: `Bearer ${secret}` } : {},
|
||||||
timeout: 15000
|
timeout: 15000
|
||||||
})
|
})
|
||||||
axiosIns.interceptors.response.use((r) => r.data)
|
|
||||||
|
axiosIns.interceptors.response.use(
|
||||||
|
(response) => {
|
||||||
|
return response.data
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
if (error.response && error.response.data) {
|
||||||
|
return Promise.reject(error.response.data)
|
||||||
|
}
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
return axiosIns
|
return axiosIns
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function mihomoVersion(): Promise<IMihomoVersion> {
|
export async function mihomoVersion(): Promise<IMihomoVersion> {
|
||||||
const instance = await getAxios()
|
const instance = await getAxios()
|
||||||
return (await instance.get('/version').catch(() => {
|
try {
|
||||||
return { version: '-' }
|
return await instance.get('/version')
|
||||||
})) as IMihomoVersion
|
} catch (error) {
|
||||||
|
return { version: '-', meta: true }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const patchMihomoConfig = async (patch: Partial<IMihomoConfig>): Promise<void> => {
|
export const patchMihomoConfig = async (patch: Partial<IMihomoConfig>): Promise<void> => {
|
||||||
const instance = await getAxios()
|
const instance = await getAxios()
|
||||||
return (await instance.patch('/configs', patch).catch((e) => {
|
return await instance.patch('/configs', patch)
|
||||||
return e.response.data
|
|
||||||
})) as Promise<void>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const mihomoCloseConnection = async (id: string): Promise<void> => {
|
export const mihomoCloseConnection = async (id: string): Promise<void> => {
|
||||||
const instance = await getAxios()
|
const instance = await getAxios()
|
||||||
return (await instance.delete(`/connections/${encodeURIComponent(id)}`).catch((e) => {
|
return await instance.delete(`/connections/${encodeURIComponent(id)}`)
|
||||||
return e.response.data
|
|
||||||
})) as Promise<void>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const mihomoCloseAllConnections = async (): Promise<void> => {
|
export const mihomoCloseAllConnections = async (): Promise<void> => {
|
||||||
const instance = await getAxios()
|
const instance = await getAxios()
|
||||||
return (await instance.delete('/connections').catch((e) => {
|
return await instance.delete('/connections')
|
||||||
return e.response.data
|
|
||||||
})) as Promise<void>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const mihomoRules = async (): Promise<IMihomoRulesInfo> => {
|
export const mihomoRules = async (): Promise<IMihomoRulesInfo> => {
|
||||||
const instance = await getAxios()
|
const instance = await getAxios()
|
||||||
return (await instance.get('/rules').catch(() => {
|
try {
|
||||||
|
return await instance.get('/rules')
|
||||||
|
} catch (e) {
|
||||||
return { rules: [] }
|
return { rules: [] }
|
||||||
})) as IMihomoRulesInfo
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const mihomoProxies = async (): Promise<IMihomoProxies> => {
|
export const mihomoProxies = async (): Promise<IMihomoProxies> => {
|
||||||
const instance = await getAxios()
|
const instance = await getAxios()
|
||||||
return (await instance.get('/proxies').catch(() => {
|
try {
|
||||||
|
return await instance.get('/proxies')
|
||||||
|
} catch (e) {
|
||||||
return { proxies: {} }
|
return { proxies: {} }
|
||||||
})) as IMihomoProxies
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const mihomoProxyProviders = async (): Promise<IMihomoProxyProviders> => {
|
export const mihomoProxyProviders = async (): Promise<IMihomoProxyProviders> => {
|
||||||
const instance = await getAxios()
|
const instance = await getAxios()
|
||||||
return (await instance.get('/providers/proxies').catch(() => {
|
try {
|
||||||
|
return await instance.get('/providers/proxies')
|
||||||
|
} catch (e) {
|
||||||
return { providers: {} }
|
return { providers: {} }
|
||||||
})) as IMihomoProxyProviders
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const mihomoUpdateProxyProviders = async (name: string): Promise<void> => {
|
export const mihomoUpdateProxyProviders = async (name: string): Promise<void> => {
|
||||||
const instance = await getAxios()
|
const instance = await getAxios()
|
||||||
return instance.put(`/providers/proxies/${encodeURIComponent(name)}`).catch((e) => {
|
return await instance.put(`/providers/proxies/${encodeURIComponent(name)}`)
|
||||||
return e.response.data
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const mihomoRuleProviders = async (): Promise<IMihomoRuleProviders> => {
|
export const mihomoRuleProviders = async (): Promise<IMihomoRuleProviders> => {
|
||||||
const instance = await getAxios()
|
const instance = await getAxios()
|
||||||
return (await instance.get('/providers/rules').catch(() => {
|
try {
|
||||||
|
return await instance.get('/providers/rules')
|
||||||
|
} catch (e) {
|
||||||
return { providers: {} }
|
return { providers: {} }
|
||||||
})) as IMihomoRuleProviders
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const mihomoUpdateRuleProviders = async (name: string): Promise<void> => {
|
export const mihomoUpdateRuleProviders = async (name: string): Promise<void> => {
|
||||||
const instance = await getAxios()
|
const instance = await getAxios()
|
||||||
return instance.put(`/providers/rules/${encodeURIComponent(name)}`).catch((e) => {
|
return await instance.put(`/providers/rules/${encodeURIComponent(name)}`)
|
||||||
return e.response.data
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const mihomoChangeProxy = async (group: string, proxy: string): Promise<IMihomoProxy> => {
|
export const mihomoChangeProxy = async (group: string, proxy: string): Promise<IMihomoProxy> => {
|
||||||
const instance = await getAxios()
|
const instance = await getAxios()
|
||||||
return (await instance.put(`/proxies/${encodeURIComponent(group)}`, { name: proxy }).catch(() => {
|
return await instance.put(`/proxies/${encodeURIComponent(group)}`, { name: proxy })
|
||||||
return {
|
|
||||||
alive: false,
|
|
||||||
extra: {},
|
|
||||||
history: [],
|
|
||||||
id: '',
|
|
||||||
name: '',
|
|
||||||
tfo: false,
|
|
||||||
type: 'Shadowsocks',
|
|
||||||
udp: false,
|
|
||||||
xudp: false
|
|
||||||
}
|
|
||||||
})) as IMihomoProxy
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const mihomoUpgradeGeo = async (): Promise<void> => {
|
export const mihomoUpgradeGeo = async (): Promise<void> => {
|
||||||
const instance = await getAxios()
|
const instance = await getAxios()
|
||||||
return instance.post('/configs/geo').catch((e) => {
|
return await instance.post('/configs/geo')
|
||||||
return e.response.data
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const mihomoProxyDelay = async (proxy: string, url?: string): Promise<IMihomoDelay> => {
|
export const mihomoProxyDelay = async (proxy: string, url?: string): Promise<IMihomoDelay> => {
|
||||||
const appConfig = getAppConfig()
|
const appConfig = await getAppConfig()
|
||||||
const { delayTestUrl, delayTestTimeout } = appConfig
|
const { delayTestUrl, delayTestTimeout } = appConfig
|
||||||
const instance = await getAxios()
|
const instance = await getAxios()
|
||||||
return (await instance
|
return await instance.get(`/proxies/${encodeURIComponent(proxy)}/delay`, {
|
||||||
.get(`/proxies/${encodeURIComponent(proxy)}/delay`, {
|
params: {
|
||||||
params: {
|
url: url || delayTestUrl || 'https://www.gstatic.com/generate_204',
|
||||||
url: url || delayTestUrl || 'https://www.gstatic.com/generate_204',
|
timeout: delayTestTimeout || 5000
|
||||||
timeout: delayTestTimeout || 5000
|
}
|
||||||
}
|
})
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
return e.response.data
|
|
||||||
})) as IMihomoDelay
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const mihomoGroupDelay = async (group: string, url?: string): Promise<IMihomoGroupDelay> => {
|
export const mihomoGroupDelay = async (group: string, url?: string): Promise<IMihomoGroupDelay> => {
|
||||||
const appConfig = getAppConfig()
|
const appConfig = await getAppConfig()
|
||||||
const { delayTestUrl, delayTestTimeout } = appConfig
|
const { delayTestUrl, delayTestTimeout } = appConfig
|
||||||
const instance = await getAxios()
|
const instance = await getAxios()
|
||||||
return (await instance
|
return await instance.get(`/group/${encodeURIComponent(group)}/delay`, {
|
||||||
.get(`/group/${encodeURIComponent(group)}/delay`, {
|
params: {
|
||||||
params: {
|
url: url || delayTestUrl || 'https://www.gstatic.com/generate_204',
|
||||||
url: url || delayTestUrl || 'https://www.gstatic.com/generate_204',
|
timeout: delayTestTimeout || 5000
|
||||||
timeout: delayTestTimeout || 5000
|
}
|
||||||
}
|
})
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
return e.response.data
|
|
||||||
})) as IMihomoGroupDelay
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const startMihomoTraffic = (): void => {
|
export const startMihomoTraffic = async (): Promise<void> => {
|
||||||
mihomoTraffic()
|
await mihomoTraffic()
|
||||||
}
|
}
|
||||||
|
|
||||||
export const stopMihomoTraffic = (): void => {
|
export const stopMihomoTraffic = (): void => {
|
||||||
@ -170,9 +159,10 @@ export const stopMihomoTraffic = (): void => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mihomoTraffic = (): void => {
|
const mihomoTraffic = async (): Promise<void> => {
|
||||||
let server = getControledMihomoConfig()['external-controller']
|
const controledMihomoConfig = await getControledMihomoConfig()
|
||||||
const secret = getControledMihomoConfig().secret ?? ''
|
let server = controledMihomoConfig['external-controller']
|
||||||
|
const secret = controledMihomoConfig.secret ?? ''
|
||||||
if (server?.startsWith(':')) server = `127.0.0.1${server}`
|
if (server?.startsWith(':')) server = `127.0.0.1${server}`
|
||||||
stopMihomoTraffic()
|
stopMihomoTraffic()
|
||||||
|
|
||||||
@ -181,7 +171,7 @@ const mihomoTraffic = (): void => {
|
|||||||
mihomoTrafficWs.onmessage = (e): void => {
|
mihomoTrafficWs.onmessage = (e): void => {
|
||||||
const data = e.data as string
|
const data = e.data as string
|
||||||
trafficRetry = 10
|
trafficRetry = 10
|
||||||
window?.webContents.send('mihomoTraffic', JSON.parse(data) as IMihomoTrafficInfo)
|
mainWindow?.webContents.send('mihomoTraffic', JSON.parse(data) as IMihomoTrafficInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
mihomoTrafficWs.onclose = (): void => {
|
mihomoTrafficWs.onclose = (): void => {
|
||||||
@ -199,8 +189,8 @@ const mihomoTraffic = (): void => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const startMihomoMemory = (): void => {
|
export const startMihomoMemory = async (): Promise<void> => {
|
||||||
mihomoMemory()
|
await mihomoMemory()
|
||||||
}
|
}
|
||||||
|
|
||||||
export const stopMihomoMemory = (): void => {
|
export const stopMihomoMemory = (): void => {
|
||||||
@ -213,9 +203,10 @@ export const stopMihomoMemory = (): void => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mihomoMemory = (): void => {
|
const mihomoMemory = async (): Promise<void> => {
|
||||||
let server = getControledMihomoConfig()['external-controller']
|
const controledMihomoConfig = await getControledMihomoConfig()
|
||||||
const secret = getControledMihomoConfig().secret ?? ''
|
let server = controledMihomoConfig['external-controller']
|
||||||
|
const secret = controledMihomoConfig.secret ?? ''
|
||||||
if (server?.startsWith(':')) server = `127.0.0.1${server}`
|
if (server?.startsWith(':')) server = `127.0.0.1${server}`
|
||||||
stopMihomoMemory()
|
stopMihomoMemory()
|
||||||
|
|
||||||
@ -224,7 +215,7 @@ const mihomoMemory = (): void => {
|
|||||||
mihomoMemoryWs.onmessage = (e): void => {
|
mihomoMemoryWs.onmessage = (e): void => {
|
||||||
const data = e.data as string
|
const data = e.data as string
|
||||||
memoryRetry = 10
|
memoryRetry = 10
|
||||||
window?.webContents.send('mihomoMemory', JSON.parse(data) as IMihomoMemoryInfo)
|
mainWindow?.webContents.send('mihomoMemory', JSON.parse(data) as IMihomoMemoryInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
mihomoMemoryWs.onclose = (): void => {
|
mihomoMemoryWs.onclose = (): void => {
|
||||||
@ -242,8 +233,8 @@ const mihomoMemory = (): void => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const startMihomoLogs = (): void => {
|
export const startMihomoLogs = async (): Promise<void> => {
|
||||||
mihomoLogs()
|
await mihomoLogs()
|
||||||
}
|
}
|
||||||
|
|
||||||
export const stopMihomoLogs = (): void => {
|
export const stopMihomoLogs = (): void => {
|
||||||
@ -256,9 +247,10 @@ export const stopMihomoLogs = (): void => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mihomoLogs = (): void => {
|
const mihomoLogs = async (): Promise<void> => {
|
||||||
const { secret = '', 'log-level': level = 'info' } = getControledMihomoConfig()
|
const controledMihomoConfig = await getControledMihomoConfig()
|
||||||
let { 'external-controller': server } = getControledMihomoConfig()
|
const { secret = '', 'log-level': level = 'info' } = controledMihomoConfig
|
||||||
|
let { 'external-controller': server } = controledMihomoConfig
|
||||||
if (server?.startsWith(':')) server = `127.0.0.1${server}`
|
if (server?.startsWith(':')) server = `127.0.0.1${server}`
|
||||||
stopMihomoLogs()
|
stopMihomoLogs()
|
||||||
|
|
||||||
@ -269,7 +261,7 @@ const mihomoLogs = (): void => {
|
|||||||
mihomoLogsWs.onmessage = (e): void => {
|
mihomoLogsWs.onmessage = (e): void => {
|
||||||
const data = e.data as string
|
const data = e.data as string
|
||||||
logsRetry = 10
|
logsRetry = 10
|
||||||
window?.webContents.send('mihomoLogs', JSON.parse(data) as IMihomoLogInfo)
|
mainWindow?.webContents.send('mihomoLogs', JSON.parse(data) as IMihomoLogInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
mihomoLogsWs.onclose = (): void => {
|
mihomoLogsWs.onclose = (): void => {
|
||||||
@ -287,8 +279,8 @@ const mihomoLogs = (): void => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const startMihomoConnections = (): void => {
|
export const startMihomoConnections = async (): Promise<void> => {
|
||||||
mihomoConnections()
|
await mihomoConnections()
|
||||||
}
|
}
|
||||||
|
|
||||||
export const stopMihomoConnections = (): void => {
|
export const stopMihomoConnections = (): void => {
|
||||||
@ -301,9 +293,10 @@ export const stopMihomoConnections = (): void => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mihomoConnections = (): void => {
|
const mihomoConnections = async (): Promise<void> => {
|
||||||
let server = getControledMihomoConfig()['external-controller']
|
const controledMihomoConfig = await getControledMihomoConfig()
|
||||||
const secret = getControledMihomoConfig().secret ?? ''
|
let server = controledMihomoConfig['external-controller']
|
||||||
|
const secret = controledMihomoConfig.secret ?? ''
|
||||||
if (server?.startsWith(':')) server = `127.0.0.1${server}`
|
if (server?.startsWith(':')) server = `127.0.0.1${server}`
|
||||||
stopMihomoConnections()
|
stopMihomoConnections()
|
||||||
|
|
||||||
@ -314,7 +307,7 @@ const mihomoConnections = (): void => {
|
|||||||
mihomoConnectionsWs.onmessage = (e): void => {
|
mihomoConnectionsWs.onmessage = (e): void => {
|
||||||
const data = e.data as string
|
const data = e.data as string
|
||||||
connectionsRetry = 10
|
connectionsRetry = 10
|
||||||
window?.webContents.send('mihomoConnections', JSON.parse(data) as IMihomoConnectionsInfo)
|
mainWindow?.webContents.send('mihomoConnections', JSON.parse(data) as IMihomoConnectionsInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
mihomoConnectionsWs.onclose = (): void => {
|
mihomoConnectionsWs.onclose = (): void => {
|
||||||
|
|||||||
@ -1,42 +1,60 @@
|
|||||||
import { addProfileItem, getCurrentProfileItem, getProfileConfig, getProfileItem } from '../config'
|
import { addProfileItem, getCurrentProfileItem, getProfileConfig } from '../config'
|
||||||
|
|
||||||
const intervalPool: Record<string, NodeJS.Timeout> = {}
|
const intervalPool: Record<string, NodeJS.Timeout> = {}
|
||||||
|
|
||||||
export async function initProfileUpdater(): Promise<void> {
|
export async function initProfileUpdater(): Promise<void> {
|
||||||
const { items, current } = getProfileConfig()
|
const { items, current } = await getProfileConfig()
|
||||||
const currentItem = getCurrentProfileItem()
|
const currentItem = await getCurrentProfileItem()
|
||||||
for (const item of items.filter((i) => i.id !== current)) {
|
for (const item of items.filter((i) => i.id !== current)) {
|
||||||
if (item.type === 'remote' && item.interval) {
|
if (item.type === 'remote' && item.interval) {
|
||||||
await addProfileItem(item)
|
intervalPool[item.id] = setTimeout(
|
||||||
intervalPool[item.id] = setInterval(
|
|
||||||
async () => {
|
async () => {
|
||||||
await addProfileItem(item)
|
try {
|
||||||
|
await addProfileItem(item)
|
||||||
|
} catch (e) {
|
||||||
|
/* ignore */
|
||||||
|
}
|
||||||
},
|
},
|
||||||
item.interval * 60 * 1000
|
item.interval * 60 * 1000
|
||||||
)
|
)
|
||||||
|
try {
|
||||||
|
await addProfileItem(item)
|
||||||
|
} catch (e) {
|
||||||
|
/* ignore */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (currentItem.type === 'remote' && currentItem.interval) {
|
if (currentItem?.type === 'remote' && currentItem.interval) {
|
||||||
await addProfileItem(currentItem)
|
intervalPool[currentItem.id] = setTimeout(
|
||||||
intervalPool[currentItem.id] = setInterval(
|
|
||||||
async () => {
|
async () => {
|
||||||
await addProfileItem(currentItem)
|
try {
|
||||||
|
await addProfileItem(currentItem)
|
||||||
|
} catch (e) {
|
||||||
|
/* ignore */
|
||||||
|
}
|
||||||
},
|
},
|
||||||
currentItem.interval * 60 * 1000 + 10000 // +10s
|
currentItem.interval * 60 * 1000 + 10000 // +10s
|
||||||
)
|
)
|
||||||
|
try {
|
||||||
|
await addProfileItem(currentItem)
|
||||||
|
} catch (e) {
|
||||||
|
/* ignore */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addProfileUpdater(id: string): void {
|
export async function addProfileUpdater(item: IProfileItem): Promise<void> {
|
||||||
const item = getProfileItem(id)
|
|
||||||
|
|
||||||
if (item.type === 'remote' && item.interval) {
|
if (item.type === 'remote' && item.interval) {
|
||||||
if (intervalPool[id]) {
|
if (intervalPool[item.id]) {
|
||||||
clearInterval(intervalPool[id])
|
clearTimeout(intervalPool[item.id])
|
||||||
}
|
}
|
||||||
intervalPool[id] = setInterval(
|
intervalPool[item.id] = setTimeout(
|
||||||
async () => {
|
async () => {
|
||||||
await addProfileItem(item)
|
try {
|
||||||
|
await addProfileItem(item)
|
||||||
|
} catch (e) {
|
||||||
|
/* ignore */
|
||||||
|
}
|
||||||
},
|
},
|
||||||
item.interval * 60 * 1000
|
item.interval * 60 * 1000
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,98 +1,100 @@
|
|||||||
import {
|
import {
|
||||||
getAppConfig,
|
getAppConfig,
|
||||||
getControledMihomoConfig,
|
getControledMihomoConfig,
|
||||||
setAppConfig,
|
patchAppConfig,
|
||||||
setControledMihomoConfig
|
patchControledMihomoConfig
|
||||||
} from '../config'
|
} from '../config'
|
||||||
import icoIcon from '../../../resources/icon.ico?asset'
|
import icoIcon from '../../../resources/icon.ico?asset'
|
||||||
import pngIcon from '../../../resources/icon.png?asset'
|
import pngIcon from '../../../resources/icon.png?asset'
|
||||||
import { patchMihomoConfig } from './mihomoApi'
|
import { patchMihomoConfig } from './mihomoApi'
|
||||||
import { window } from '..'
|
import { mainWindow } from '..'
|
||||||
import { app, ipcMain, Menu, shell, Tray } from 'electron'
|
import { app, ipcMain, Menu, shell, Tray } from 'electron'
|
||||||
import { dataDir, logDir, mihomoCoreDir, mihomoWorkDir } from '../utils/dirs'
|
import { dataDir, logDir, mihomoCoreDir, mihomoWorkDir } from '../utils/dirs'
|
||||||
import { triggerSysProxy } from '../resolve/sysproxy'
|
import { triggerSysProxy } from '../resolve/sysproxy'
|
||||||
|
|
||||||
let tray: Tray | null = null
|
let tray: Tray | null = null
|
||||||
|
|
||||||
const buildContextMenu = (): Menu => {
|
const buildContextMenu = async (): Promise<Menu> => {
|
||||||
|
const { mode, tun } = await getControledMihomoConfig()
|
||||||
|
const { sysProxy } = await getAppConfig()
|
||||||
const contextMenu = [
|
const contextMenu = [
|
||||||
{
|
{
|
||||||
id: 'show',
|
id: 'show',
|
||||||
label: '显示窗口',
|
label: '显示窗口',
|
||||||
type: 'normal',
|
type: 'normal',
|
||||||
click: (): void => {
|
click: (): void => {
|
||||||
window?.show()
|
mainWindow?.show()
|
||||||
window?.focusOnWebView()
|
mainWindow?.focusOnWebView()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'rule',
|
id: 'rule',
|
||||||
label: '规则模式',
|
label: '规则模式',
|
||||||
type: 'radio',
|
type: 'radio',
|
||||||
checked: getControledMihomoConfig().mode === 'rule',
|
checked: mode === 'rule',
|
||||||
click: (): void => {
|
click: async (): Promise<void> => {
|
||||||
setControledMihomoConfig({ mode: 'rule' })
|
await patchControledMihomoConfig({ mode: 'rule' })
|
||||||
patchMihomoConfig({ mode: 'rule' })
|
await patchMihomoConfig({ mode: 'rule' })
|
||||||
window?.webContents.send('controledMihomoConfigUpdated')
|
mainWindow?.webContents.send('controledMihomoConfigUpdated')
|
||||||
updateTrayMenu()
|
await updateTrayMenu()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'global',
|
id: 'global',
|
||||||
label: '全局模式',
|
label: '全局模式',
|
||||||
type: 'radio',
|
type: 'radio',
|
||||||
checked: getControledMihomoConfig().mode === 'global',
|
checked: mode === 'global',
|
||||||
click: (): void => {
|
click: async (): Promise<void> => {
|
||||||
setControledMihomoConfig({ mode: 'global' })
|
await patchControledMihomoConfig({ mode: 'global' })
|
||||||
patchMihomoConfig({ mode: 'global' })
|
await patchMihomoConfig({ mode: 'global' })
|
||||||
window?.webContents.send('controledMihomoConfigUpdated')
|
mainWindow?.webContents.send('controledMihomoConfigUpdated')
|
||||||
updateTrayMenu()
|
await updateTrayMenu()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'direct',
|
id: 'direct',
|
||||||
label: '直连模式',
|
label: '直连模式',
|
||||||
type: 'radio',
|
type: 'radio',
|
||||||
checked: getControledMihomoConfig().mode === 'direct',
|
checked: mode === 'direct',
|
||||||
click: (): void => {
|
click: async (): Promise<void> => {
|
||||||
setControledMihomoConfig({ mode: 'direct' })
|
await patchControledMihomoConfig({ mode: 'direct' })
|
||||||
patchMihomoConfig({ mode: 'direct' })
|
await patchMihomoConfig({ mode: 'direct' })
|
||||||
window?.webContents.send('controledMihomoConfigUpdated')
|
mainWindow?.webContents.send('controledMihomoConfigUpdated')
|
||||||
updateTrayMenu()
|
await updateTrayMenu()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ type: 'separator' },
|
{ type: 'separator' },
|
||||||
{
|
{
|
||||||
type: 'checkbox',
|
type: 'checkbox',
|
||||||
label: '系统代理',
|
label: '系统代理',
|
||||||
checked: getAppConfig().sysProxy?.enable ?? false,
|
checked: sysProxy.enable,
|
||||||
click: (item): void => {
|
click: async (item): Promise<void> => {
|
||||||
const enable = item.checked
|
const enable = item.checked
|
||||||
try {
|
try {
|
||||||
|
await patchAppConfig({ sysProxy: { enable } })
|
||||||
triggerSysProxy(enable)
|
triggerSysProxy(enable)
|
||||||
setAppConfig({ sysProxy: { enable } })
|
|
||||||
window?.webContents.send('appConfigUpdated')
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setAppConfig({ sysProxy: { enable: !enable } })
|
await patchAppConfig({ sysProxy: { enable: !enable } })
|
||||||
} finally {
|
} finally {
|
||||||
updateTrayMenu()
|
mainWindow?.webContents.send('appConfigUpdated')
|
||||||
|
await updateTrayMenu()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'checkbox',
|
type: 'checkbox',
|
||||||
label: '虚拟网卡',
|
label: '虚拟网卡',
|
||||||
checked: getControledMihomoConfig().tun?.enable ?? false,
|
checked: tun?.enable ?? false,
|
||||||
click: (item): void => {
|
click: async (item): Promise<void> => {
|
||||||
const enable = item.checked
|
const enable = item.checked
|
||||||
if (enable) {
|
if (enable) {
|
||||||
setControledMihomoConfig({ tun: { enable }, dns: { enable: true } })
|
await patchControledMihomoConfig({ tun: { enable }, dns: { enable: true } })
|
||||||
} else {
|
} else {
|
||||||
setControledMihomoConfig({ tun: { enable } })
|
await patchControledMihomoConfig({ tun: { enable } })
|
||||||
}
|
}
|
||||||
patchMihomoConfig({ tun: { enable } })
|
await patchMihomoConfig({ tun: { enable } })
|
||||||
window?.webContents.send('controledMihomoConfigUpdated')
|
mainWindow?.webContents.send('controledMihomoConfigUpdated')
|
||||||
updateTrayMenu()
|
await updateTrayMenu()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ type: 'separator' },
|
{ type: 'separator' },
|
||||||
@ -137,19 +139,19 @@ const buildContextMenu = (): Menu => {
|
|||||||
return Menu.buildFromTemplate(contextMenu)
|
return Menu.buildFromTemplate(contextMenu)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createTray(): void {
|
export async function createTray(): Promise<void> {
|
||||||
if (process.platform === 'linux') {
|
if (process.platform === 'linux') {
|
||||||
tray = new Tray(pngIcon)
|
tray = new Tray(pngIcon)
|
||||||
} else {
|
} else {
|
||||||
tray = new Tray(icoIcon)
|
tray = new Tray(icoIcon)
|
||||||
}
|
}
|
||||||
const menu = buildContextMenu()
|
const menu = await buildContextMenu()
|
||||||
|
|
||||||
ipcMain.on('controledMihomoConfigUpdated', () => {
|
ipcMain.on('controledMihomoConfigUpdated', async () => {
|
||||||
updateTrayMenu()
|
await updateTrayMenu()
|
||||||
})
|
})
|
||||||
ipcMain.on('appConfigUpdated', () => {
|
ipcMain.on('appConfigUpdated', async () => {
|
||||||
updateTrayMenu()
|
await updateTrayMenu()
|
||||||
})
|
})
|
||||||
|
|
||||||
tray.setContextMenu(menu)
|
tray.setContextMenu(menu)
|
||||||
@ -157,11 +159,11 @@ export function createTray(): void {
|
|||||||
tray.setToolTip('Another Mihomo GUI.')
|
tray.setToolTip('Another Mihomo GUI.')
|
||||||
tray.setTitle('Mihomo Party')
|
tray.setTitle('Mihomo Party')
|
||||||
tray.addListener('click', () => {
|
tray.addListener('click', () => {
|
||||||
window?.isVisible() ? window?.hide() : window?.show()
|
mainWindow?.isVisible() ? mainWindow?.hide() : mainWindow?.show()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateTrayMenu(): void {
|
async function updateTrayMenu(): Promise<void> {
|
||||||
const menu = buildContextMenu()
|
const menu = await buildContextMenu()
|
||||||
tray?.setContextMenu(menu) // 更新菜单
|
tray?.setContextMenu(menu) // 更新菜单
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
|
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
|
||||||
import { registerIpcMainHandlers } from './utils/ipc'
|
import { registerIpcMainHandlers } from './utils/ipc'
|
||||||
import { app, shell, BrowserWindow, Menu } from 'electron'
|
import { app, shell, BrowserWindow, Menu, dialog } from 'electron'
|
||||||
import { stopCore, startCore } from './core/manager'
|
import { stopCore } from './core/manager'
|
||||||
import { triggerSysProxy } from './resolve/sysproxy'
|
import { triggerSysProxy } from './resolve/sysproxy'
|
||||||
import icon from '../../resources/icon.png?asset'
|
import icon from '../../resources/icon.png?asset'
|
||||||
import { createTray } from './core/tray'
|
import { createTray } from './core/tray'
|
||||||
@ -14,76 +14,78 @@ import {
|
|||||||
stopMihomoMemory,
|
stopMihomoMemory,
|
||||||
stopMihomoTraffic
|
stopMihomoTraffic
|
||||||
} from './core/mihomoApi'
|
} from './core/mihomoApi'
|
||||||
import { initProfileUpdater } from './core/profileUpdater'
|
|
||||||
|
|
||||||
export let window: BrowserWindow | null = null
|
export let mainWindow: BrowserWindow | null = null
|
||||||
|
|
||||||
const gotTheLock = app.requestSingleInstanceLock()
|
const gotTheLock = app.requestSingleInstanceLock()
|
||||||
|
|
||||||
if (!gotTheLock) {
|
if (!gotTheLock) {
|
||||||
app.quit()
|
app.quit()
|
||||||
} else {
|
|
||||||
init()
|
|
||||||
app.on('second-instance', (_event, commandline) => {
|
|
||||||
window?.show()
|
|
||||||
window?.focusOnWebView()
|
|
||||||
const url = commandline.pop()
|
|
||||||
if (url) {
|
|
||||||
handleDeepLink(url)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
app.on('open-url', (_event, url) => {
|
|
||||||
window?.show()
|
|
||||||
window?.focusOnWebView()
|
|
||||||
handleDeepLink(url)
|
|
||||||
})
|
|
||||||
// Quit when all windows are closed, except on macOS. There, it's common
|
|
||||||
// for applications and their menu bar to stay active until the user quits
|
|
||||||
// explicitly with Cmd + Q.
|
|
||||||
app.on('window-all-closed', () => {
|
|
||||||
if (process.platform !== 'darwin') {
|
|
||||||
app.quit()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
app.on('before-quit', () => {
|
|
||||||
stopCore()
|
|
||||||
triggerSysProxy(false)
|
|
||||||
app.exit()
|
|
||||||
})
|
|
||||||
|
|
||||||
// This method will be called when Electron has finished
|
|
||||||
// initialization and is ready to create browser windows.
|
|
||||||
// Some APIs can only be used after this event occurs.
|
|
||||||
app.whenReady().then(() => {
|
|
||||||
// Set app user model id for windows
|
|
||||||
electronApp.setAppUserModelId('party.mihomo.app')
|
|
||||||
startCore().then(() => {
|
|
||||||
setTimeout(async () => {
|
|
||||||
await initProfileUpdater()
|
|
||||||
}, 60000)
|
|
||||||
})
|
|
||||||
// Default open or close DevTools by F12 in development
|
|
||||||
// and ignore CommandOrControl + R in production.
|
|
||||||
// see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
|
|
||||||
app.on('browser-window-created', (_, window) => {
|
|
||||||
optimizer.watchWindowShortcuts(window)
|
|
||||||
})
|
|
||||||
registerIpcMainHandlers()
|
|
||||||
createWindow()
|
|
||||||
createTray()
|
|
||||||
app.on('activate', function () {
|
|
||||||
// On macOS it's common to re-create a window in the app when the
|
|
||||||
// dock icon is clicked and there are no other windows open.
|
|
||||||
if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
const initPromise = init()
|
||||||
|
|
||||||
function handleDeepLink(url: string): void {
|
app.on('second-instance', async (_event, commandline) => {
|
||||||
|
mainWindow?.show()
|
||||||
|
mainWindow?.focusOnWebView()
|
||||||
|
const url = commandline.pop()
|
||||||
|
if (url) {
|
||||||
|
await handleDeepLink(url)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
app.on('open-url', async (_event, url) => {
|
||||||
|
mainWindow?.show()
|
||||||
|
mainWindow?.focusOnWebView()
|
||||||
|
await handleDeepLink(url)
|
||||||
|
})
|
||||||
|
// Quit when all windows are closed, except on macOS. There, it's common
|
||||||
|
// for applications and their menu bar to stay active until the user quits
|
||||||
|
// explicitly with Cmd + Q.
|
||||||
|
app.on('window-all-closed', () => {
|
||||||
|
if (process.platform !== 'darwin') {
|
||||||
|
app.quit()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
app.on('before-quit', () => {
|
||||||
|
stopCore()
|
||||||
|
triggerSysProxy(false)
|
||||||
|
app.exit()
|
||||||
|
})
|
||||||
|
|
||||||
|
// This method will be called when Electron has finished
|
||||||
|
// initialization and is ready to create browser windows.
|
||||||
|
// Some APIs can only be used after this event occurs.
|
||||||
|
app.whenReady().then(async () => {
|
||||||
|
// Set app user model id for windows
|
||||||
|
electronApp.setAppUserModelId('party.mihomo.app')
|
||||||
|
try {
|
||||||
|
await initPromise
|
||||||
|
} catch (e) {
|
||||||
|
dialog.showErrorBox('应用初始化失败', `${e}`)
|
||||||
|
app.quit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default open or close DevTools by F12 in development
|
||||||
|
// and ignore CommandOrControl + R in production.
|
||||||
|
// see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
|
||||||
|
app.on('browser-window-created', (_, window) => {
|
||||||
|
optimizer.watchWindowShortcuts(window)
|
||||||
|
})
|
||||||
|
registerIpcMainHandlers()
|
||||||
|
createWindow()
|
||||||
|
await createTray()
|
||||||
|
app.on('activate', function () {
|
||||||
|
// On macOS it's common to re-create a window in the app when the
|
||||||
|
// dock icon is clicked and there are no other windows open.
|
||||||
|
if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
async function handleDeepLink(url: string): Promise<void> {
|
||||||
if (url.startsWith('clash://install-config')) {
|
if (url.startsWith('clash://install-config')) {
|
||||||
url = url.replace('clash://install-config/?url=', '').replace('clash://install-config?url=', '')
|
url = url.replace('clash://install-config/?url=', '').replace('clash://install-config?url=', '')
|
||||||
addProfileItem({
|
await addProfileItem({
|
||||||
type: 'remote',
|
type: 'remote',
|
||||||
name: 'Remote File',
|
name: 'Remote File',
|
||||||
url
|
url
|
||||||
@ -93,7 +95,7 @@ function handleDeepLink(url: string): void {
|
|||||||
url = url
|
url = url
|
||||||
.replace('mihomo://install-config/?url=', '')
|
.replace('mihomo://install-config/?url=', '')
|
||||||
.replace('mihomo://install-config?url=', '')
|
.replace('mihomo://install-config?url=', '')
|
||||||
addProfileItem({
|
await addProfileItem({
|
||||||
type: 'remote',
|
type: 'remote',
|
||||||
name: 'Remote File',
|
name: 'Remote File',
|
||||||
url
|
url
|
||||||
@ -104,7 +106,7 @@ function handleDeepLink(url: string): void {
|
|||||||
function createWindow(): void {
|
function createWindow(): void {
|
||||||
Menu.setApplicationMenu(null)
|
Menu.setApplicationMenu(null)
|
||||||
// Create the browser window.
|
// Create the browser window.
|
||||||
window = new BrowserWindow({
|
mainWindow = new BrowserWindow({
|
||||||
minWidth: 800,
|
minWidth: 800,
|
||||||
minHeight: 600,
|
minHeight: 600,
|
||||||
width: 800,
|
width: 800,
|
||||||
@ -118,31 +120,32 @@ function createWindow(): void {
|
|||||||
sandbox: false
|
sandbox: false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
window.on('ready-to-show', () => {
|
mainWindow.on('ready-to-show', async () => {
|
||||||
if (!getAppConfig().silentStart) {
|
const { silentStart } = await getAppConfig()
|
||||||
window?.show()
|
if (!silentStart) {
|
||||||
window?.focusOnWebView()
|
mainWindow?.show()
|
||||||
|
mainWindow?.focusOnWebView()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
window.on('resize', () => {
|
mainWindow.on('resize', () => {
|
||||||
window?.webContents.send('resize')
|
mainWindow?.webContents.send('resize')
|
||||||
})
|
})
|
||||||
|
|
||||||
window.on('show', () => {
|
mainWindow.on('show', () => {
|
||||||
startMihomoTraffic()
|
startMihomoTraffic()
|
||||||
startMihomoMemory()
|
startMihomoMemory()
|
||||||
})
|
})
|
||||||
|
|
||||||
window.on('close', (event) => {
|
mainWindow.on('close', (event) => {
|
||||||
stopMihomoTraffic()
|
stopMihomoTraffic()
|
||||||
stopMihomoMemory()
|
stopMihomoMemory()
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
window?.hide()
|
mainWindow?.hide()
|
||||||
window?.webContents.reload()
|
mainWindow?.webContents.reload()
|
||||||
})
|
})
|
||||||
|
|
||||||
window.webContents.setWindowOpenHandler((details) => {
|
mainWindow.webContents.setWindowOpenHandler((details) => {
|
||||||
shell.openExternal(details.url)
|
shell.openExternal(details.url)
|
||||||
return { action: 'deny' }
|
return { action: 'deny' }
|
||||||
})
|
})
|
||||||
@ -150,8 +153,8 @@ function createWindow(): void {
|
|||||||
// HMR for renderer base on electron-vite cli.
|
// HMR for renderer base on electron-vite cli.
|
||||||
// Load the remote URL for development or the local html file for production.
|
// Load the remote URL for development or the local html file for production.
|
||||||
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
|
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
|
||||||
window.loadURL(process.env['ELECTRON_RENDERER_URL'])
|
mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
|
||||||
} else {
|
} else {
|
||||||
window.loadFile(join(__dirname, '../renderer/index.html'))
|
mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,10 @@
|
|||||||
import { exec } from 'child_process'
|
import { exec } from 'child_process'
|
||||||
import { exePath } from '../utils/dirs'
|
import { dataDir, exePath, homeDir } from '../utils/dirs'
|
||||||
|
import { mkdir, readFile, rm, writeFile } from 'fs/promises'
|
||||||
|
import { existsSync } from 'fs'
|
||||||
import { app } from 'electron'
|
import { app } from 'electron'
|
||||||
import fs from 'fs'
|
import { promisify } from 'util'
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
const appName = 'mihomo-party'
|
const appName = 'mihomo-party'
|
||||||
|
|
||||||
@ -51,12 +54,13 @@ const taskXml = `
|
|||||||
|
|
||||||
export async function checkAutoRun(): Promise<boolean> {
|
export async function checkAutoRun(): Promise<boolean> {
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
const { stdout } = (await new Promise((resolve) => {
|
const execPromise = promisify(exec)
|
||||||
exec(`schtasks /query /tn "${appName}"`, (_err, stdout, stderr) => {
|
try {
|
||||||
resolve({ stdout, stderr })
|
const { stdout } = await execPromise(`schtasks /query /tn "${appName}"`)
|
||||||
})
|
return stdout.includes(appName)
|
||||||
})) as { stdout: string; stderr: string }
|
} catch (e) {
|
||||||
return stdout.includes(appName)
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
@ -64,16 +68,17 @@ export async function checkAutoRun(): Promise<boolean> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (process.platform === 'linux') {
|
if (process.platform === 'linux') {
|
||||||
return fs.existsSync(`${app.getPath('home')}/.config/autostart/${appName}.desktop`)
|
return existsSync(path.join(homeDir, '.config', 'autostart', `${appName}.desktop`))
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
export function enableAutoRun(): void {
|
export async function enableAutoRun(): Promise<void> {
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
const taskFilePath = `${app.getPath('userData')}\\${appName}.xml`
|
const execPromise = promisify(exec)
|
||||||
fs.writeFileSync(taskFilePath, taskXml)
|
const taskFilePath = path.join(dataDir, `${appName}.xml`)
|
||||||
exec(`schtasks /create /tn "${appName}" /xml "${taskFilePath}" /f`)
|
await writeFile(taskFilePath, taskXml)
|
||||||
|
await execPromise(`schtasks /create /tn "${appName}" /xml "${taskFilePath}" /f`)
|
||||||
}
|
}
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
app.setLoginItemSettings({
|
app.setLoginItemSettings({
|
||||||
@ -93,22 +98,23 @@ StartupWMClass=mihomo-party
|
|||||||
Comment=Mihomo Party
|
Comment=Mihomo Party
|
||||||
Categories=Utility;
|
Categories=Utility;
|
||||||
`
|
`
|
||||||
try {
|
|
||||||
if (fs.existsSync(`/usr/share/applications/${appName}.desktop`)) {
|
if (existsSync(`/usr/share/applications/${appName}.desktop`)) {
|
||||||
desktop = fs.readFileSync(`/usr/share/applications/${appName}.desktop`, 'utf8')
|
desktop = await readFile(`/usr/share/applications/${appName}.desktop`, 'utf8')
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e)
|
|
||||||
}
|
}
|
||||||
fs.mkdirSync(`${app.getPath('home')}/.config/autostart`, { recursive: true })
|
const autostartDir = path.join(homeDir, '.config', 'autostart')
|
||||||
const desktopFilePath = `${app.getPath('home')}/.config/autostart/${appName}.desktop`
|
if (!existsSync(autostartDir)) {
|
||||||
fs.writeFileSync(desktopFilePath, desktop)
|
await mkdir(autostartDir, { recursive: true })
|
||||||
|
}
|
||||||
|
const desktopFilePath = path.join(autostartDir, `${appName}.desktop`)
|
||||||
|
await writeFile(desktopFilePath, desktop)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function disableAutoRun(): void {
|
export async function disableAutoRun(): Promise<void> {
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
exec(`schtasks /delete /tn "${appName}" /f`)
|
const execPromise = promisify(exec)
|
||||||
|
await execPromise(`schtasks /delete /tn "${appName}" /f`)
|
||||||
}
|
}
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
app.setLoginItemSettings({
|
app.setLoginItemSettings({
|
||||||
@ -116,11 +122,7 @@ export function disableAutoRun(): void {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (process.platform === 'linux') {
|
if (process.platform === 'linux') {
|
||||||
const desktopFilePath = `${app.getPath('home')}/.config/autostart/${appName}.desktop`
|
const desktopFilePath = path.join(homeDir, '.config', 'autostart', `${appName}.desktop`)
|
||||||
try {
|
await rm(desktopFilePath)
|
||||||
fs.rmSync(desktopFilePath)
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,26 +4,23 @@ import { app } from 'electron'
|
|||||||
import { getControledMihomoConfig } from '../config'
|
import { getControledMihomoConfig } from '../config'
|
||||||
|
|
||||||
export async function checkUpdate(): Promise<string | undefined> {
|
export async function checkUpdate(): Promise<string | undefined> {
|
||||||
try {
|
const res = await axios.get(
|
||||||
const res = await axios.get(
|
'https://github.com/pompurin404/mihomo-party/releases/latest/download/latest.yml',
|
||||||
'https://github.com/pompurin404/mihomo-party/releases/latest/download/latest.yml',
|
{
|
||||||
{
|
headers: { 'Content-Type': 'application/octet-stream' },
|
||||||
headers: { 'Content-Type': 'application/octet-stream' },
|
proxy: {
|
||||||
proxy: {
|
protocol: 'http',
|
||||||
protocol: 'http',
|
host: '127.0.0.1',
|
||||||
host: '127.0.0.1',
|
port: getControledMihomoConfig()['mixed-port'] || 7890
|
||||||
port: getControledMihomoConfig()['mixed-port'] || 7890
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
|
||||||
const latest = yaml.parse(res.data)
|
|
||||||
const remoteVersion = latest.version
|
|
||||||
const currentVersion = app.getVersion()
|
|
||||||
if (remoteVersion !== currentVersion) {
|
|
||||||
return remoteVersion
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
)
|
||||||
console.error(e)
|
const latest = yaml.parse(res.data) as { version: string }
|
||||||
|
const remoteVersion = latest.version
|
||||||
|
const currentVersion = app.getVersion()
|
||||||
|
if (remoteVersion !== currentVersion) {
|
||||||
|
return remoteVersion
|
||||||
|
} else {
|
||||||
|
return undefined
|
||||||
}
|
}
|
||||||
return undefined
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,10 +9,10 @@ import { mihomoWorkConfigPath } from '../utils/dirs'
|
|||||||
import yaml from 'yaml'
|
import yaml from 'yaml'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
|
|
||||||
export function generateProfile(): void {
|
export async function generateProfile(): Promise<void> {
|
||||||
const current = getProfileConfig().current
|
const { current } = await getProfileConfig()
|
||||||
const currentProfile = overrideProfile(current, getProfile(current))
|
const currentProfile = await overrideProfile(current, await getProfile(current))
|
||||||
const controledMihomoConfig = getControledMihomoConfig()
|
const controledMihomoConfig = await getControledMihomoConfig()
|
||||||
const { tun: profileTun = {} } = currentProfile
|
const { tun: profileTun = {} } = currentProfile
|
||||||
const { tun: controledTun } = controledMihomoConfig
|
const { tun: controledTun } = controledMihomoConfig
|
||||||
const tun = Object.assign(profileTun, controledTun)
|
const tun = Object.assign(profileTun, controledTun)
|
||||||
@ -26,13 +26,24 @@ export function generateProfile(): void {
|
|||||||
profile.tun = tun
|
profile.tun = tun
|
||||||
profile.dns = dns
|
profile.dns = dns
|
||||||
profile.sniffer = sniffer
|
profile.sniffer = sniffer
|
||||||
fs.writeFileSync(mihomoWorkConfigPath(), yaml.stringify(profile))
|
return new Promise((resolve, reject) => {
|
||||||
|
fs.writeFile(mihomoWorkConfigPath(), yaml.stringify(profile), (err) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err)
|
||||||
|
} else {
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function overrideProfile(current: string | undefined, profile: IMihomoConfig): IMihomoConfig {
|
async function overrideProfile(
|
||||||
const overrideScriptList = getProfileItem(current).override || []
|
current: string | undefined,
|
||||||
for (const override of overrideScriptList) {
|
profile: IMihomoConfig
|
||||||
const script = getOverride(override)
|
): Promise<IMihomoConfig> {
|
||||||
|
const { override = [] } = (await getProfileItem(current)) || {}
|
||||||
|
for (const ov of override) {
|
||||||
|
const script = await getOverride(ov)
|
||||||
profile = runOverrideScript(profile, script)
|
profile = runOverrideScript(profile, script)
|
||||||
}
|
}
|
||||||
return profile
|
return profile
|
||||||
@ -42,9 +53,7 @@ function runOverrideScript(profile: IMihomoConfig, script: string): IMihomoConfi
|
|||||||
try {
|
try {
|
||||||
const func = eval(`${script} main`)
|
const func = eval(`${script} main`)
|
||||||
const newProfile = func(profile)
|
const newProfile = func(profile)
|
||||||
if (typeof newProfile !== 'object') {
|
if (typeof newProfile !== 'object') return profile
|
||||||
throw new Error('Override script must return an object')
|
|
||||||
}
|
|
||||||
return newProfile
|
return newProfile
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return profile
|
return profile
|
||||||
|
|||||||
@ -20,83 +20,98 @@ import {
|
|||||||
defaultProfileConfig
|
defaultProfileConfig
|
||||||
} from '../utils/template'
|
} from '../utils/template'
|
||||||
import yaml from 'yaml'
|
import yaml from 'yaml'
|
||||||
import fs from 'fs'
|
import { mkdir, writeFile, copyFile } from 'fs/promises'
|
||||||
|
import { existsSync } from 'fs'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { startPacServer } from './server'
|
import { startPacServer } from './server'
|
||||||
import { triggerSysProxy } from './sysproxy'
|
import { triggerSysProxy } from './sysproxy'
|
||||||
import { getAppConfig } from '../config'
|
import { getAppConfig } from '../config'
|
||||||
import { app } from 'electron'
|
import { app } from 'electron'
|
||||||
|
import { startCore } from '../core/manager'
|
||||||
|
import { initProfileUpdater } from '../core/profileUpdater'
|
||||||
|
|
||||||
function initDirs(): void {
|
async function initDirs(): Promise<void> {
|
||||||
if (!fs.existsSync(dataDir)) {
|
if (!existsSync(dataDir)) {
|
||||||
fs.mkdirSync(dataDir)
|
await mkdir(dataDir)
|
||||||
}
|
}
|
||||||
if (!fs.existsSync(profilesDir())) {
|
if (!existsSync(profilesDir())) {
|
||||||
fs.mkdirSync(profilesDir())
|
await mkdir(profilesDir())
|
||||||
}
|
}
|
||||||
if (!fs.existsSync(overrideDir())) {
|
if (!existsSync(overrideDir())) {
|
||||||
fs.mkdirSync(overrideDir())
|
await mkdir(overrideDir())
|
||||||
}
|
}
|
||||||
if (!fs.existsSync(mihomoWorkDir())) {
|
if (!existsSync(mihomoWorkDir())) {
|
||||||
fs.mkdirSync(mihomoWorkDir())
|
await mkdir(mihomoWorkDir())
|
||||||
}
|
}
|
||||||
if (!fs.existsSync(logDir())) {
|
if (!existsSync(logDir())) {
|
||||||
fs.mkdirSync(logDir())
|
await mkdir(logDir())
|
||||||
}
|
}
|
||||||
if (!fs.existsSync(mihomoTestDir())) {
|
if (!existsSync(mihomoTestDir())) {
|
||||||
fs.mkdirSync(mihomoTestDir())
|
await mkdir(mihomoTestDir())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function initConfig(): void {
|
async function initConfig(): Promise<void> {
|
||||||
if (!fs.existsSync(appConfigPath())) {
|
if (!existsSync(appConfigPath())) {
|
||||||
fs.writeFileSync(appConfigPath(), yaml.stringify(defaultConfig))
|
await writeFile(appConfigPath(), yaml.stringify(defaultConfig))
|
||||||
}
|
}
|
||||||
if (!fs.existsSync(profileConfigPath())) {
|
if (!existsSync(profileConfigPath())) {
|
||||||
fs.writeFileSync(profileConfigPath(), yaml.stringify(defaultProfileConfig))
|
await writeFile(profileConfigPath(), yaml.stringify(defaultProfileConfig))
|
||||||
}
|
}
|
||||||
if (!fs.existsSync(overrideConfigPath())) {
|
if (!existsSync(overrideConfigPath())) {
|
||||||
fs.writeFileSync(overrideConfigPath(), yaml.stringify(defaultOverrideConfig))
|
await writeFile(overrideConfigPath(), yaml.stringify(defaultOverrideConfig))
|
||||||
}
|
}
|
||||||
if (!fs.existsSync(profilePath('default'))) {
|
if (!existsSync(profilePath('default'))) {
|
||||||
fs.writeFileSync(profilePath('default'), yaml.stringify(defaultProfile))
|
await writeFile(profilePath('default'), yaml.stringify(defaultProfile))
|
||||||
}
|
}
|
||||||
if (!fs.existsSync(controledMihomoConfigPath())) {
|
if (!existsSync(controledMihomoConfigPath())) {
|
||||||
fs.writeFileSync(controledMihomoConfigPath(), yaml.stringify(defaultControledMihomoConfig))
|
await writeFile(controledMihomoConfigPath(), yaml.stringify(defaultControledMihomoConfig))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function initFiles(): void {
|
async function initFiles(): Promise<void> {
|
||||||
const fileList = ['country.mmdb', 'geoip.dat', 'geosite.dat', 'ASN.mmdb']
|
const copy = async (file: string): Promise<void> => {
|
||||||
for (const file of fileList) {
|
|
||||||
const targetPath = path.join(mihomoWorkDir(), file)
|
const targetPath = path.join(mihomoWorkDir(), file)
|
||||||
const testTargrtPath = path.join(mihomoTestDir(), file)
|
const testTargrtPath = path.join(mihomoTestDir(), file)
|
||||||
const sourcePath = path.join(resourcesFilesDir(), file)
|
const sourcePath = path.join(resourcesFilesDir(), file)
|
||||||
if (!fs.existsSync(targetPath) && fs.existsSync(sourcePath)) {
|
if (!existsSync(targetPath) && existsSync(sourcePath)) {
|
||||||
fs.copyFileSync(sourcePath, targetPath)
|
await copyFile(sourcePath, targetPath)
|
||||||
}
|
}
|
||||||
if (!fs.existsSync(testTargrtPath) && fs.existsSync(sourcePath)) {
|
if (!existsSync(testTargrtPath) && existsSync(sourcePath)) {
|
||||||
fs.copyFileSync(sourcePath, testTargrtPath)
|
await copyFile(sourcePath, testTargrtPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
await Promise.all([
|
||||||
|
copy('country.mmdb'),
|
||||||
|
copy('geoip.dat'),
|
||||||
|
copy('geosite.dat'),
|
||||||
|
copy('ASN.mmdb')
|
||||||
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
function initDeeplink(): void {
|
function initDeeplink(): void {
|
||||||
if (process.defaultApp) {
|
if (process.defaultApp) {
|
||||||
if (process.argv.length >= 2) {
|
if (process.argv.length >= 2) {
|
||||||
app.setAsDefaultProtocolClient('clash', process.execPath, [path.resolve(process.argv[1])])
|
app.setAsDefaultProtocolClient('clash', process.execPath, [path.resolve(process.argv[1])])
|
||||||
|
app.setAsDefaultProtocolClient('mihomo', process.execPath, [path.resolve(process.argv[1])])
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
app.setAsDefaultProtocolClient('clash')
|
app.setAsDefaultProtocolClient('clash')
|
||||||
|
app.setAsDefaultProtocolClient('mihomo')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function init(): void {
|
export async function init(): Promise<void> {
|
||||||
initDirs()
|
await initDirs()
|
||||||
initConfig()
|
await initConfig()
|
||||||
initFiles()
|
await initFiles()
|
||||||
initDeeplink()
|
await startPacServer()
|
||||||
startPacServer().then(() => {
|
const { sysProxy } = await getAppConfig()
|
||||||
triggerSysProxy(getAppConfig().sysProxy.enable)
|
await triggerSysProxy(sysProxy.enable)
|
||||||
|
startCore().then(() => {
|
||||||
|
setTimeout(async () => {
|
||||||
|
await initProfileUpdater()
|
||||||
|
}, 60000)
|
||||||
})
|
})
|
||||||
|
initDeeplink()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,11 +34,11 @@ function findAvailablePort(startPort: number): Promise<number> {
|
|||||||
export async function startPacServer(): Promise<void> {
|
export async function startPacServer(): Promise<void> {
|
||||||
pacPort = await findAvailablePort(10000)
|
pacPort = await findAvailablePort(10000)
|
||||||
const server = http
|
const server = http
|
||||||
.createServer((_req, res) => {
|
.createServer(async (_req, res) => {
|
||||||
const {
|
const {
|
||||||
sysProxy: { pacScript }
|
sysProxy: { pacScript }
|
||||||
} = getAppConfig()
|
} = await getAppConfig()
|
||||||
const { 'mixed-port': port = 7890 } = getControledMihomoConfig()
|
const { 'mixed-port': port = 7890 } = await getControledMihomoConfig()
|
||||||
let script = pacScript || defaultPacScript
|
let script = pacScript || defaultPacScript
|
||||||
script = script.replaceAll('%mixed-port%', port.toString())
|
script = script.replaceAll('%mixed-port%', port.toString())
|
||||||
res.writeHead(200, { 'Content-Type': 'application/x-ns-proxy-autoconfig' })
|
res.writeHead(200, { 'Content-Type': 'application/x-ns-proxy-autoconfig' })
|
||||||
|
|||||||
@ -42,19 +42,19 @@ if (process.platform === 'win32')
|
|||||||
'<local>'
|
'<local>'
|
||||||
]
|
]
|
||||||
|
|
||||||
export function triggerSysProxy(enable: boolean): void {
|
export async function triggerSysProxy(enable: boolean): Promise<void> {
|
||||||
if (enable) {
|
if (enable) {
|
||||||
disableSysProxy()
|
disableSysProxy()
|
||||||
enableSysProxy()
|
await enableSysProxy()
|
||||||
} else {
|
} else {
|
||||||
disableSysProxy()
|
disableSysProxy()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function enableSysProxy(): void {
|
export async function enableSysProxy(): Promise<void> {
|
||||||
const { sysProxy } = getAppConfig()
|
const { sysProxy } = await getAppConfig()
|
||||||
const { mode, host, bypass = defaultBypass } = sysProxy
|
const { mode, host, bypass = defaultBypass } = sysProxy
|
||||||
const { 'mixed-port': port = 7890 } = getControledMihomoConfig()
|
const { 'mixed-port': port = 7890 } = await getControledMihomoConfig()
|
||||||
|
|
||||||
switch (mode || 'manual') {
|
switch (mode || 'manual') {
|
||||||
case 'auto': {
|
case 'auto': {
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { app } from 'electron'
|
|||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
|
||||||
export const dataDir = app.getPath('userData')
|
export const dataDir = app.getPath('userData')
|
||||||
|
export const homeDir = app.getPath('home')
|
||||||
|
|
||||||
export function exePath(): string {
|
export function exePath(): string {
|
||||||
return app.getPath('exe')
|
return app.getPath('exe')
|
||||||
|
|||||||
@ -22,9 +22,9 @@ import {
|
|||||||
import { checkAutoRun, disableAutoRun, enableAutoRun } from '../resolve/autoRun'
|
import { checkAutoRun, disableAutoRun, enableAutoRun } from '../resolve/autoRun'
|
||||||
import {
|
import {
|
||||||
getAppConfig,
|
getAppConfig,
|
||||||
setAppConfig,
|
patchAppConfig,
|
||||||
getControledMihomoConfig,
|
getControledMihomoConfig,
|
||||||
setControledMihomoConfig,
|
patchControledMihomoConfig,
|
||||||
getProfileConfig,
|
getProfileConfig,
|
||||||
getCurrentProfileItem,
|
getCurrentProfileItem,
|
||||||
getProfileItem,
|
getProfileItem,
|
||||||
@ -48,68 +48,95 @@ import { isEncryptionAvailable, restartCore } from '../core/manager'
|
|||||||
import { triggerSysProxy } from '../resolve/sysproxy'
|
import { triggerSysProxy } from '../resolve/sysproxy'
|
||||||
import { checkUpdate } from '../resolve/autoUpdater'
|
import { checkUpdate } from '../resolve/autoUpdater'
|
||||||
import { exePath, mihomoCorePath, mihomoWorkConfigPath, resourcesDir } from './dirs'
|
import { exePath, mihomoCorePath, mihomoWorkConfigPath, resourcesDir } from './dirs'
|
||||||
import { execFile, execSync } from 'child_process'
|
import { exec, execFile } from 'child_process'
|
||||||
import yaml from 'yaml'
|
import yaml from 'yaml'
|
||||||
import fs from 'fs'
|
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
import { promisify } from 'util'
|
||||||
|
import { readFile } from 'fs/promises'
|
||||||
|
|
||||||
|
function ipcErrorWrapper<T>( // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
fn: (...args: any[]) => Promise<T> // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
): (...args: any[]) => Promise<T | { invokeError: unknown }> {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
return async (...args: any[]) => {
|
||||||
|
try {
|
||||||
|
return await fn(...args)
|
||||||
|
} catch (e) {
|
||||||
|
return { invokeError: `${e}` }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
export function registerIpcMainHandlers(): void {
|
export function registerIpcMainHandlers(): void {
|
||||||
ipcMain.handle('mihomoVersion', mihomoVersion)
|
ipcMain.handle('mihomoVersion', ipcErrorWrapper(mihomoVersion))
|
||||||
ipcMain.handle('mihomoCloseConnection', (_e, id) => mihomoCloseConnection(id))
|
ipcMain.handle('mihomoCloseConnection', (_e, id) => ipcErrorWrapper(mihomoCloseConnection)(id))
|
||||||
ipcMain.handle('mihomoCloseAllConnections', mihomoCloseAllConnections)
|
ipcMain.handle('mihomoCloseAllConnections', ipcErrorWrapper(mihomoCloseAllConnections))
|
||||||
ipcMain.handle('mihomoRules', mihomoRules)
|
ipcMain.handle('mihomoRules', ipcErrorWrapper(mihomoRules))
|
||||||
ipcMain.handle('mihomoProxies', mihomoProxies)
|
ipcMain.handle('mihomoProxies', ipcErrorWrapper(mihomoProxies))
|
||||||
ipcMain.handle('mihomoProxyProviders', () => mihomoProxyProviders())
|
ipcMain.handle('mihomoProxyProviders', ipcErrorWrapper(mihomoProxyProviders))
|
||||||
ipcMain.handle('mihomoUpdateProxyProviders', (_e, name) => mihomoUpdateProxyProviders(name))
|
ipcMain.handle('mihomoUpdateProxyProviders', (_e, name) =>
|
||||||
ipcMain.handle('mihomoRuleProviders', () => mihomoRuleProviders())
|
ipcErrorWrapper(mihomoUpdateProxyProviders)(name)
|
||||||
ipcMain.handle('mihomoUpdateRuleProviders', (_e, name) => mihomoUpdateRuleProviders(name))
|
)
|
||||||
ipcMain.handle('mihomoChangeProxy', (_e, group, proxy) => mihomoChangeProxy(group, proxy))
|
ipcMain.handle('mihomoRuleProviders', ipcErrorWrapper(mihomoRuleProviders))
|
||||||
ipcMain.handle('mihomoUpgradeGeo', mihomoUpgradeGeo)
|
ipcMain.handle('mihomoUpdateRuleProviders', (_e, name) =>
|
||||||
ipcMain.handle('mihomoProxyDelay', (_e, proxy, url) => mihomoProxyDelay(proxy, url))
|
ipcErrorWrapper(mihomoUpdateRuleProviders)(name)
|
||||||
ipcMain.handle('mihomoGroupDelay', (_e, group, url) => mihomoGroupDelay(group, url))
|
)
|
||||||
ipcMain.handle('startMihomoLogs', startMihomoLogs)
|
ipcMain.handle('mihomoChangeProxy', (_e, group, proxy) =>
|
||||||
|
ipcErrorWrapper(mihomoChangeProxy)(group, proxy)
|
||||||
|
)
|
||||||
|
ipcMain.handle('mihomoUpgradeGeo', ipcErrorWrapper(mihomoUpgradeGeo))
|
||||||
|
ipcMain.handle('mihomoProxyDelay', (_e, proxy, url) =>
|
||||||
|
ipcErrorWrapper(mihomoProxyDelay)(proxy, url)
|
||||||
|
)
|
||||||
|
ipcMain.handle('mihomoGroupDelay', (_e, group, url) =>
|
||||||
|
ipcErrorWrapper(mihomoGroupDelay)(group, url)
|
||||||
|
)
|
||||||
|
ipcMain.handle('startMihomoLogs', ipcErrorWrapper(startMihomoLogs))
|
||||||
ipcMain.handle('stopMihomoLogs', stopMihomoLogs)
|
ipcMain.handle('stopMihomoLogs', stopMihomoLogs)
|
||||||
ipcMain.handle('startMihomoConnections', () => startMihomoConnections())
|
ipcMain.handle('startMihomoConnections', ipcErrorWrapper(startMihomoConnections))
|
||||||
ipcMain.handle('stopMihomoConnections', () => stopMihomoConnections())
|
ipcMain.handle('stopMihomoConnections', stopMihomoConnections)
|
||||||
ipcMain.handle('patchMihomoConfig', (_e, patch) => patchMihomoConfig(patch))
|
ipcMain.handle('patchMihomoConfig', (_e, patch) => ipcErrorWrapper(patchMihomoConfig)(patch))
|
||||||
ipcMain.handle('checkAutoRun', checkAutoRun)
|
ipcMain.handle('checkAutoRun', ipcErrorWrapper(checkAutoRun))
|
||||||
ipcMain.handle('enableAutoRun', enableAutoRun)
|
ipcMain.handle('enableAutoRun', ipcErrorWrapper(enableAutoRun))
|
||||||
ipcMain.handle('disableAutoRun', disableAutoRun)
|
ipcMain.handle('disableAutoRun', ipcErrorWrapper(disableAutoRun))
|
||||||
ipcMain.handle('getAppConfig', (_e, force) => getAppConfig(force))
|
ipcMain.handle('getAppConfig', (_e, force) => ipcErrorWrapper(getAppConfig)(force))
|
||||||
ipcMain.handle('setAppConfig', (_e, config) => setAppConfig(config))
|
ipcMain.handle('patchAppConfig', (_e, config) => ipcErrorWrapper(patchAppConfig)(config))
|
||||||
ipcMain.handle('getControledMihomoConfig', (_e, force) => getControledMihomoConfig(force))
|
ipcMain.handle('getControledMihomoConfig', (_e, force) =>
|
||||||
ipcMain.handle('setControledMihomoConfig', (_e, config) => setControledMihomoConfig(config))
|
ipcErrorWrapper(getControledMihomoConfig)(force)
|
||||||
ipcMain.handle('getProfileConfig', (_e, force) => getProfileConfig(force))
|
)
|
||||||
ipcMain.handle('setProfileConfig', (_e, config) => setProfileConfig(config))
|
ipcMain.handle('patchControledMihomoConfig', (_e, config) =>
|
||||||
ipcMain.handle('getCurrentProfileItem', getCurrentProfileItem)
|
ipcErrorWrapper(patchControledMihomoConfig)(config)
|
||||||
ipcMain.handle('getProfileItem', (_e, id) => getProfileItem(id))
|
)
|
||||||
ipcMain.handle('getProfileStr', (_e, id) => getProfileStr(id))
|
ipcMain.handle('getProfileConfig', (_e, force) => ipcErrorWrapper(getProfileConfig)(force))
|
||||||
ipcMain.handle('setProfileStr', (_e, id, str) => setProfileStr(id, str))
|
ipcMain.handle('setProfileConfig', (_e, config) => ipcErrorWrapper(setProfileConfig)(config))
|
||||||
ipcMain.handle('updateProfileItem', (_e, item) => updateProfileItem(item))
|
ipcMain.handle('getCurrentProfileItem', ipcErrorWrapper(getCurrentProfileItem))
|
||||||
ipcMain.handle('changeCurrentProfile', (_e, id) => changeCurrentProfile(id))
|
ipcMain.handle('getProfileItem', (_e, id) => ipcErrorWrapper(getProfileItem)(id))
|
||||||
ipcMain.handle('addProfileItem', (_e, item) => addProfileItem(item))
|
ipcMain.handle('getProfileStr', (_e, id) => ipcErrorWrapper(getProfileStr)(id))
|
||||||
ipcMain.handle('removeProfileItem', (_e, id) => removeProfileItem(id))
|
ipcMain.handle('setProfileStr', (_e, id, str) => ipcErrorWrapper(setProfileStr)(id, str))
|
||||||
ipcMain.handle('getOverrideConfig', (_e, force) => getOverrideConfig(force))
|
ipcMain.handle('updateProfileItem', (_e, item) => ipcErrorWrapper(updateProfileItem)(item))
|
||||||
ipcMain.handle('setOverrideConfig', (_e, config) => setOverrideConfig(config))
|
ipcMain.handle('changeCurrentProfile', (_e, id) => ipcErrorWrapper(changeCurrentProfile)(id))
|
||||||
ipcMain.handle('getOverrideItem', (_e, id) => getOverrideItem(id))
|
ipcMain.handle('addProfileItem', (_e, item) => ipcErrorWrapper(addProfileItem)(item))
|
||||||
ipcMain.handle('addOverrideItem', (_e, item) => addOverrideItem(item))
|
ipcMain.handle('removeProfileItem', (_e, id) => ipcErrorWrapper(removeProfileItem)(id))
|
||||||
ipcMain.handle('removeOverrideItem', (_e, id) => removeOverrideItem(id))
|
ipcMain.handle('getOverrideConfig', (_e, force) => ipcErrorWrapper(getOverrideConfig)(force))
|
||||||
ipcMain.handle('updateOverrideItem', (_e, item) => updateOverrideItem(item))
|
ipcMain.handle('setOverrideConfig', (_e, config) => ipcErrorWrapper(setOverrideConfig)(config))
|
||||||
ipcMain.handle('getOverride', (_e, id) => getOverride(id))
|
ipcMain.handle('getOverrideItem', (_e, id) => ipcErrorWrapper(getOverrideItem)(id))
|
||||||
ipcMain.handle('setOverride', (_e, id, str) => setOverride(id, str))
|
ipcMain.handle('addOverrideItem', (_e, item) => ipcErrorWrapper(addOverrideItem)(item))
|
||||||
ipcMain.handle('restartCore', restartCore)
|
ipcMain.handle('removeOverrideItem', (_e, id) => ipcErrorWrapper(removeOverrideItem)(id))
|
||||||
ipcMain.handle('triggerSysProxy', (_e, enable) => triggerSysProxy(enable))
|
ipcMain.handle('updateOverrideItem', (_e, item) => ipcErrorWrapper(updateOverrideItem)(item))
|
||||||
|
ipcMain.handle('getOverride', (_e, id) => ipcErrorWrapper(getOverride)(id))
|
||||||
|
ipcMain.handle('setOverride', (_e, id, str) => ipcErrorWrapper(setOverride)(id, str))
|
||||||
|
ipcMain.handle('restartCore', ipcErrorWrapper(restartCore))
|
||||||
|
ipcMain.handle('triggerSysProxy', (_e, enable) => ipcErrorWrapper(triggerSysProxy)(enable))
|
||||||
ipcMain.handle('isEncryptionAvailable', isEncryptionAvailable)
|
ipcMain.handle('isEncryptionAvailable', isEncryptionAvailable)
|
||||||
ipcMain.handle('encryptString', (_e, str) => safeStorage.encryptString(str))
|
ipcMain.handle('encryptString', (_e, str) => safeStorage.encryptString(str))
|
||||||
ipcMain.handle('getFilePath', (_e, ext) => getFilePath(ext))
|
ipcMain.handle('getFilePath', (_e, ext) => getFilePath(ext))
|
||||||
ipcMain.handle('readTextFile', (_e, filePath) => readTextFile(filePath))
|
ipcMain.handle('readTextFile', (_e, filePath) => ipcErrorWrapper(readTextFile)(filePath))
|
||||||
ipcMain.handle('getRuntimeConfigStr', getRuntimeConfigStr)
|
ipcMain.handle('getRuntimeConfigStr', ipcErrorWrapper(getRuntimeConfigStr))
|
||||||
ipcMain.handle('getRuntimeConfig', getRuntimeConfig)
|
ipcMain.handle('getRuntimeConfig', ipcErrorWrapper(getRuntimeConfig))
|
||||||
ipcMain.handle('checkUpdate', () => checkUpdate())
|
ipcMain.handle('checkUpdate', ipcErrorWrapper(checkUpdate))
|
||||||
ipcMain.handle('getVersion', () => app.getVersion())
|
ipcMain.handle('getVersion', () => app.getVersion())
|
||||||
ipcMain.handle('platform', () => process.platform)
|
ipcMain.handle('platform', () => process.platform)
|
||||||
ipcMain.handle('openUWPTool', openUWPTool)
|
ipcMain.handle('openUWPTool', ipcErrorWrapper(openUWPTool))
|
||||||
ipcMain.handle('setupFirewall', setupFirewall)
|
ipcMain.handle('setupFirewall', ipcErrorWrapper(setupFirewall))
|
||||||
ipcMain.handle('quitApp', () => app.quit())
|
ipcMain.handle('quitApp', () => app.quit())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,51 +148,39 @@ function getFilePath(ext: string[]): string[] | undefined {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function readTextFile(filePath: string): string {
|
async function readTextFile(filePath: string): Promise<string> {
|
||||||
return fs.readFileSync(filePath, 'utf8')
|
return await readFile(filePath, 'utf8')
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRuntimeConfigStr(): string {
|
async function getRuntimeConfigStr(): Promise<string> {
|
||||||
return fs.readFileSync(mihomoWorkConfigPath(), 'utf8')
|
return readFile(mihomoWorkConfigPath(), 'utf8')
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRuntimeConfig(): IMihomoConfig {
|
async function getRuntimeConfig(): Promise<IMihomoConfig> {
|
||||||
return yaml.parse(getRuntimeConfigStr())
|
return yaml.parse(await getRuntimeConfigStr())
|
||||||
}
|
}
|
||||||
|
|
||||||
function openUWPTool(): void {
|
async function openUWPTool(): Promise<void> {
|
||||||
|
const execFilePromise = promisify(execFile)
|
||||||
const uwpToolPath = path.join(resourcesDir(), 'files', 'enableLoopback.exe')
|
const uwpToolPath = path.join(resourcesDir(), 'files', 'enableLoopback.exe')
|
||||||
const child = execFile(uwpToolPath)
|
await execFilePromise(uwpToolPath)
|
||||||
child.unref()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setupFirewall(): Promise<void> {
|
async function setupFirewall(): Promise<void> {
|
||||||
return new Promise((resolve, reject) => {
|
const execPromise = promisify(exec)
|
||||||
const removeCommand = `
|
const removeCommand = `
|
||||||
Remove-NetFirewallRule -DisplayName "mihomo" -ErrorAction SilentlyContinue
|
Remove-NetFirewallRule -DisplayName "mihomo" -ErrorAction SilentlyContinue
|
||||||
Remove-NetFirewallRule -DisplayName "mihomo-alpha" -ErrorAction SilentlyContinue
|
Remove-NetFirewallRule -DisplayName "mihomo-alpha" -ErrorAction SilentlyContinue
|
||||||
Remove-NetFirewallRule -DisplayName "Mihomo Party" -ErrorAction SilentlyContinue
|
Remove-NetFirewallRule -DisplayName "Mihomo Party" -ErrorAction SilentlyContinue
|
||||||
`
|
`
|
||||||
const createCommand = `
|
const createCommand = `
|
||||||
New-NetFirewallRule -DisplayName "mihomo" -Direction Inbound -Action Allow -Program "${mihomoCorePath('mihomo')}" -Enabled True -Profile Any -ErrorAction SilentlyContinue
|
New-NetFirewallRule -DisplayName "mihomo" -Direction Inbound -Action Allow -Program "${mihomoCorePath('mihomo')}" -Enabled True -Profile Any -ErrorAction SilentlyContinue
|
||||||
New-NetFirewallRule -DisplayName "mihomo-alpha" -Direction Inbound -Action Allow -Program "${mihomoCorePath('mihomo-alpha')}" -Enabled True -Profile Any -ErrorAction SilentlyContinue
|
New-NetFirewallRule -DisplayName "mihomo-alpha" -Direction Inbound -Action Allow -Program "${mihomoCorePath('mihomo-alpha')}" -Enabled True -Profile Any -ErrorAction SilentlyContinue
|
||||||
New-NetFirewallRule -DisplayName "Mihomo Party" -Direction Inbound -Action Allow -Program "${exePath()}" -Enabled True -Profile Any -ErrorAction SilentlyContinue
|
New-NetFirewallRule -DisplayName "Mihomo Party" -Direction Inbound -Action Allow -Program "${exePath()}" -Enabled True -Profile Any -ErrorAction SilentlyContinue
|
||||||
`
|
`
|
||||||
|
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
try {
|
await execPromise(removeCommand, { shell: 'powershell' })
|
||||||
execSync(removeCommand, { shell: 'powershell' })
|
await execPromise(createCommand, { shell: 'powershell' })
|
||||||
} catch {
|
}
|
||||||
console.error('Remove-NetFirewallRule Failed')
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
execSync(createCommand, { shell: 'powershell' })
|
|
||||||
} catch (e) {
|
|
||||||
dialog.showErrorBox('防火墙设置失败', `${e}`)
|
|
||||||
reject(e)
|
|
||||||
console.error('New-NetFirewallRule Failed')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resolve()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -179,9 +179,13 @@ const ProfileItem: React.FC<Props> = (props) => {
|
|||||||
disabled={updating}
|
disabled={updating}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
setUpdating(true)
|
setUpdating(true)
|
||||||
addProfileItem(info).finally(() => {
|
addProfileItem(info)
|
||||||
setUpdating(false)
|
.catch((e) => {
|
||||||
})
|
alert(e)
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setUpdating(false)
|
||||||
|
})
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<IoMdRefresh
|
<IoMdRefresh
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import useSWR from 'swr'
|
import useSWR from 'swr'
|
||||||
import { getAppConfig, setAppConfig } from '@renderer/utils/ipc'
|
import { getAppConfig, patchAppConfig as patch } from '@renderer/utils/ipc'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
|
|
||||||
interface RetuenType {
|
interface RetuenType {
|
||||||
@ -12,7 +12,7 @@ export const useAppConfig = (listenUpdate = false): RetuenType => {
|
|||||||
const { data: appConfig, mutate: mutateAppConfig } = useSWR('getConfig', () => getAppConfig())
|
const { data: appConfig, mutate: mutateAppConfig } = useSWR('getConfig', () => getAppConfig())
|
||||||
|
|
||||||
const patchAppConfig = async (value: Partial<IAppConfig>): Promise<void> => {
|
const patchAppConfig = async (value: Partial<IAppConfig>): Promise<void> => {
|
||||||
await setAppConfig(value)
|
await patch(value)
|
||||||
mutateAppConfig()
|
mutateAppConfig()
|
||||||
window.electron.ipcRenderer.send('appConfigUpdated')
|
window.electron.ipcRenderer.send('appConfigUpdated')
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import useSWR from 'swr'
|
import useSWR from 'swr'
|
||||||
import { getControledMihomoConfig, setControledMihomoConfig } from '@renderer/utils/ipc'
|
import { getControledMihomoConfig, patchControledMihomoConfig as patch } from '@renderer/utils/ipc'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
|
|
||||||
interface RetuenType {
|
interface RetuenType {
|
||||||
@ -15,7 +15,7 @@ export const useControledMihomoConfig = (listenUpdate = false): RetuenType => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const patchControledMihomoConfig = async (value: Partial<IMihomoConfig>): Promise<void> => {
|
const patchControledMihomoConfig = async (value: Partial<IMihomoConfig>): Promise<void> => {
|
||||||
await setControledMihomoConfig(value)
|
await patch(value)
|
||||||
mutateControledMihomoConfig()
|
mutateControledMihomoConfig()
|
||||||
window.electron.ipcRenderer.send('controledMihomoConfigUpdated')
|
window.electron.ipcRenderer.send('controledMihomoConfigUpdated')
|
||||||
}
|
}
|
||||||
|
|||||||
@ -165,7 +165,11 @@ const Profiles: React.FC = () => {
|
|||||||
updateProfileItem={updateProfileItem}
|
updateProfileItem={updateProfileItem}
|
||||||
info={item}
|
info={item}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await changeCurrentProfile(item.id)
|
try {
|
||||||
|
await changeCurrentProfile(item.id)
|
||||||
|
} catch (e) {
|
||||||
|
alert(e)
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@ -1,223 +1,242 @@
|
|||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
function ipcErrorWrapper(response: any): any {
|
||||||
|
if (typeof response === 'object' && 'invokeError' in response) {
|
||||||
|
throw response.invokeError
|
||||||
|
} else {
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function mihomoVersion(): Promise<IMihomoVersion> {
|
export async function mihomoVersion(): Promise<IMihomoVersion> {
|
||||||
return await window.electron.ipcRenderer.invoke('mihomoVersion')
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('mihomoVersion'))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function mihomoCloseConnection(id: string): Promise<void> {
|
export async function mihomoCloseConnection(id: string): Promise<void> {
|
||||||
return await window.electron.ipcRenderer.invoke('mihomoCloseConnection', id)
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('mihomoCloseConnection', id))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function mihomoCloseAllConnections(): Promise<void> {
|
export async function mihomoCloseAllConnections(): Promise<void> {
|
||||||
return await window.electron.ipcRenderer.invoke('mihomoCloseAllConnections')
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('mihomoCloseAllConnections'))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function mihomoRules(): Promise<IMihomoRulesInfo> {
|
export async function mihomoRules(): Promise<IMihomoRulesInfo> {
|
||||||
return await window.electron.ipcRenderer.invoke('mihomoRules')
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('mihomoRules'))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function mihomoProxies(): Promise<IMihomoProxies> {
|
export async function mihomoProxies(): Promise<IMihomoProxies> {
|
||||||
return await window.electron.ipcRenderer.invoke('mihomoProxies')
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('mihomoProxies'))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function mihomoProxyProviders(): Promise<IMihomoProxyProviders> {
|
export async function mihomoProxyProviders(): Promise<IMihomoProxyProviders> {
|
||||||
return await window.electron.ipcRenderer.invoke('mihomoProxyProviders')
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('mihomoProxyProviders'))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function mihomoUpdateProxyProviders(name: string): Promise<void> {
|
export async function mihomoUpdateProxyProviders(name: string): Promise<void> {
|
||||||
return await window.electron.ipcRenderer.invoke('mihomoUpdateProxyProviders', name)
|
return ipcErrorWrapper(
|
||||||
|
await window.electron.ipcRenderer.invoke('mihomoUpdateProxyProviders', name)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function mihomoRuleProviders(): Promise<IMihomoRuleProviders> {
|
export async function mihomoRuleProviders(): Promise<IMihomoRuleProviders> {
|
||||||
return await window.electron.ipcRenderer.invoke('mihomoRuleProviders')
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('mihomoRuleProviders'))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function mihomoUpdateRuleProviders(name: string): Promise<void> {
|
export async function mihomoUpdateRuleProviders(name: string): Promise<void> {
|
||||||
return await window.electron.ipcRenderer.invoke('mihomoUpdateRuleProviders', name)
|
return ipcErrorWrapper(
|
||||||
|
await window.electron.ipcRenderer.invoke('mihomoUpdateRuleProviders', name)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function mihomoChangeProxy(group: string, proxy: string): Promise<IMihomoProxy> {
|
export async function mihomoChangeProxy(group: string, proxy: string): Promise<IMihomoProxy> {
|
||||||
return await window.electron.ipcRenderer.invoke('mihomoChangeProxy', group, proxy)
|
return ipcErrorWrapper(
|
||||||
|
await window.electron.ipcRenderer.invoke('mihomoChangeProxy', group, proxy)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function mihomoUpgradeGeo(): Promise<void> {
|
export async function mihomoUpgradeGeo(): Promise<void> {
|
||||||
return await window.electron.ipcRenderer.invoke('mihomoUpgradeGeo')
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('mihomoUpgradeGeo'))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function mihomoProxyDelay(proxy: string, url?: string): Promise<IMihomoDelay> {
|
export async function mihomoProxyDelay(proxy: string, url?: string): Promise<IMihomoDelay> {
|
||||||
return await window.electron.ipcRenderer.invoke('mihomoProxyDelay', proxy, url)
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('mihomoProxyDelay', proxy, url))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function mihomoGroupDelay(group: string, url?: string): Promise<IMihomoGroupDelay> {
|
export async function mihomoGroupDelay(group: string, url?: string): Promise<IMihomoGroupDelay> {
|
||||||
return await window.electron.ipcRenderer.invoke('mihomoGroupDelay', group, url)
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('mihomoGroupDelay', group, url))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function startMihomoLogs(): Promise<void> {
|
export async function startMihomoLogs(): Promise<void> {
|
||||||
return await window.electron.ipcRenderer.invoke('startMihomoLogs')
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('startMihomoLogs'))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function stopMihomoLogs(): Promise<void> {
|
export async function stopMihomoLogs(): Promise<void> {
|
||||||
return await window.electron.ipcRenderer.invoke('stopMihomoLogs')
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('stopMihomoLogs'))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function startMihomoConnections(): Promise<void> {
|
export async function startMihomoConnections(): Promise<void> {
|
||||||
return await window.electron.ipcRenderer.invoke('startMihomoConnections')
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('startMihomoConnections'))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function stopMihomoConnections(): Promise<void> {
|
export async function stopMihomoConnections(): Promise<void> {
|
||||||
return await window.electron.ipcRenderer.invoke('stopMihomoConnections')
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('stopMihomoConnections'))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function patchMihomoConfig(patch: Partial<IMihomoConfig>): Promise<void> {
|
export async function patchMihomoConfig(patch: Partial<IMihomoConfig>): Promise<void> {
|
||||||
return await window.electron.ipcRenderer.invoke('patchMihomoConfig', patch)
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('patchMihomoConfig', patch))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function checkAutoRun(): Promise<boolean> {
|
export async function checkAutoRun(): Promise<boolean> {
|
||||||
return await window.electron.ipcRenderer.invoke('checkAutoRun')
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('checkAutoRun'))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function enableAutoRun(): Promise<void> {
|
export async function enableAutoRun(): Promise<void> {
|
||||||
return await window.electron.ipcRenderer.invoke('enableAutoRun')
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('enableAutoRun'))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function disableAutoRun(): Promise<void> {
|
export async function disableAutoRun(): Promise<void> {
|
||||||
return await window.electron.ipcRenderer.invoke('disableAutoRun')
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('disableAutoRun'))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getAppConfig(force = false): Promise<IAppConfig> {
|
export async function getAppConfig(force = false): Promise<IAppConfig> {
|
||||||
return await window.electron.ipcRenderer.invoke('getAppConfig', force)
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getAppConfig', force))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setAppConfig(patch: Partial<IAppConfig>): Promise<void> {
|
export async function patchAppConfig(patch: Partial<IAppConfig>): Promise<void> {
|
||||||
return await window.electron.ipcRenderer.invoke('setAppConfig', patch)
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('patchAppConfig', patch))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getControledMihomoConfig(force = false): Promise<Partial<IMihomoConfig>> {
|
export async function getControledMihomoConfig(force = false): Promise<Partial<IMihomoConfig>> {
|
||||||
return await window.electron.ipcRenderer.invoke('getControledMihomoConfig', force)
|
return ipcErrorWrapper(
|
||||||
|
await window.electron.ipcRenderer.invoke('getControledMihomoConfig', force)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setControledMihomoConfig(patch: Partial<IMihomoConfig>): Promise<void> {
|
export async function patchControledMihomoConfig(patch: Partial<IMihomoConfig>): Promise<void> {
|
||||||
return await window.electron.ipcRenderer.invoke('setControledMihomoConfig', patch)
|
return ipcErrorWrapper(
|
||||||
|
await window.electron.ipcRenderer.invoke('patchControledMihomoConfig', patch)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getProfileConfig(force = false): Promise<IProfileConfig> {
|
export async function getProfileConfig(force = false): Promise<IProfileConfig> {
|
||||||
return await window.electron.ipcRenderer.invoke('getProfileConfig', force)
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getProfileConfig', force))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setProfileConfig(config: IProfileConfig): Promise<void> {
|
export async function setProfileConfig(config: IProfileConfig): Promise<void> {
|
||||||
return await window.electron.ipcRenderer.invoke('setProfileConfig', config)
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('setProfileConfig', config))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getCurrentProfileItem(): Promise<IProfileItem> {
|
export async function getCurrentProfileItem(): Promise<IProfileItem> {
|
||||||
return await window.electron.ipcRenderer.invoke('getCurrentProfileItem')
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getCurrentProfileItem'))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getProfileItem(id: string | undefined): Promise<IProfileItem> {
|
export async function getProfileItem(id: string | undefined): Promise<IProfileItem> {
|
||||||
return await window.electron.ipcRenderer.invoke('getProfileItem', id)
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getProfileItem', id))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function changeCurrentProfile(id: string): Promise<void> {
|
export async function changeCurrentProfile(id: string): Promise<void> {
|
||||||
return await window.electron.ipcRenderer.invoke('changeCurrentProfile', id)
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('changeCurrentProfile', id))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function addProfileItem(item: Partial<IProfileItem>): Promise<void> {
|
export async function addProfileItem(item: Partial<IProfileItem>): Promise<void> {
|
||||||
return await window.electron.ipcRenderer.invoke('addProfileItem', item)
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('addProfileItem', item))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function removeProfileItem(id: string): Promise<void> {
|
export async function removeProfileItem(id: string): Promise<void> {
|
||||||
return await window.electron.ipcRenderer.invoke('removeProfileItem', id)
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('removeProfileItem', id))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateProfileItem(item: IProfileItem): Promise<void> {
|
export async function updateProfileItem(item: IProfileItem): Promise<void> {
|
||||||
return await window.electron.ipcRenderer.invoke('updateProfileItem', item)
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('updateProfileItem', item))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getProfileStr(id: string): Promise<string> {
|
export async function getProfileStr(id: string): Promise<string> {
|
||||||
return await window.electron.ipcRenderer.invoke('getProfileStr', id)
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getProfileStr', id))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setProfileStr(id: string, str: string): Promise<void> {
|
export async function setProfileStr(id: string, str: string): Promise<void> {
|
||||||
return await window.electron.ipcRenderer.invoke('setProfileStr', id, str)
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('setProfileStr', id, str))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getOverrideConfig(force = false): Promise<IOverrideConfig> {
|
export async function getOverrideConfig(force = false): Promise<IOverrideConfig> {
|
||||||
return await window.electron.ipcRenderer.invoke('getOverrideConfig', force)
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getOverrideConfig', force))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setOverrideConfig(config: IOverrideConfig): Promise<void> {
|
export async function setOverrideConfig(config: IOverrideConfig): Promise<void> {
|
||||||
return await window.electron.ipcRenderer.invoke('setOverrideConfig', config)
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('setOverrideConfig', config))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getOverrideItem(id: string): Promise<IOverrideItem | undefined> {
|
export async function getOverrideItem(id: string): Promise<IOverrideItem | undefined> {
|
||||||
return await window.electron.ipcRenderer.invoke('getOverrideItem', id)
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getOverrideItem', id))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function addOverrideItem(item: Partial<IOverrideItem>): Promise<void> {
|
export async function addOverrideItem(item: Partial<IOverrideItem>): Promise<void> {
|
||||||
return await window.electron.ipcRenderer.invoke('addOverrideItem', item)
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('addOverrideItem', item))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function removeOverrideItem(id: string): Promise<void> {
|
export async function removeOverrideItem(id: string): Promise<void> {
|
||||||
return await window.electron.ipcRenderer.invoke('removeOverrideItem', id)
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('removeOverrideItem', id))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateOverrideItem(item: IOverrideItem): Promise<void> {
|
export async function updateOverrideItem(item: IOverrideItem): Promise<void> {
|
||||||
return await window.electron.ipcRenderer.invoke('updateOverrideItem', item)
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('updateOverrideItem', item))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getOverride(id: string): Promise<string> {
|
export async function getOverride(id: string): Promise<string> {
|
||||||
return await window.electron.ipcRenderer.invoke('getOverride', id)
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getOverride', id))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setOverride(id: string, str: string): Promise<void> {
|
export async function setOverride(id: string, str: string): Promise<void> {
|
||||||
return await window.electron.ipcRenderer.invoke('setOverride', id, str)
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('setOverride', id, str))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function restartCore(): Promise<void> {
|
export async function restartCore(): Promise<void> {
|
||||||
return await window.electron.ipcRenderer.invoke('restartCore')
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('restartCore'))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function triggerSysProxy(enable: boolean): Promise<void> {
|
export async function triggerSysProxy(enable: boolean): Promise<void> {
|
||||||
return await window.electron.ipcRenderer.invoke('triggerSysProxy', enable)
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('triggerSysProxy', enable))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function isEncryptionAvailable(): Promise<boolean> {
|
export async function isEncryptionAvailable(): Promise<boolean> {
|
||||||
return await window.electron.ipcRenderer.invoke('isEncryptionAvailable')
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('isEncryptionAvailable'))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function encryptString(str: string): Promise<Buffer> {
|
export async function encryptString(str: string): Promise<Buffer> {
|
||||||
return await window.electron.ipcRenderer.invoke('encryptString', str)
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('encryptString', str))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getFilePath(ext: string[]): Promise<string[] | undefined> {
|
export async function getFilePath(ext: string[]): Promise<string[] | undefined> {
|
||||||
return await window.electron.ipcRenderer.invoke('getFilePath', ext)
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getFilePath', ext))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function readTextFile(filePath: string): Promise<string> {
|
export async function readTextFile(filePath: string): Promise<string> {
|
||||||
return await window.electron.ipcRenderer.invoke('readTextFile', filePath)
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('readTextFile', filePath))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getRuntimeConfigStr(): Promise<string> {
|
export async function getRuntimeConfigStr(): Promise<string> {
|
||||||
return await window.electron.ipcRenderer.invoke('getRuntimeConfigStr')
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getRuntimeConfigStr'))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getRuntimeConfig(): Promise<IMihomoConfig> {
|
export async function getRuntimeConfig(): Promise<IMihomoConfig> {
|
||||||
return await window.electron.ipcRenderer.invoke('getRuntimeConfig')
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getRuntimeConfig'))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function checkUpdate(): Promise<string | undefined> {
|
export async function checkUpdate(): Promise<string | undefined> {
|
||||||
return await window.electron.ipcRenderer.invoke('checkUpdate')
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('checkUpdate'))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getVersion(): Promise<string> {
|
export async function getVersion(): Promise<string> {
|
||||||
return await window.electron.ipcRenderer.invoke('getVersion')
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getVersion'))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getPlatform(): Promise<NodeJS.Platform> {
|
export async function getPlatform(): Promise<NodeJS.Platform> {
|
||||||
return await window.electron.ipcRenderer.invoke('platform')
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('platform'))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setupFirewall(): Promise<void> {
|
export async function setupFirewall(): Promise<void> {
|
||||||
return await window.electron.ipcRenderer.invoke('setupFirewall')
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('setupFirewall'))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function quitApp(): Promise<void> {
|
export async function quitApp(): Promise<void> {
|
||||||
return await window.electron.ipcRenderer.invoke('quitApp')
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('quitApp'))
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user