feat: evolve rate-filter to a more generic filter

This commit is contained in:
2026-07-28 20:40:12 +08:00
parent a3e3079af1
commit b206594eb8
15 changed files with 492 additions and 193 deletions

View File

@ -374,10 +374,15 @@ func (m *Manager) mergeSubscriptions(subs []*model.Subscription) (map[string]int
data = map[string]interface{}{}
}
proxies = append(proxies, automation.PrefixProxyNames(sub.Name, data)...)
proxyRefs, groupRefs := automation.PrefixProxyNames(sub.Name, data)
proxies = append(proxies, proxyRefs...)
if group := popFirstProxyGroup(data); group != nil {
selectionGroups = append(selectionGroups, group)
groupRefs = rewritePoppedGroupRef(groupRefs)
}
proxies = append(proxies, groupRefs...)
combined = deepMerge(combined, data)
}
@ -405,7 +410,7 @@ func dedupeMatchRule(rulesRaw interface{}) interface{} {
filtered = append(filtered, item)
}
return append(filtered, "MATCH,all proxies")
return append(filtered, "MATCH,Proxy Selection")
}
// dedupeGeoIPRule keeps only the last occurrence of each "GEOIP,<payload>"

View File

@ -51,6 +51,23 @@ func popFirstProxyGroup(data map[string]interface{}) map[string]interface{} {
// folding every merged subscription's default selector group together.
const selectionGroupName = "Proxy Selection"
// rewritePoppedGroupRef reflects, in a subscription's own proxy-group refs,
// that its first group was just popped off (via popFirstProxyGroup) to be
// folded into the shared selectionGroupName group by mergeProxySelection —
// so an ssm rule matching this subscription's original default-selector
// name (e.g. via match.name-pattern) resolves to the DisplayName that will
// actually exist in the final config, not one that's about to disappear.
// groupRefs must be in the same order popFirstProxyGroup pops from (i.e.
// straight from automation.PrefixProxyNames), so index 0 is always the
// popped group. No-op if groupRefs is empty.
func rewritePoppedGroupRef(groupRefs []automation.ProxyRef) []automation.ProxyRef {
if len(groupRefs) == 0 {
return groupRefs
}
groupRefs[0].DisplayName = selectionGroupName
return groupRefs
}
// mergeProxySelection collapses each subscription's default selector group
// (already popped off by popFirstProxyGroup) into a single combined group
// named selectionGroupName, covering every subscription's proxies, and

View File

@ -3,6 +3,8 @@ package corecfg
import (
"reflect"
"testing"
"gitea.epss.net.cn/klesh/ss/internal/automation"
)
func TestPopFirstProxyGroup(t *testing.T) {
@ -72,6 +74,31 @@ func TestMergeProxySelection(t *testing.T) {
}
}
func TestRewritePoppedGroupRef(t *testing.T) {
groupRefs := []automation.ProxyRef{
{OriginalName: "Auto", DisplayName: "sub-a | Auto", Subscription: "sub-a"},
{OriginalName: "AnotherGroup", DisplayName: "sub-a | AnotherGroup", Subscription: "sub-a"},
}
got := rewritePoppedGroupRef(groupRefs)
if len(got) != 2 {
t.Fatalf("expected 2 refs, got %#v", got)
}
if got[0].DisplayName != selectionGroupName || got[0].OriginalName != "Auto" {
t.Fatalf("expected the popped group's DisplayName rewritten to %q, got %#v", selectionGroupName, got[0])
}
if got[1].DisplayName != "sub-a | AnotherGroup" {
t.Fatalf("expected the non-popped group left untouched, got %#v", got[1])
}
}
func TestRewritePoppedGroupRefEmpty(t *testing.T) {
if got := rewritePoppedGroupRef(nil); len(got) != 0 {
t.Fatalf("expected no-op on empty input, got %#v", got)
}
}
func TestMergeProxySelectionNoGroups(t *testing.T) {
combined := map[string]interface{}{"proxy-groups": []interface{}{"unchanged"}}
mergeProxySelection(combined, nil)