把入站固定到某条订阅线路
订阅线路不在内核的全局名字表里,入站的 proxy 指不到它(决策记录 0005)。做法是自动建 一个只装它的策略组再指过去,用户看到的只是「这个口固定走这条线」,组是实现细节: 不出现在策略组列表里,列表和详情里都显示成线路本身的名字,没人再引用时自动删掉。 筛选条件必须做对两件事,都写了测试:转义正则元字符(线路名形如 [vmess]DMIT.LAX.Pro 里的方括号和点不转义会匹配错),以及前后加锚点(内核是部分匹配,不加锚点时「香港01」 会把「香港011」也捞进来)。线路名含反引号时表达不了——那是内核切分多个筛选条件的 分隔符——这种情况明说做不到而不是生成一个会静默匹配错的组。 实测:生成的配置 mihomo -t 通过;起一个临时内核问 /proxies,这个组解析出来的成员 恰好只有那一条线路,且确实进了名字表所以入站指得到。
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
import { describe, expect, it } from 'bun:test'
|
||||
import {
|
||||
generatedGroupName,
|
||||
isGeneratedGroup,
|
||||
lineFilter,
|
||||
makeFilterGroup,
|
||||
pinnedLineOf,
|
||||
pruneGeneratedGroups,
|
||||
} from './filter-groups.ts'
|
||||
import type { ConfigObject } from '../vendor/config-editor/index.ts'
|
||||
|
||||
const LINE = '[vless]Gomami.SIN Secret'
|
||||
|
||||
describe('筛选条件', () => {
|
||||
it('转义正则元字符', () => {
|
||||
// 线路名里的 [ ] . 不转义会当成正则语法,匹配到别的线路上去
|
||||
expect(lineFilter(LINE)).toBe('^\\[vless\\]Gomami\\.SIN Secret$')
|
||||
})
|
||||
|
||||
it('前后加锚点,避免匹配到名字更长的线路', () => {
|
||||
// 内核的筛选是部分匹配:不加锚点时「香港01」会把「香港011」也捞进来
|
||||
expect(lineFilter('香港01')).toBe('^香港01$')
|
||||
})
|
||||
|
||||
it('线路名含反引号时表达不了,返回空让调用方说明原因', () => {
|
||||
// 反引号在内核里是多个筛选条件的分隔符,塞进一条正则会被切成两半
|
||||
expect(lineFilter('怪`名字')).toBeNull()
|
||||
})
|
||||
|
||||
it('转义后的正则确实只匹配它自己', () => {
|
||||
const re = new RegExp(lineFilter(LINE)!)
|
||||
expect(re.test(LINE)).toBe(true)
|
||||
expect(re.test('[vless]GomamiXSIN Secret')).toBe(false)
|
||||
expect(re.test(`${LINE} 备用`)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('生成的组', () => {
|
||||
it('名字带前缀,这是识别它的唯一可靠标记', () => {
|
||||
// 注释靠不住——被碰到的段落重写时会丢,见决策记录 0002
|
||||
const name = generatedGroupName(LINE)
|
||||
expect(isGeneratedGroup(name)).toBe(true)
|
||||
expect(pinnedLineOf(name)).toBe(LINE)
|
||||
})
|
||||
|
||||
it('用户自己建的组不会被认成生成的', () => {
|
||||
expect(isGeneratedGroup('PROXY')).toBe(false)
|
||||
expect(pinnedLineOf('PROXY')).toBeNull()
|
||||
})
|
||||
|
||||
it('组里只有那一条线路,类型是手动选', () => {
|
||||
expect(makeFilterGroup(LINE, 'subs')).toEqual({
|
||||
name: `patchbay:${LINE}`,
|
||||
type: 'select',
|
||||
use: ['subs'],
|
||||
filter: '^\\[vless\\]Gomami\\.SIN Secret$',
|
||||
})
|
||||
})
|
||||
|
||||
it('表达不了的线路名不生成组', () => {
|
||||
expect(makeFilterGroup('怪`名字', 'subs')).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('清理没人用的生成组', () => {
|
||||
const generated = (line: string) => ({ name: generatedGroupName(line), type: 'select' })
|
||||
|
||||
it('入站还指着它就留着', () => {
|
||||
const data: ConfigObject = {
|
||||
listeners: [{ name: 'A', type: 'mixed', port: 1, proxy: generatedGroupName(LINE) }],
|
||||
'proxy-groups': [generated(LINE)],
|
||||
}
|
||||
expect(pruneGeneratedGroups(data)['proxy-groups']).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('没人再指它就删掉', () => {
|
||||
const data: ConfigObject = {
|
||||
listeners: [{ name: 'A', type: 'mixed', port: 1 }],
|
||||
'proxy-groups': [generated(LINE)],
|
||||
}
|
||||
// 不清理的话,改几次出站文件里就攒下一堆用户从没主动建过的组
|
||||
expect(pruneGeneratedGroups(data)['proxy-groups']).toBeUndefined()
|
||||
})
|
||||
|
||||
it('被别的策略组当成员引用也算在用', () => {
|
||||
const data: ConfigObject = {
|
||||
'proxy-groups': [generated(LINE), { name: 'PROXY', proxies: [generatedGroupName(LINE)] }],
|
||||
}
|
||||
expect(pruneGeneratedGroups(data)['proxy-groups']).toHaveLength(2)
|
||||
})
|
||||
|
||||
it('被规则引用也算在用', () => {
|
||||
const data: ConfigObject = {
|
||||
'proxy-groups': [generated(LINE)],
|
||||
rules: [`MATCH,${generatedGroupName(LINE)}`],
|
||||
}
|
||||
expect(pruneGeneratedGroups(data)['proxy-groups']).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('用户自己建的组一律不碰,哪怕真的没人引用', () => {
|
||||
const data: ConfigObject = { 'proxy-groups': [{ name: '我建的组', type: 'select' }] }
|
||||
expect(pruneGeneratedGroups(data)['proxy-groups']).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('没有可删的时候原样返回,不制造无谓的改动', () => {
|
||||
// 返回同一个对象,落盘链路才看得出「内容没变」而跳过备份和重载
|
||||
const data: ConfigObject = { 'proxy-groups': [{ name: 'PROXY' }] }
|
||||
expect(pruneGeneratedGroups(data)).toBe(data)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user