mirror of
https://gh.catmak.name/https://github.com/mihomo-party-org/mihomo-party
synced 2025-12-28 05:30:29 +08:00
fix: windows first startup issue
This commit is contained in:
parent
d6f0d30f9a
commit
0a064bdbb8
@ -39,6 +39,7 @@ import os from 'os'
|
|||||||
import { createWriteStream, existsSync } from 'fs'
|
import { createWriteStream, existsSync } from 'fs'
|
||||||
import { uploadRuntimeConfig } from '../resolve/gistApi'
|
import { uploadRuntimeConfig } from '../resolve/gistApi'
|
||||||
import { startMonitor } from '../resolve/trafficMonitor'
|
import { startMonitor } from '../resolve/trafficMonitor'
|
||||||
|
import { safeShowErrorBox } from '../utils/init'
|
||||||
import i18next from '../../shared/i18n'
|
import i18next from '../../shared/i18n'
|
||||||
|
|
||||||
chokidar.watch(path.join(mihomoCoreDir(), 'meta-update'), {}).on('unlinkDir', async () => {
|
chokidar.watch(path.join(mihomoCoreDir(), 'meta-update'), {}).on('unlinkDir', async () => {
|
||||||
@ -46,7 +47,7 @@ chokidar.watch(path.join(mihomoCoreDir(), 'meta-update'), {}).on('unlinkDir', as
|
|||||||
await stopCore(true)
|
await stopCore(true)
|
||||||
await startCore()
|
await startCore()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
dialog.showErrorBox(i18next.t('mihomo.error.coreStartFailed'), `${e}`)
|
safeShowErrorBox('mihomo.error.coreStartFailed', `${e}`)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -228,7 +229,7 @@ export async function keepCoreAlive(): Promise<void> {
|
|||||||
await writeFile(path.join(dataDir(), 'core.pid'), child.pid.toString())
|
await writeFile(path.join(dataDir(), 'core.pid'), child.pid.toString())
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
dialog.showErrorBox(i18next.t('mihomo.error.coreStartFailed'), `${e}`)
|
safeShowErrorBox('mihomo.error.coreStartFailed', `${e}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -18,12 +18,31 @@ import { initProfileUpdater } from './core/profileUpdater'
|
|||||||
import { existsSync, writeFileSync } from 'fs'
|
import { existsSync, writeFileSync } from 'fs'
|
||||||
import { exePath, taskDir } from './utils/dirs'
|
import { exePath, taskDir } from './utils/dirs'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
import iconv from 'iconv-lite'
|
||||||
import { startMonitor } from './resolve/trafficMonitor'
|
import { startMonitor } from './resolve/trafficMonitor'
|
||||||
import { showFloatingWindow } from './resolve/floatingWindow'
|
import { showFloatingWindow } from './resolve/floatingWindow'
|
||||||
import iconv from 'iconv-lite'
|
|
||||||
import { initI18n } from '../shared/i18n'
|
import { initI18n } from '../shared/i18n'
|
||||||
import i18next from 'i18next'
|
import i18next from 'i18next'
|
||||||
|
|
||||||
|
// 错误处理
|
||||||
|
function showSafeErrorBox(titleKey: string, message: string): void {
|
||||||
|
let title: string
|
||||||
|
try {
|
||||||
|
title = i18next.t(titleKey)
|
||||||
|
if (!title || title === titleKey) throw new Error('Translation not ready')
|
||||||
|
} catch {
|
||||||
|
const isZh = app.getLocale().startsWith('zh')
|
||||||
|
const fallbacks: Record<string, { zh: string; en: string }> = {
|
||||||
|
'common.error.initFailed': { zh: '应用初始化失败', en: 'Application initialization failed' },
|
||||||
|
'mihomo.error.coreStartFailed': { zh: '内核启动出错', en: 'Core start failed' },
|
||||||
|
'profiles.error.importFailed': { zh: '配置导入失败', en: 'Profile import failed' },
|
||||||
|
'common.error.adminRequired': { zh: '需要管理员权限', en: 'Administrator privileges required' }
|
||||||
|
}
|
||||||
|
title = fallbacks[titleKey] ? (isZh ? fallbacks[titleKey].zh : fallbacks[titleKey].en) : (isZh ? '错误' : 'Error')
|
||||||
|
}
|
||||||
|
dialog.showErrorBox(title, message)
|
||||||
|
}
|
||||||
|
|
||||||
async function fixUserDataPermissions(): Promise<void> {
|
async function fixUserDataPermissions(): Promise<void> {
|
||||||
if (process.platform !== 'darwin') return
|
if (process.platform !== 'darwin') return
|
||||||
|
|
||||||
@ -50,6 +69,7 @@ async function fixUserDataPermissions(): Promise<void> {
|
|||||||
let quitTimeout: NodeJS.Timeout | null = null
|
let quitTimeout: NodeJS.Timeout | null = null
|
||||||
export let mainWindow: BrowserWindow | null = null
|
export let mainWindow: BrowserWindow | null = null
|
||||||
|
|
||||||
|
// Windows 管理员权限检查(仅在生产模式下)
|
||||||
if (process.platform === 'win32' && !is.dev && !process.argv.includes('noadmin')) {
|
if (process.platform === 'win32' && !is.dev && !process.argv.includes('noadmin')) {
|
||||||
try {
|
try {
|
||||||
createElevateTask()
|
createElevateTask()
|
||||||
@ -74,10 +94,7 @@ if (process.platform === 'win32' && !is.dev && !process.argv.includes('noadmin')
|
|||||||
} catch {
|
} catch {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
dialog.showErrorBox(
|
showSafeErrorBox('common.error.adminRequired', `${createErrorStr}\n${eStr}`)
|
||||||
i18next.t('common.error.adminRequired'),
|
|
||||||
`${i18next.t('common.error.adminRequired')}\n${createErrorStr}\n${eStr}`
|
|
||||||
)
|
|
||||||
} finally {
|
} finally {
|
||||||
app.exit()
|
app.exit()
|
||||||
}
|
}
|
||||||
@ -171,6 +188,9 @@ app.whenReady().then(async () => {
|
|||||||
electronApp.setAppUserModelId('party.mihomo.app')
|
electronApp.setAppUserModelId('party.mihomo.app')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// 首先等待初始化完成,确保配置文件和目录都已创建
|
||||||
|
await initPromise
|
||||||
|
|
||||||
const appConfig = await getAppConfig()
|
const appConfig = await getAppConfig()
|
||||||
// 如果配置中没有语言设置,则使用系统语言
|
// 如果配置中没有语言设置,则使用系统语言
|
||||||
if (!appConfig.language) {
|
if (!appConfig.language) {
|
||||||
@ -179,9 +199,8 @@ app.whenReady().then(async () => {
|
|||||||
appConfig.language = systemLanguage
|
appConfig.language = systemLanguage
|
||||||
}
|
}
|
||||||
await initI18n({ lng: appConfig.language })
|
await initI18n({ lng: appConfig.language })
|
||||||
await initPromise
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
dialog.showErrorBox(i18next.t('common.error.initFailed'), `${e}`)
|
showSafeErrorBox('common.error.initFailed', `${e}`)
|
||||||
app.quit()
|
app.quit()
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@ -190,7 +209,7 @@ app.whenReady().then(async () => {
|
|||||||
await initProfileUpdater()
|
await initProfileUpdater()
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
dialog.showErrorBox(i18next.t('mihomo.error.coreStartFailed'), `${e}`)
|
showSafeErrorBox('mihomo.error.coreStartFailed', `${e}`)
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await startMonitor()
|
await startMonitor()
|
||||||
@ -242,7 +261,7 @@ async function handleDeepLink(url: string): Promise<void> {
|
|||||||
new Notification({ title: i18next.t('profiles.notification.importSuccess') }).show()
|
new Notification({ title: i18next.t('profiles.notification.importSuccess') }).show()
|
||||||
break
|
break
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
dialog.showErrorBox(i18next.t('profiles.error.importFailed'), `${url}\n${e}`)
|
showSafeErrorBox('profiles.error.importFailed', `${url}\n${e}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,8 +39,25 @@ import {
|
|||||||
patchAppConfig,
|
patchAppConfig,
|
||||||
patchControledMihomoConfig
|
patchControledMihomoConfig
|
||||||
} from '../config'
|
} from '../config'
|
||||||
import { app } from 'electron'
|
import { app, dialog } from 'electron'
|
||||||
import { startSSIDCheck } from '../sys/ssid'
|
import { startSSIDCheck } from '../sys/ssid'
|
||||||
|
import i18next from '../../shared/i18n'
|
||||||
|
|
||||||
|
// 安全错误处理
|
||||||
|
export function safeShowErrorBox(titleKey: string, message: string): void {
|
||||||
|
let title: string
|
||||||
|
try {
|
||||||
|
title = i18next.t(titleKey)
|
||||||
|
if (!title || title === titleKey) throw new Error('Translation not ready')
|
||||||
|
} catch {
|
||||||
|
const isZh = process.env.LANG?.startsWith('zh') || process.env.LC_ALL?.startsWith('zh')
|
||||||
|
const fallbacks: Record<string, { zh: string; en: string }> = {
|
||||||
|
'mihomo.error.coreStartFailed': { zh: '内核启动出错', en: 'Core start failed' }
|
||||||
|
}
|
||||||
|
title = fallbacks[titleKey] ? (isZh ? fallbacks[titleKey].zh : fallbacks[titleKey].en) : (isZh ? '错误' : 'Error')
|
||||||
|
}
|
||||||
|
dialog.showErrorBox(title, message)
|
||||||
|
}
|
||||||
|
|
||||||
async function fixDataDirPermissions(): Promise<void> {
|
async function fixDataDirPermissions(): Promise<void> {
|
||||||
if (process.platform !== 'darwin') return
|
if (process.platform !== 'darwin') return
|
||||||
@ -68,47 +85,48 @@ async function fixDataDirPermissions(): Promise<void> {
|
|||||||
async function initDirs(): Promise<void> {
|
async function initDirs(): Promise<void> {
|
||||||
await fixDataDirPermissions()
|
await fixDataDirPermissions()
|
||||||
|
|
||||||
if (!existsSync(dataDir())) {
|
// 按依赖顺序创建目录
|
||||||
await mkdir(dataDir())
|
const dirsToCreate = [
|
||||||
}
|
dataDir(),
|
||||||
if (!existsSync(themesDir())) {
|
themesDir(),
|
||||||
await mkdir(themesDir())
|
profilesDir(),
|
||||||
}
|
overrideDir(),
|
||||||
if (!existsSync(profilesDir())) {
|
mihomoWorkDir(),
|
||||||
await mkdir(profilesDir())
|
logDir(),
|
||||||
}
|
mihomoTestDir(),
|
||||||
if (!existsSync(overrideDir())) {
|
subStoreDir()
|
||||||
await mkdir(overrideDir())
|
]
|
||||||
}
|
|
||||||
if (!existsSync(mihomoWorkDir())) {
|
for (const dir of dirsToCreate) {
|
||||||
await mkdir(mihomoWorkDir())
|
try {
|
||||||
}
|
if (!existsSync(dir)) {
|
||||||
if (!existsSync(logDir())) {
|
await mkdir(dir, { recursive: true })
|
||||||
await mkdir(logDir())
|
}
|
||||||
}
|
} catch (error) {
|
||||||
if (!existsSync(mihomoTestDir())) {
|
console.error(`Failed to create directory ${dir}:`, error)
|
||||||
await mkdir(mihomoTestDir())
|
throw new Error(`Failed to create directory ${dir}: ${error}`)
|
||||||
}
|
}
|
||||||
if (!existsSync(subStoreDir())) {
|
|
||||||
await mkdir(subStoreDir())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function initConfig(): Promise<void> {
|
async function initConfig(): Promise<void> {
|
||||||
if (!existsSync(appConfigPath())) {
|
const configs = [
|
||||||
await writeFile(appConfigPath(), yaml.stringify(defaultConfig))
|
{ path: appConfigPath(), content: defaultConfig, name: 'app config' },
|
||||||
}
|
{ path: profileConfigPath(), content: defaultProfileConfig, name: 'profile config' },
|
||||||
if (!existsSync(profileConfigPath())) {
|
{ path: overrideConfigPath(), content: defaultOverrideConfig, name: 'override config' },
|
||||||
await writeFile(profileConfigPath(), yaml.stringify(defaultProfileConfig))
|
{ path: profilePath('default'), content: defaultProfile, name: 'default profile' },
|
||||||
}
|
{ path: controledMihomoConfigPath(), content: defaultControledMihomoConfig, name: 'mihomo config' }
|
||||||
if (!existsSync(overrideConfigPath())) {
|
]
|
||||||
await writeFile(overrideConfigPath(), yaml.stringify(defaultOverrideConfig))
|
|
||||||
}
|
for (const config of configs) {
|
||||||
if (!existsSync(profilePath('default'))) {
|
try {
|
||||||
await writeFile(profilePath('default'), yaml.stringify(defaultProfile))
|
if (!existsSync(config.path)) {
|
||||||
}
|
await writeFile(config.path, yaml.stringify(config.content))
|
||||||
if (!existsSync(controledMihomoConfigPath())) {
|
}
|
||||||
await writeFile(controledMihomoConfigPath(), yaml.stringify(defaultControledMihomoConfig))
|
} catch (error) {
|
||||||
|
console.error(`Failed to create ${config.name} at ${config.path}:`, error)
|
||||||
|
throw new Error(`Failed to create ${config.name}: ${error}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,13 +135,30 @@ async function initFiles(): Promise<void> {
|
|||||||
const targetPath = path.join(mihomoWorkDir(), file)
|
const targetPath = path.join(mihomoWorkDir(), file)
|
||||||
const testTargetPath = path.join(mihomoTestDir(), file)
|
const testTargetPath = path.join(mihomoTestDir(), file)
|
||||||
const sourcePath = path.join(resourcesFilesDir(), file)
|
const sourcePath = path.join(resourcesFilesDir(), file)
|
||||||
if (!existsSync(targetPath) && existsSync(sourcePath)) {
|
|
||||||
await cp(sourcePath, targetPath, { recursive: true })
|
try {
|
||||||
}
|
if (!existsSync(targetPath) && existsSync(sourcePath)) {
|
||||||
if (!existsSync(testTargetPath) && existsSync(sourcePath)) {
|
await cp(sourcePath, targetPath, { recursive: true })
|
||||||
await cp(sourcePath, testTargetPath, { recursive: true })
|
}
|
||||||
|
if (!existsSync(testTargetPath) && existsSync(sourcePath)) {
|
||||||
|
await cp(sourcePath, testTargetPath, { recursive: true })
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to copy ${file}:`, error)
|
||||||
|
if (['country.mmdb', 'geoip.dat', 'geosite.dat'].includes(file)) {
|
||||||
|
throw new Error(`Failed to copy critical file ${file}: ${error}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 确保工作目录存在
|
||||||
|
if (!existsSync(mihomoWorkDir())) {
|
||||||
|
await mkdir(mihomoWorkDir(), { recursive: true })
|
||||||
|
}
|
||||||
|
if (!existsSync(mihomoTestDir())) {
|
||||||
|
await mkdir(mihomoTestDir(), { recursive: true })
|
||||||
|
}
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
copy('country.mmdb'),
|
copy('country.mmdb'),
|
||||||
copy('geoip.metadb'),
|
copy('geoip.metadb'),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user