mirror of
https://gh.catmak.name/https://github.com/mihomo-party-org/mihomo-party
synced 2026-02-10 19:50:28 +08:00
refactor: optimize init.ts with parallel execution and split migration
This commit is contained in:
parent
bff3aedf86
commit
0b65eb490f
@ -47,7 +47,6 @@ import { initLogger } from './logger'
|
|||||||
|
|
||||||
let isInitBasicCompleted = false
|
let isInitBasicCompleted = false
|
||||||
|
|
||||||
// 安全错误处理
|
|
||||||
export function safeShowErrorBox(titleKey: string, message: string): void {
|
export function safeShowErrorBox(titleKey: string, message: string): void {
|
||||||
let title: string
|
let title: string
|
||||||
try {
|
try {
|
||||||
@ -84,12 +83,9 @@ async function fixDataDirPermissions(): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 比较修改 geodata 文件修改时间
|
|
||||||
async function isSourceNewer(sourcePath: string, targetPath: string): Promise<boolean> {
|
async function isSourceNewer(sourcePath: string, targetPath: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const sourceStats = await stat(sourcePath)
|
const [sourceStats, targetStats] = await Promise.all([stat(sourcePath), stat(targetPath)])
|
||||||
const targetStats = await stat(targetPath)
|
|
||||||
|
|
||||||
return sourceStats.mtime > targetStats.mtime
|
return sourceStats.mtime > targetStats.mtime
|
||||||
} catch {
|
} catch {
|
||||||
return true
|
return true
|
||||||
@ -99,7 +95,6 @@ async function isSourceNewer(sourcePath: string, targetPath: string): Promise<bo
|
|||||||
async function initDirs(): Promise<void> {
|
async function initDirs(): Promise<void> {
|
||||||
await fixDataDirPermissions()
|
await fixDataDirPermissions()
|
||||||
|
|
||||||
// 按依赖顺序创建目录
|
|
||||||
const dirsToCreate = [
|
const dirsToCreate = [
|
||||||
dataDir(),
|
dataDir(),
|
||||||
themesDir(),
|
themesDir(),
|
||||||
@ -112,16 +107,13 @@ async function initDirs(): Promise<void> {
|
|||||||
subStoreDir()
|
subStoreDir()
|
||||||
]
|
]
|
||||||
|
|
||||||
for (const dir of dirsToCreate) {
|
await Promise.all(
|
||||||
try {
|
dirsToCreate.map(async (dir) => {
|
||||||
if (!existsSync(dir)) {
|
if (!existsSync(dir)) {
|
||||||
await mkdir(dir, { recursive: true })
|
await mkdir(dir, { recursive: true })
|
||||||
}
|
}
|
||||||
} catch (error) {
|
})
|
||||||
await initLogger.error(`Failed to create directory ${dir}`, error)
|
)
|
||||||
throw new Error(`Failed to create directory ${dir}: ${error}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function initConfig(): Promise<void> {
|
async function initConfig(): Promise<void> {
|
||||||
@ -137,19 +129,18 @@ async function initConfig(): Promise<void> {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
for (const config of configs) {
|
await Promise.all(
|
||||||
try {
|
configs.map(async (config) => {
|
||||||
if (!existsSync(config.path)) {
|
if (!existsSync(config.path)) {
|
||||||
await writeFile(config.path, stringify(config.content))
|
await writeFile(config.path, stringify(config.content))
|
||||||
}
|
}
|
||||||
} catch (error) {
|
})
|
||||||
await initLogger.error(`Failed to create ${config.name} at ${config.path}`, error)
|
)
|
||||||
throw new Error(`Failed to create ${config.name}: ${error}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function killOldMihomoProcesses(): Promise<void> {
|
async function killOldMihomoProcesses(): Promise<void> {
|
||||||
|
if (process.platform !== 'win32') return
|
||||||
|
|
||||||
const execPromise = promisify(exec)
|
const execPromise = promisify(exec)
|
||||||
try {
|
try {
|
||||||
const { stdout } = await execPromise(
|
const { stdout } = await execPromise(
|
||||||
@ -174,95 +165,84 @@ async function killOldMihomoProcesses(): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 等待进程完全退出
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 500))
|
await new Promise((resolve) => setTimeout(resolve, 500))
|
||||||
} catch {
|
} catch {
|
||||||
|
// 忽略错误
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function initFiles(): Promise<void> {
|
async function initFiles(): Promise<void> {
|
||||||
// 结束旧 mihomo 进程
|
await killOldMihomoProcesses()
|
||||||
if (process.platform === 'win32') {
|
|
||||||
await killOldMihomoProcesses()
|
|
||||||
}
|
|
||||||
|
|
||||||
const copy = async (file: string): Promise<void> => {
|
const copyFile = async (file: string): Promise<void> => {
|
||||||
const targetPath = path.join(mihomoWorkDir(), file)
|
|
||||||
const testTargetPath = path.join(mihomoTestDir(), file)
|
|
||||||
const sourcePath = path.join(resourcesFilesDir(), file)
|
const sourcePath = path.join(resourcesFilesDir(), file)
|
||||||
|
if (!existsSync(sourcePath)) return
|
||||||
|
|
||||||
try {
|
const targets = [
|
||||||
if (existsSync(sourcePath)) {
|
path.join(mihomoWorkDir(), file),
|
||||||
const shouldCopyToWork =
|
path.join(mihomoTestDir(), file)
|
||||||
!existsSync(targetPath) || (await isSourceNewer(sourcePath, targetPath))
|
]
|
||||||
if (shouldCopyToWork) {
|
|
||||||
|
await Promise.all(
|
||||||
|
targets.map(async (targetPath) => {
|
||||||
|
const shouldCopy = !existsSync(targetPath) || (await isSourceNewer(sourcePath, targetPath))
|
||||||
|
if (shouldCopy) {
|
||||||
await cp(sourcePath, targetPath, { recursive: true, force: true })
|
await cp(sourcePath, targetPath, { recursive: true, force: true })
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
if (existsSync(sourcePath)) {
|
)
|
||||||
const shouldCopyToTest =
|
}
|
||||||
!existsSync(testTargetPath) || (await isSourceNewer(sourcePath, testTargetPath))
|
|
||||||
if (shouldCopyToTest) {
|
const files = [
|
||||||
await cp(sourcePath, testTargetPath, { recursive: true, force: true })
|
'country.mmdb',
|
||||||
}
|
'geoip.metadb',
|
||||||
}
|
'geoip.dat',
|
||||||
} catch (error) {
|
'geosite.dat',
|
||||||
await initLogger.error(`Failed to copy ${file}`, error)
|
'ASN.mmdb',
|
||||||
if (['country.mmdb', 'geoip.dat', 'geosite.dat'].includes(file)) {
|
'sub-store.bundle.cjs',
|
||||||
throw new Error(`Failed to copy critical file ${file}: ${error}`)
|
'sub-store-frontend'
|
||||||
|
]
|
||||||
|
|
||||||
|
const criticalFiles = ['country.mmdb', 'geoip.dat', 'geosite.dat']
|
||||||
|
|
||||||
|
const results = await Promise.allSettled(files.map(copyFile))
|
||||||
|
|
||||||
|
for (let i = 0; i < results.length; i++) {
|
||||||
|
const result = results[i]
|
||||||
|
if (result.status === 'rejected') {
|
||||||
|
const file = files[i]
|
||||||
|
await initLogger.error(`Failed to copy ${file}`, result.reason)
|
||||||
|
if (criticalFiles.includes(file)) {
|
||||||
|
throw new Error(`Failed to copy critical file ${file}: ${result.reason}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确保工作目录存在
|
|
||||||
if (!existsSync(mihomoWorkDir())) {
|
|
||||||
await mkdir(mihomoWorkDir(), { recursive: true })
|
|
||||||
}
|
|
||||||
if (!existsSync(mihomoTestDir())) {
|
|
||||||
await mkdir(mihomoTestDir(), { recursive: true })
|
|
||||||
}
|
|
||||||
|
|
||||||
await Promise.all([
|
|
||||||
copy('country.mmdb'),
|
|
||||||
copy('geoip.metadb'),
|
|
||||||
copy('geoip.dat'),
|
|
||||||
copy('geosite.dat'),
|
|
||||||
copy('ASN.mmdb'),
|
|
||||||
copy('sub-store.bundle.cjs'),
|
|
||||||
copy('sub-store-frontend')
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function cleanup(): Promise<void> {
|
async function cleanup(): Promise<void> {
|
||||||
// update cache
|
const [dataFiles, logFiles] = await Promise.all([readdir(dataDir()), readdir(logDir())])
|
||||||
const files = await readdir(dataDir())
|
|
||||||
for (const file of files) {
|
// 清理更新缓存
|
||||||
if (file.endsWith('.exe') || file.endsWith('.pkg') || file.endsWith('.7z')) {
|
const cacheExtensions = ['.exe', '.pkg', '.7z']
|
||||||
try {
|
const cacheCleanup = dataFiles
|
||||||
await rm(path.join(dataDir(), file))
|
.filter((file) => cacheExtensions.some((ext) => file.endsWith(ext)))
|
||||||
} catch {
|
.map((file) => rm(path.join(dataDir(), file)).catch(() => {}))
|
||||||
// ignore
|
|
||||||
}
|
// 清理过期日志
|
||||||
}
|
|
||||||
}
|
|
||||||
// logs
|
|
||||||
const { maxLogDays = 7 } = await getAppConfig()
|
const { maxLogDays = 7 } = await getAppConfig()
|
||||||
const logs = await readdir(logDir())
|
const maxAge = maxLogDays * 24 * 60 * 60 * 1000
|
||||||
const datePattern = /^\d{4}-\d{2}-\d{2}/
|
const datePattern = /^\d{4}-\d{2}-\d{2}/
|
||||||
for (const log of logs) {
|
|
||||||
const match = log.match(datePattern)
|
const logCleanup = logFiles
|
||||||
if (!match) continue
|
.filter((log) => {
|
||||||
const date = new Date(match[0])
|
const match = log.match(datePattern)
|
||||||
if (isNaN(date.getTime())) continue
|
if (!match) return false
|
||||||
const diff = Date.now() - date.getTime()
|
const date = new Date(match[0])
|
||||||
if (diff > maxLogDays * 24 * 60 * 60 * 1000) {
|
return !isNaN(date.getTime()) && Date.now() - date.getTime() > maxAge
|
||||||
try {
|
})
|
||||||
await rm(path.join(logDir(), log))
|
.map((log) => rm(path.join(logDir(), log)).catch(() => {}))
|
||||||
} catch {
|
|
||||||
// ignore
|
await Promise.all([...cacheCleanup, ...logCleanup])
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function migrateSubStoreFiles(): Promise<void> {
|
async function migrateSubStoreFiles(): Promise<void> {
|
||||||
@ -278,111 +258,97 @@ async function migrateSubStoreFiles(): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function migration(): Promise<void> {
|
// 迁移:添加 substore 到侧边栏
|
||||||
const {
|
async function migrateSiderOrder(): Promise<void> {
|
||||||
siderOrder = [
|
const { siderOrder = [], useSubStore = true } = await getAppConfig()
|
||||||
'sysproxy',
|
|
||||||
'tun',
|
|
||||||
'profile',
|
|
||||||
'proxy',
|
|
||||||
'rule',
|
|
||||||
'resource',
|
|
||||||
'override',
|
|
||||||
'connection',
|
|
||||||
'mihomo',
|
|
||||||
'dns',
|
|
||||||
'sniff',
|
|
||||||
'log',
|
|
||||||
'substore'
|
|
||||||
],
|
|
||||||
appTheme = 'system',
|
|
||||||
envType = [process.platform === 'win32' ? 'powershell' : 'bash'],
|
|
||||||
useSubStore = true,
|
|
||||||
showFloatingWindow = false,
|
|
||||||
disableTray = false,
|
|
||||||
encryptedPassword
|
|
||||||
} = await getAppConfig()
|
|
||||||
const {
|
|
||||||
'external-controller-pipe': externalControllerPipe,
|
|
||||||
'external-controller-unix': externalControllerUnix,
|
|
||||||
'external-controller': externalController,
|
|
||||||
'skip-auth-prefixes': skipAuthPrefixes,
|
|
||||||
authentication,
|
|
||||||
'bind-address': bindAddress,
|
|
||||||
'lan-allowed-ips': lanAllowedIps,
|
|
||||||
'lan-disallowed-ips': lanDisallowedIps,
|
|
||||||
tun
|
|
||||||
} = await getControledMihomoConfig()
|
|
||||||
// add substore sider card
|
|
||||||
if (useSubStore && !siderOrder.includes('substore')) {
|
if (useSubStore && !siderOrder.includes('substore')) {
|
||||||
await patchAppConfig({ siderOrder: [...siderOrder, 'substore'] })
|
await patchAppConfig({ siderOrder: [...siderOrder, 'substore'] })
|
||||||
}
|
}
|
||||||
// add default skip auth prefix
|
}
|
||||||
if (!skipAuthPrefixes) {
|
|
||||||
await patchControledMihomoConfig({ 'skip-auth-prefixes': ['127.0.0.1/32', '::1/128'] })
|
// 迁移:修复 appTheme
|
||||||
} else if (skipAuthPrefixes.length >= 1 && skipAuthPrefixes[0] === '127.0.0.1/32') {
|
async function migrateAppTheme(): Promise<void> {
|
||||||
const filteredPrefixes = skipAuthPrefixes.filter((ip) => ip !== '::1/128')
|
const { appTheme = 'system' } = await getAppConfig()
|
||||||
const newPrefixes = [filteredPrefixes[0], '::1/128', ...filteredPrefixes.slice(1)]
|
|
||||||
if (JSON.stringify(newPrefixes) !== JSON.stringify(skipAuthPrefixes)) {
|
|
||||||
await patchControledMihomoConfig({ 'skip-auth-prefixes': newPrefixes })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// add default authentication
|
|
||||||
if (!authentication) {
|
|
||||||
await patchControledMihomoConfig({ authentication: [] })
|
|
||||||
}
|
|
||||||
// add default bind address
|
|
||||||
if (!bindAddress) {
|
|
||||||
await patchControledMihomoConfig({ 'bind-address': '*' })
|
|
||||||
}
|
|
||||||
// add default lan allowed ips
|
|
||||||
if (!lanAllowedIps) {
|
|
||||||
await patchControledMihomoConfig({ 'lan-allowed-ips': ['0.0.0.0/0', '::/0'] })
|
|
||||||
}
|
|
||||||
// add default lan disallowed ips
|
|
||||||
if (!lanDisallowedIps) {
|
|
||||||
await patchControledMihomoConfig({ 'lan-disallowed-ips': [] })
|
|
||||||
}
|
|
||||||
// default tun device
|
|
||||||
if (!tun?.device || (process.platform === 'darwin' && tun.device === 'Mihomo')) {
|
|
||||||
const defaultDevice = process.platform === 'darwin' ? 'utun1500' : 'Mihomo'
|
|
||||||
await patchControledMihomoConfig({
|
|
||||||
tun: {
|
|
||||||
...tun,
|
|
||||||
device: defaultDevice
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// remove custom app theme
|
|
||||||
if (!['system', 'light', 'dark'].includes(appTheme)) {
|
if (!['system', 'light', 'dark'].includes(appTheme)) {
|
||||||
await patchAppConfig({ appTheme: 'system' })
|
await patchAppConfig({ appTheme: 'system' })
|
||||||
}
|
}
|
||||||
// change env type
|
}
|
||||||
|
|
||||||
|
// 迁移:envType 字符串转数组
|
||||||
|
async function migrateEnvType(): Promise<void> {
|
||||||
|
const { envType } = await getAppConfig()
|
||||||
if (typeof envType === 'string') {
|
if (typeof envType === 'string') {
|
||||||
await patchAppConfig({ envType: [envType] })
|
await patchAppConfig({ envType: [envType] })
|
||||||
}
|
}
|
||||||
// use unix socket
|
}
|
||||||
if (externalControllerUnix) {
|
|
||||||
await patchControledMihomoConfig({ 'external-controller-unix': undefined })
|
// 迁移:禁用托盘时必须显示悬浮窗
|
||||||
}
|
async function migrateTraySettings(): Promise<void> {
|
||||||
// use named pipe
|
const { showFloatingWindow = false, disableTray = false } = await getAppConfig()
|
||||||
if (externalControllerPipe) {
|
|
||||||
await patchControledMihomoConfig({
|
|
||||||
'external-controller-pipe': undefined
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if (externalController === undefined) {
|
|
||||||
await patchControledMihomoConfig({ 'external-controller': '' })
|
|
||||||
}
|
|
||||||
if (!showFloatingWindow && disableTray) {
|
if (!showFloatingWindow && disableTray) {
|
||||||
await patchAppConfig({ disableTray: false })
|
await patchAppConfig({ disableTray: false })
|
||||||
}
|
}
|
||||||
// remove password
|
}
|
||||||
|
|
||||||
|
// 迁移:移除加密密码
|
||||||
|
async function migrateRemovePassword(): Promise<void> {
|
||||||
|
const { encryptedPassword } = await getAppConfig()
|
||||||
if (encryptedPassword) {
|
if (encryptedPassword) {
|
||||||
await patchAppConfig({ encryptedPassword: undefined })
|
await patchAppConfig({ encryptedPassword: undefined })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 迁移:mihomo 配置默认值
|
||||||
|
async function migrateMihomoConfig(): Promise<void> {
|
||||||
|
const config = await getControledMihomoConfig()
|
||||||
|
const patches: Partial<IMihomoConfig> = {}
|
||||||
|
|
||||||
|
// skip-auth-prefixes
|
||||||
|
if (!config['skip-auth-prefixes']) {
|
||||||
|
patches['skip-auth-prefixes'] = ['127.0.0.1/32', '::1/128']
|
||||||
|
} else if (
|
||||||
|
config['skip-auth-prefixes'].length >= 1 &&
|
||||||
|
config['skip-auth-prefixes'][0] === '127.0.0.1/32' &&
|
||||||
|
!config['skip-auth-prefixes'].includes('::1/128')
|
||||||
|
) {
|
||||||
|
patches['skip-auth-prefixes'] = ['127.0.0.1/32', '::1/128', ...config['skip-auth-prefixes'].slice(1)]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 其他默认值
|
||||||
|
if (!config.authentication) patches.authentication = []
|
||||||
|
if (!config['bind-address']) patches['bind-address'] = '*'
|
||||||
|
if (!config['lan-allowed-ips']) patches['lan-allowed-ips'] = ['0.0.0.0/0', '::/0']
|
||||||
|
if (!config['lan-disallowed-ips']) patches['lan-disallowed-ips'] = []
|
||||||
|
|
||||||
|
// tun device
|
||||||
|
if (!config.tun?.device || (process.platform === 'darwin' && config.tun.device === 'Mihomo')) {
|
||||||
|
patches.tun = {
|
||||||
|
...config.tun,
|
||||||
|
device: process.platform === 'darwin' ? 'utun1500' : 'Mihomo'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除废弃配置
|
||||||
|
if (config['external-controller-unix']) patches['external-controller-unix'] = undefined
|
||||||
|
if (config['external-controller-pipe']) patches['external-controller-pipe'] = undefined
|
||||||
|
if (config['external-controller'] === undefined) patches['external-controller'] = ''
|
||||||
|
|
||||||
|
if (Object.keys(patches).length > 0) {
|
||||||
|
await patchControledMihomoConfig(patches)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function migration(): Promise<void> {
|
||||||
|
await Promise.all([
|
||||||
|
migrateSiderOrder(),
|
||||||
|
migrateAppTheme(),
|
||||||
|
migrateEnvType(),
|
||||||
|
migrateTraySettings(),
|
||||||
|
migrateRemovePassword(),
|
||||||
|
migrateMihomoConfig()
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
function initDeeplink(): void {
|
function initDeeplink(): void {
|
||||||
if (process.defaultApp) {
|
if (process.defaultApp) {
|
||||||
if (process.argv.length >= 2) {
|
if (process.argv.length >= 2) {
|
||||||
@ -395,11 +361,8 @@ function initDeeplink(): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 基础初始化
|
|
||||||
export async function initBasic(): Promise<void> {
|
export async function initBasic(): Promise<void> {
|
||||||
if (isInitBasicCompleted) {
|
if (isInitBasicCompleted) return
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
await initDirs()
|
await initDirs()
|
||||||
await initConfig()
|
await initConfig()
|
||||||
@ -414,6 +377,7 @@ export async function initBasic(): Promise<void> {
|
|||||||
export async function init(): Promise<void> {
|
export async function init(): Promise<void> {
|
||||||
await startSubStoreFrontendServer()
|
await startSubStoreFrontendServer()
|
||||||
await startSubStoreBackendServer()
|
await startSubStoreBackendServer()
|
||||||
|
|
||||||
const { sysProxy } = await getAppConfig()
|
const { sysProxy } = await getAppConfig()
|
||||||
try {
|
try {
|
||||||
if (sysProxy.enable) {
|
if (sysProxy.enable) {
|
||||||
@ -423,7 +387,7 @@ export async function init(): Promise<void> {
|
|||||||
} catch {
|
} catch {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
await startSSIDCheck()
|
|
||||||
|
|
||||||
|
await startSSIDCheck()
|
||||||
initDeeplink()
|
initDeeplink()
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user