import { appConfigPath, controledMihomoConfigPath, dataDir, logDir, mihomoTestDir, mihomoWorkDir, overrideConfigPath, overrideDir, profileConfigPath, profilePath, profilesDir, resourcesFilesDir, subStoreDir, themesDir } from './dirs' import { defaultConfig, defaultControledMihomoConfig, defaultOverrideConfig, defaultProfile, defaultProfileConfig } from './template' import yaml from 'yaml' import { mkdir, writeFile, copyFile, rm, readdir } from 'fs/promises' import { existsSync } from 'fs' import path from 'path' import { startPacServer, startSubStoreServer } from '../resolve/server' import { triggerSysProxy } from '../sys/sysproxy' import { getAppConfig, getControledMihomoConfig, patchAppConfig, patchControledMihomoConfig } from '../config' import { app } from 'electron' async function initDirs(): Promise { if (!existsSync(dataDir())) { await mkdir(dataDir()) } if (!existsSync(themesDir())) { await mkdir(themesDir()) } if (!existsSync(profilesDir())) { await mkdir(profilesDir()) } if (!existsSync(overrideDir())) { await mkdir(overrideDir()) } if (!existsSync(mihomoWorkDir())) { await mkdir(mihomoWorkDir()) } if (!existsSync(logDir())) { await mkdir(logDir()) } if (!existsSync(mihomoTestDir())) { await mkdir(mihomoTestDir()) } if (!existsSync(subStoreDir())) { await mkdir(subStoreDir()) } } async function initConfig(): Promise { if (!existsSync(appConfigPath())) { await writeFile(appConfigPath(), yaml.stringify(defaultConfig)) } if (!existsSync(profileConfigPath())) { await writeFile(profileConfigPath(), yaml.stringify(defaultProfileConfig)) } if (!existsSync(overrideConfigPath())) { await writeFile(overrideConfigPath(), yaml.stringify(defaultOverrideConfig)) } if (!existsSync(profilePath('default'))) { await writeFile(profilePath('default'), yaml.stringify(defaultProfile)) } if (!existsSync(controledMihomoConfigPath())) { await writeFile(controledMihomoConfigPath(), yaml.stringify(defaultControledMihomoConfig)) } } async function initFiles(): Promise { const copy = async (file: string): Promise => { const targetPath = path.join(mihomoWorkDir(), file) const testTargrtPath = path.join(mihomoTestDir(), file) const sourcePath = path.join(resourcesFilesDir(), file) if (!existsSync(targetPath) && existsSync(sourcePath)) { await copyFile(sourcePath, targetPath) } if (!existsSync(testTargrtPath) && existsSync(sourcePath)) { await copyFile(sourcePath, testTargrtPath) } } await Promise.all([ copy('country.mmdb'), copy('geoip.dat'), copy('geosite.dat'), copy('ASN.mmdb') ]) } async function cleanup(): Promise { // update cache const files = await readdir(dataDir()) for (const file of files) { if (file.endsWith('.exe') || file.endsWith('.dmg') || file.endsWith('.zip')) { try { await rm(path.join(dataDir(), file)) } catch { // ignore } } } // logs const { maxLogDays = 7 } = await getAppConfig() const logs = await readdir(logDir()) for (const log of logs) { const date = new Date(log.split('.')[0]) const diff = Date.now() - date.getTime() if (diff > maxLogDays * 24 * 60 * 60 * 1000) { try { await rm(path.join(logDir(), log)) } catch { // ignore } } } } async function migration(): Promise { const { siderOrder = [ 'sysproxy', 'tun', 'profile', 'proxy', 'rule', 'resource', 'override', 'connection', 'mihomo', 'dns', 'sniff', 'log', 'substore' ], appTheme = 'system', useSubStore = true } = await getAppConfig() const { 'skip-auth-prefixes': skipAuthPrefixes, authentication, 'bind-address': bindAddress, 'lan-allowed-ips': lanAllowedIps, 'lan-disallowed-ips': lanDisallowedIps } = await getControledMihomoConfig() // add substore sider card if (useSubStore && !siderOrder.includes('substore')) { await patchAppConfig({ siderOrder: [...siderOrder, 'substore'] }) } // add default skip auth prefix if (!skipAuthPrefixes) { await patchControledMihomoConfig({ 'skip-auth-prefixes': ['127.0.0.1/32'] }) } // 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': [] }) } // remove custom app theme if (!['system', 'light', 'dark'].includes(appTheme)) { await patchAppConfig({ appTheme: 'system' }) } } function initDeeplink(): void { if (process.defaultApp) { if (process.argv.length >= 2) { app.setAsDefaultProtocolClient('clash', process.execPath, [path.resolve(process.argv[1])]) app.setAsDefaultProtocolClient('mihomo', process.execPath, [path.resolve(process.argv[1])]) } } else { app.setAsDefaultProtocolClient('clash') app.setAsDefaultProtocolClient('mihomo') } } export async function init(): Promise { await initDirs() await initConfig() await migration() await initFiles() await cleanup() await startPacServer() await startSubStoreServer() const { sysProxy } = await getAppConfig() await triggerSysProxy(sysProxy.enable) initDeeplink() }