mirror of
https://gh.catmak.name/https://github.com/mihomo-party-org/mihomo-party
synced 2026-04-12 23:50:31 +08:00
424 lines
15 KiB
TypeScript
424 lines
15 KiB
TypeScript
import { overrideLogger } from '../utils/logger'
|
||
import { getAppConfig } from './app'
|
||
import { addOverrideItem, removeOverrideItem, getOverrideItem } from './override'
|
||
|
||
const SMART_OVERRIDE_ID = 'smart-core-override'
|
||
|
||
/**
|
||
* Smart 内核的覆写配置模板
|
||
*/
|
||
function generateSmartOverrideTemplate(
|
||
useLightGBM: boolean,
|
||
collectData: boolean,
|
||
strategy: string,
|
||
collectorSize: number
|
||
): string {
|
||
return `
|
||
// 配置会在启用 Smart 内核时自动应用
|
||
|
||
function main(config) {
|
||
try {
|
||
// 确保配置对象存在
|
||
if (!config || typeof config !== 'object') {
|
||
console.log('[Smart Override] Invalid config object')
|
||
return config
|
||
}
|
||
|
||
// 设置 Smart 内核的 profile 配置
|
||
if (!config.profile) {
|
||
config.profile = {}
|
||
}
|
||
config.profile['smart-collector-size'] = ${collectorSize}
|
||
|
||
// 确保代理组配置存在
|
||
if (!config['proxy-groups']) {
|
||
config['proxy-groups'] = []
|
||
}
|
||
|
||
// 确保代理组是数组
|
||
if (!Array.isArray(config['proxy-groups'])) {
|
||
console.log('[Smart Override] proxy-groups is not an array, converting...')
|
||
config['proxy-groups'] = []
|
||
}
|
||
|
||
// 首先检查是否存在 url-test 或 load-balance 代理组
|
||
let hasUrlTestOrLoadBalance = false
|
||
for (let i = 0; i < config['proxy-groups'].length; i++) {
|
||
const group = config['proxy-groups'][i]
|
||
if (group && group.type) {
|
||
const groupType = group.type.toLowerCase()
|
||
if (groupType === 'url-test' || groupType === 'load-balance') {
|
||
hasUrlTestOrLoadBalance = true
|
||
break
|
||
}
|
||
}
|
||
}
|
||
|
||
// 如果存在 url-test 或 load-balance 代理组,只进行类型转换
|
||
if (hasUrlTestOrLoadBalance) {
|
||
console.log('[Smart Override] Found url-test or load-balance groups, converting to smart type')
|
||
|
||
// 记录需要更新引用的代理组名称映射
|
||
const nameMapping = new Map()
|
||
|
||
for (let i = 0; i < config['proxy-groups'].length; i++) {
|
||
const group = config['proxy-groups'][i]
|
||
if (group && group.type) {
|
||
const groupType = group.type.toLowerCase()
|
||
if (groupType === 'url-test' || groupType === 'load-balance') {
|
||
console.log('[Smart Override] Converting group:', group.name, 'from', group.type, 'to smart')
|
||
|
||
// 记录原名称和新名称的映射关系
|
||
const originalName = group.name
|
||
|
||
// 保留原有配置,只修改 type 和添加 Smart 特有配置
|
||
group.type = 'smart'
|
||
|
||
// 为代理组名称添加 (Smart Group) 后缀
|
||
if (group.name && !group.name.includes('(Smart Group)')) {
|
||
group.name = group.name + '(Smart Group)'
|
||
nameMapping.set(originalName, group.name)
|
||
}
|
||
|
||
// 添加 Smart 特有配置
|
||
if (!group['policy-priority']) {
|
||
group['policy-priority'] = '' // policy-priority: <1 means lower priority, >1 means higher priority, the default is 1, pattern support regex and string
|
||
}
|
||
group.uselightgbm = ${useLightGBM}
|
||
group.collectdata = ${collectData}
|
||
group.strategy = '${strategy}'
|
||
|
||
// 移除 url-test 和 load-balance 特有的配置
|
||
if (group.url) delete group.url
|
||
if (group.interval) delete group.interval
|
||
if (group.tolerance) delete group.tolerance
|
||
if (group.lazy) delete group.lazy
|
||
if (group.expected_status) delete group['expected-status']
|
||
}
|
||
}
|
||
}
|
||
|
||
// 更新配置文件中其他位置对代理组名称的引用
|
||
if (nameMapping.size > 0) {
|
||
console.log('[Smart Override] Updating references to renamed groups:', Array.from(nameMapping.entries()))
|
||
|
||
// 更新代理组中的 proxies 字段引用
|
||
if (config['proxy-groups'] && Array.isArray(config['proxy-groups'])) {
|
||
config['proxy-groups'].forEach(group => {
|
||
if (group && group.proxies && Array.isArray(group.proxies)) {
|
||
group.proxies = group.proxies.map(proxyName => {
|
||
if (nameMapping.has(proxyName)) {
|
||
console.log('[Smart Override] Updated proxy reference:', proxyName, '→', nameMapping.get(proxyName))
|
||
return nameMapping.get(proxyName)
|
||
}
|
||
return proxyName
|
||
})
|
||
}
|
||
})
|
||
}
|
||
|
||
// 更新规则中的代理组引用
|
||
// 规则参数列表,这些不是策略组名称
|
||
const ruleParamsSet = new Set(['no-resolve', 'force-remote-dns', 'prefer-ipv6'])
|
||
|
||
if (config.rules && Array.isArray(config.rules)) {
|
||
config.rules = config.rules.map(rule => {
|
||
if (typeof rule === 'string') {
|
||
// 按逗号分割规则,精确匹配策略组名称位置
|
||
const parts = rule.split(',').map(part => part.trim())
|
||
|
||
if (parts.length >= 2) {
|
||
// 找到策略组名称的位置
|
||
let targetIndex = -1
|
||
|
||
// MATCH 规则:MATCH,策略组
|
||
if (parts[0] === 'MATCH' && parts.length === 2) {
|
||
targetIndex = 1
|
||
} else if (parts.length >= 3) {
|
||
// 其他规则:TYPE,MATCHER,策略组 [,参数...]
|
||
// 策略组通常在第 3 个位置(索引 2),但需要跳过参数
|
||
for (let i = 2; i < parts.length; i++) {
|
||
if (!ruleParamsSet.has(parts[i])) {
|
||
targetIndex = i
|
||
break
|
||
}
|
||
}
|
||
}
|
||
|
||
// 只替换策略组名称位置
|
||
if (targetIndex !== -1 && nameMapping.has(parts[targetIndex])) {
|
||
const oldName = parts[targetIndex]
|
||
parts[targetIndex] = nameMapping.get(oldName)
|
||
console.log('[Smart Override] Updated rule reference:', oldName, '→', nameMapping.get(oldName))
|
||
return parts.join(',')
|
||
}
|
||
}
|
||
return rule
|
||
} else if (typeof rule === 'object' && rule !== null) {
|
||
// 处理对象格式的规则
|
||
['target', 'proxy'].forEach(field => {
|
||
if (rule[field] && nameMapping.has(rule[field])) {
|
||
console.log('[Smart Override] Updated rule object reference:', rule[field], '→', nameMapping.get(rule[field]))
|
||
rule[field] = nameMapping.get(rule[field])
|
||
}
|
||
})
|
||
}
|
||
return rule
|
||
})
|
||
}
|
||
|
||
// 更新其他可能的配置字段引用
|
||
['mode', 'proxy-mode'].forEach(field => {
|
||
if (config[field] && nameMapping.has(config[field])) {
|
||
console.log('[Smart Override] Updated config field', field + ':', config[field], '→', nameMapping.get(config[field]))
|
||
config[field] = nameMapping.get(config[field])
|
||
}
|
||
})
|
||
}
|
||
|
||
console.log('[Smart Override] Conversion completed, skipping other operations')
|
||
return config
|
||
}
|
||
|
||
// 如果没有 url-test 或 load-balance 代理组,执行原有逻辑
|
||
console.log('[Smart Override] No url-test or load-balance groups found, executing original logic')
|
||
|
||
// 查找现有的 Smart 代理组并更新
|
||
let smartGroupExists = false
|
||
for (let i = 0; i < config['proxy-groups'].length; i++) {
|
||
const group = config['proxy-groups'][i]
|
||
if (group && group.type === 'smart') {
|
||
smartGroupExists = true
|
||
console.log('[Smart Override] Found existing smart group:', group.name)
|
||
|
||
if (!group['policy-priority']) {
|
||
group['policy-priority'] = '' // policy-priority: <1 means lower priority, >1 means higher priority, the default is 1, pattern support regex and string
|
||
}
|
||
group.uselightgbm = ${useLightGBM}
|
||
group.collectdata = ${collectData}
|
||
group.strategy = '${strategy}'
|
||
break
|
||
}
|
||
}
|
||
|
||
// 如果没有 Smart 组且有可用代理,创建示例组
|
||
if (!smartGroupExists && config.proxies && Array.isArray(config.proxies) && config.proxies.length > 0) {
|
||
console.log('[Smart Override] Creating new smart group with', config.proxies.length, 'proxies')
|
||
|
||
// 获取所有代理的名称
|
||
const proxyNames = config.proxies
|
||
.filter(proxy => proxy && typeof proxy === 'object' && proxy.name)
|
||
.map(proxy => proxy.name)
|
||
|
||
if (proxyNames.length > 0) {
|
||
const smartGroup = {
|
||
name: 'Smart Group',
|
||
type: 'smart',
|
||
'policy-priority': '', // policy-priority: <1 means lower priority, >1 means higher priority, the default is 1, pattern support regex and string
|
||
uselightgbm: ${useLightGBM},
|
||
collectdata: ${collectData},
|
||
strategy: '${strategy}',
|
||
proxies: proxyNames
|
||
}
|
||
config['proxy-groups'].unshift(smartGroup)
|
||
console.log('[Smart Override] Created smart group at first position with proxies:', proxyNames)
|
||
} else {
|
||
console.log('[Smart Override] No valid proxies found, skipping smart group creation')
|
||
}
|
||
} else if (!smartGroupExists) {
|
||
console.log('[Smart Override] No proxies available, skipping smart group creation')
|
||
}
|
||
|
||
// 处理规则替换
|
||
if (config.rules && Array.isArray(config.rules)) {
|
||
console.log('[Smart Override] Processing rules, original count:', config.rules.length)
|
||
|
||
// 收集所有代理组名称
|
||
const proxyGroupNames = new Set()
|
||
if (config['proxy-groups'] && Array.isArray(config['proxy-groups'])) {
|
||
config['proxy-groups'].forEach(group => {
|
||
if (group && group.name) {
|
||
proxyGroupNames.add(group.name)
|
||
}
|
||
})
|
||
}
|
||
|
||
// 添加常见的内置目标
|
||
const builtinTargets = new Set([
|
||
'DIRECT',
|
||
'REJECT',
|
||
'REJECT-DROP',
|
||
'PASS',
|
||
'COMPATIBLE'
|
||
])
|
||
|
||
// 添加常见的规则参数,不应该替换
|
||
const ruleParams = new Set(['no-resolve', 'force-remote-dns', 'prefer-ipv6'])
|
||
|
||
console.log('[Smart Override] Found', proxyGroupNames.size, 'proxy groups:', Array.from(proxyGroupNames))
|
||
|
||
let replacedCount = 0
|
||
config.rules = config.rules.map(rule => {
|
||
if (typeof rule === 'string') {
|
||
// 检查是否是复杂规则格式(包含括号的嵌套规则)
|
||
if (rule.includes('((') || rule.includes('))')) {
|
||
console.log('[Smart Override] Skipping complex nested rule:', rule)
|
||
return rule
|
||
}
|
||
|
||
// 处理字符串格式的规则
|
||
const parts = rule.split(',').map(part => part.trim())
|
||
if (parts.length >= 2) {
|
||
// 找到代理组名称的位置
|
||
let targetIndex = -1
|
||
let targetValue = ''
|
||
|
||
// 处理 MATCH 规则
|
||
if (parts[0] === 'MATCH' && parts.length === 2) {
|
||
targetIndex = 1
|
||
targetValue = parts[1]
|
||
} else if (parts.length >= 3) {
|
||
// 处理其他规则
|
||
for (let i = 2; i < parts.length; i++) {
|
||
const part = parts[i]
|
||
if (!ruleParams.has(part)) {
|
||
targetIndex = i
|
||
targetValue = part
|
||
break
|
||
}
|
||
}
|
||
}
|
||
|
||
if (targetIndex !== -1 && targetValue) {
|
||
// 检查是否应该替换
|
||
const shouldReplace = !builtinTargets.has(targetValue) &&
|
||
(proxyGroupNames.has(targetValue) ||
|
||
!ruleParams.has(targetValue))
|
||
|
||
if (shouldReplace) {
|
||
parts[targetIndex] = 'Smart Group'
|
||
replacedCount++
|
||
console.log('[Smart Override] Replaced rule target:', targetValue, '→ Smart Group')
|
||
return parts.join(',')
|
||
}
|
||
}
|
||
}
|
||
} else if (typeof rule === 'object' && rule !== null) {
|
||
// 处理对象格式
|
||
let targetField = ''
|
||
let targetValue = ''
|
||
|
||
if (rule.target) {
|
||
targetField = 'target'
|
||
targetValue = rule.target
|
||
} else if (rule.proxy) {
|
||
targetField = 'proxy'
|
||
targetValue = rule.proxy
|
||
}
|
||
|
||
if (targetField && targetValue) {
|
||
const shouldReplace = !builtinTargets.has(targetValue) &&
|
||
(proxyGroupNames.has(targetValue) ||
|
||
!ruleParams.has(targetValue))
|
||
|
||
if (shouldReplace) {
|
||
rule[targetField] = 'Smart Group'
|
||
replacedCount++
|
||
console.log('[Smart Override] Replaced rule target:', targetValue, '→ Smart Group')
|
||
}
|
||
}
|
||
}
|
||
return rule
|
||
})
|
||
|
||
console.log('[Smart Override] Rules processed, replaced', replacedCount, 'non-DIRECT rules with Smart Group')
|
||
} else {
|
||
console.log('[Smart Override] No rules found or rules is not an array')
|
||
}
|
||
|
||
console.log('[Smart Override] Configuration processed successfully')
|
||
return config
|
||
} catch (error) {
|
||
console.error('[Smart Override] Error processing config:', error)
|
||
// 发生错误时返回原始配置,避免破坏整个配置
|
||
return config
|
||
}
|
||
}
|
||
`
|
||
}
|
||
|
||
/**
|
||
* 创建或更新 Smart 内核覆写配置
|
||
*/
|
||
export async function createSmartOverride(): Promise<void> {
|
||
try {
|
||
// 获取应用配置
|
||
const {
|
||
smartCoreUseLightGBM = false,
|
||
smartCoreCollectData = false,
|
||
smartCoreStrategy = 'sticky-sessions',
|
||
smartCollectorSize = 100
|
||
} = await getAppConfig()
|
||
|
||
// 生成覆写模板
|
||
const template = generateSmartOverrideTemplate(
|
||
smartCoreUseLightGBM,
|
||
smartCoreCollectData,
|
||
smartCoreStrategy,
|
||
smartCollectorSize
|
||
)
|
||
|
||
await addOverrideItem({
|
||
id: SMART_OVERRIDE_ID,
|
||
name: 'Smart Core Override',
|
||
type: 'local',
|
||
ext: 'js',
|
||
global: true,
|
||
file: template
|
||
})
|
||
} catch (error) {
|
||
await overrideLogger.error('Failed to create Smart override', error)
|
||
throw error
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 删除 Smart 内核覆写配置
|
||
*/
|
||
export async function removeSmartOverride(): Promise<void> {
|
||
try {
|
||
const existingOverride = await getOverrideItem(SMART_OVERRIDE_ID)
|
||
if (existingOverride) {
|
||
await removeOverrideItem(SMART_OVERRIDE_ID)
|
||
}
|
||
} catch (error) {
|
||
await overrideLogger.error('Failed to remove Smart override', error)
|
||
throw error
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 根据应用配置管理 Smart 覆写
|
||
*/
|
||
export async function manageSmartOverride(): Promise<void> {
|
||
const { enableSmartCore = true, enableSmartOverride = true, core } = await getAppConfig()
|
||
|
||
if (enableSmartCore && enableSmartOverride && core === 'mihomo-smart') {
|
||
await createSmartOverride()
|
||
} else {
|
||
await removeSmartOverride()
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查 Smart 覆写是否存在
|
||
*/
|
||
export async function isSmartOverrideExists(): Promise<boolean> {
|
||
try {
|
||
const override = await getOverrideItem(SMART_OVERRIDE_ID)
|
||
return !!override
|
||
} catch {
|
||
return false
|
||
}
|
||
}
|