mihomo-party/src/main/core/factory.ts
2024-08-18 21:50:04 +08:00

102 lines
2.9 KiB
TypeScript

import {
getControledMihomoConfig,
getProfileConfig,
getProfile,
getProfileItem,
getOverride,
getOverrideItem
} from '../config'
import { mihomoWorkConfigPath, overridePath } from '../utils/dirs'
import yaml from 'yaml'
import { readFile, writeFile } from 'fs/promises'
import { deepMerge } from '../utils/merge'
import vm from 'vm'
import { writeFileSync } from 'fs'
export async function generateProfile(): Promise<void> {
const { current } = await getProfileConfig()
const currentProfile = await overrideProfile(current, await getProfile(current))
const controledMihomoConfig = await getControledMihomoConfig()
const profile = deepMerge(currentProfile, controledMihomoConfig)
await writeFile(mihomoWorkConfigPath(), yaml.stringify(profile))
}
async function overrideProfile(
current: string | undefined,
profile: IMihomoConfig
): Promise<IMihomoConfig> {
const { override = [] } = (await getProfileItem(current)) || {}
for (const ov of override) {
const item = await getOverrideItem(ov)
const content = await getOverride(ov, item?.ext || 'js')
switch (item?.ext) {
case 'js':
profile = runOverrideScript(profile, content, item)
break
case 'yaml': {
const patch = yaml.parse(content)
if (patch.rules) {
patch.rules = [...patch.rules, ...(profile.rules || [])]
}
if (patch.proxies) {
patch.proxies = [...patch.proxies, ...(profile.proxies || [])]
}
profile = deepMerge(profile, patch)
break
}
}
}
return profile
}
function runOverrideScript(
profile: IMihomoConfig,
script: string,
item: IOverrideItem
): IMihomoConfig {
const log = (type: string, data: string, flag = 'a'): void => {
writeFileSync(overridePath(item.id, 'log'), `[${type}] ${data}\n`, {
encoding: 'utf-8',
flag
})
}
try {
const ctx = {
console: Object.freeze({
log(data: never) {
log('log', JSON.stringify(data))
},
info(data: never) {
log('info', JSON.stringify(data))
},
error(data: never) {
log('error', JSON.stringify(data))
},
debug(data: never) {
log('debug', JSON.stringify(data))
}
})
}
vm.createContext(ctx)
const code = `${script} main(${JSON.stringify(profile)})`
log('info', '开始执行脚本', 'w')
const newProfile = vm.runInContext(code, ctx)
if (typeof newProfile !== 'object') {
throw new Error('脚本返回值必须是对象')
}
log('info', '脚本执行成功')
return newProfile
} catch (e) {
log('exception', `脚本执行失败: ${e}`)
return profile
}
}
export async function getRuntimeConfigStr(): Promise<string> {
return await readFile(mihomoWorkConfigPath(), 'utf8')
}
export async function getRuntimeConfig(): Promise<IMihomoConfig> {
return yaml.parse(await getRuntimeConfigStr())
}