diff --git a/shared/rules.test.ts b/shared/rules.test.ts index 1a64098..4a97264 100644 --- a/shared/rules.test.ts +++ b/shared/rules.test.ts @@ -16,6 +16,7 @@ import { ruleSetReferences, setRuleProviders, setRules, + subRuleSetReferences, } from './rules.ts' import type { ConfigObject } from '../vendor/config-editor/index.ts' @@ -316,3 +317,17 @@ describe('清单格式与缓存路径', () => { expect(ruleProviderPathConflict(data, 'x', ruleProviderPath('x', 'yaml'))).toBeNull() }) }) + +describe('附加规则里的规则组引用', () => { + it('sub-rules 各表里的 RULE-SET 引用都认出来,别的表不误报', () => { + const data = { + 'sub-rules': { + 'patchbay:t1 附加': ['RULE-SET,ads,REJECT', 'MATCH,patchbay:t1'], + mine: ['RULE-SET,other,DIRECT'], + }, + } + expect(subRuleSetReferences(data, 'ads')).toEqual(['RULE-SET,ads,REJECT']) + expect(subRuleSetReferences(data, 'ghost')).toEqual([]) + expect(subRuleSetReferences({}, 'ads')).toEqual([]) + }) +}) diff --git a/shared/rules.ts b/shared/rules.ts index 6a2df02..e005932 100644 --- a/shared/rules.ts +++ b/shared/rules.ts @@ -168,6 +168,28 @@ export function ruleSetReferences(rules: string[], providerName: string): number return result } +/** + * sub-rules 各表(含每路入站的附加规则)里引用这个规则组的规则原文。 + * 删除规则组前和顶层的一起查——漏了的话删除会被内核重载拒掉,用户看到的 + * 是一段原始解析错误而不是「谁在引用它」。 + */ +export function subRuleSetReferences(data: ConfigObject, providerName: string): string[] { + const tables = data['sub-rules'] + if (typeof tables !== 'object' || tables === null || Array.isArray(tables)) return [] + const hits: string[] = [] + for (const rules of Object.values(tables)) { + if (!Array.isArray(rules)) continue + for (const rule of rules) { + if (typeof rule !== 'string') continue + const parsed = parseRule(rule) + if (parsed.type.toUpperCase() === 'RULE-SET' && parsed.payload === providerName) { + hits.push(rule) + } + } + } + return hits +} + /** 配置里的 rule-providers 段。不是映射就当没有。 */ export function ruleProvidersOf(data: ConfigObject): Record { const value = data['rule-providers'] diff --git a/web/views/RulesPage.vue b/web/views/RulesPage.vue index 84b21c0..97beecb 100644 --- a/web/views/RulesPage.vue +++ b/web/views/RulesPage.vue @@ -23,6 +23,7 @@ import { rulesOf, setRuleProviders, setRules, + subRuleSetReferences, } from '@shared/rules.ts' import type { IniImportPlan, RuleIssue } from '@shared/rules.ts' import type { ConfigObject } from '../../vendor/config-editor/index.ts' @@ -290,9 +291,14 @@ function onBlockedBackdrop(event: MouseEvent) { } function askRemoveProvider(name: string) { - const refs = ruleSetReferences(rules.value, name) + // 附加规则(sub-rules)里的引用也要查:漏了的话删除会被内核重载拒掉, + // 用户看到的是解析错误原文而不是「谁在引用它」 + const refs = [ + ...ruleSetReferences(rules.value, name).map((i) => rules.value[i]!), + ...subRuleSetReferences(props.data, name), + ] if (refs.length > 0) { - blockedProvider.value = { name, refs: refs.map((i) => rules.value[i]!) } + blockedProvider.value = { name, refs } } else { removingProvider.value = name }