fix: drop nodes that fail geo detection in collection rename
Nodes whose country cannot be detected are now filtered out instead of kept with their original name. This prevents unlabelled/unrenamed nodes from appearing in collection output.
This commit is contained in:
@@ -30,17 +30,18 @@ func resolveRenameOptions(opts *model.RenameOptions) model.RenameOptions {
|
|||||||
|
|
||||||
// RenameCollectionNodes renames proxy nodes in the collection-level format:
|
// RenameCollectionNodes renames proxy nodes in the collection-level format:
|
||||||
//
|
//
|
||||||
// [emoji country flag] [alias] [country name] [city] [-1...-N]
|
// [emoji country flag] [alias] [country name] [city] [01...NN]
|
||||||
//
|
//
|
||||||
// The alias is read from each node's "_sourceAlias" field (tagged by the
|
// The alias is read from each node's "_sourceAlias" field (tagged by the
|
||||||
// subscription service from the source's Alias). Geographic info (flag,
|
// subscription service from the source's Alias). Geographic info (flag,
|
||||||
// country, city) is auto-detected from the node's original name and server.
|
// country, city) is auto-detected from the node's original name and server.
|
||||||
//
|
//
|
||||||
// Nodes are grouped by their **display baseName** (the name without the index
|
// Nodes are grouped by their **display baseName** (the name without the index
|
||||||
// suffix) and numbered sequentially within each group, starting at 1. This
|
// suffix) and numbered sequentially within each group, starting at 1.
|
||||||
// ensures that nodes producing the same display name get unique suffixes
|
//
|
||||||
// (-1, -2, -3, …) in a single pass, so EnsureUniqueProxyNames never needs to
|
// Nodes whose geographic info cannot be detected (no country found) are
|
||||||
// append a secondary suffix.
|
// dropped from the output — they cannot be meaningfully renamed and would
|
||||||
|
// appear as unlabelled entries in the collection.
|
||||||
//
|
//
|
||||||
// opts controls which fields are included in the output. A nil opts means all
|
// opts controls which fields are included in the output. A nil opts means all
|
||||||
// fields are included (full default format).
|
// fields are included (full default format).
|
||||||
@@ -54,13 +55,12 @@ func RenameCollectionNodes(proxies []model.ProxyNode, opts *model.RenameOptions)
|
|||||||
// First pass: compute display baseName (without index) for each node.
|
// First pass: compute display baseName (without index) for each node.
|
||||||
type nodeInfo struct {
|
type nodeInfo struct {
|
||||||
proxy model.ProxyNode
|
proxy model.ProxyNode
|
||||||
baseName string // display name without index suffix; "" means skip rename
|
baseName string // display name without index suffix; "" means geo failed
|
||||||
}
|
}
|
||||||
|
|
||||||
infos := make([]nodeInfo, len(proxies))
|
infos := make([]nodeInfo, 0, len(proxies))
|
||||||
for i, proxy := range proxies {
|
for _, proxy := range proxies {
|
||||||
if proxy == nil {
|
if proxy == nil {
|
||||||
infos[i] = nodeInfo{proxy: proxy}
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ func RenameCollectionNodes(proxies []model.ProxyNode, opts *model.RenameOptions)
|
|||||||
|
|
||||||
geo := util.DetectGeoWithServer(name, server)
|
geo := util.DetectGeoWithServer(name, server)
|
||||||
if geo.CountryName == "" {
|
if geo.CountryName == "" {
|
||||||
infos[i] = nodeInfo{proxy: proxy}
|
// Geo detection failed — skip this node
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,34 +96,22 @@ func RenameCollectionNodes(proxies []model.ProxyNode, opts *model.RenameOptions)
|
|||||||
parts = append(parts, geo.CityCN)
|
parts = append(parts, geo.CityCN)
|
||||||
}
|
}
|
||||||
|
|
||||||
infos[i] = nodeInfo{
|
infos = append(infos, nodeInfo{
|
||||||
proxy: proxy,
|
proxy: proxy,
|
||||||
baseName: strings.Join(parts, " "),
|
baseName: strings.Join(parts, " "),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count group sizes by baseName.
|
// Count group sizes by baseName.
|
||||||
baseCounts := make(map[string]int)
|
baseCounts := make(map[string]int)
|
||||||
for _, info := range infos {
|
for _, info := range infos {
|
||||||
if info.baseName != "" {
|
baseCounts[info.baseName]++
|
||||||
baseCounts[info.baseName]++
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Second pass: assign names with sequential numbering.
|
// Second pass: assign names with sequential numbering.
|
||||||
counters := make(map[string]int)
|
counters := make(map[string]int)
|
||||||
results := make([]model.ProxyNode, len(proxies))
|
results := make([]model.ProxyNode, 0, len(infos))
|
||||||
for i, info := range infos {
|
for _, info := range infos {
|
||||||
if info.proxy == nil {
|
|
||||||
results[i] = info.proxy
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if info.baseName == "" {
|
|
||||||
// Geo detection failed — keep original name.
|
|
||||||
results[i] = info.proxy
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
counters[info.baseName]++
|
counters[info.baseName]++
|
||||||
total := baseCounts[info.baseName]
|
total := baseCounts[info.baseName]
|
||||||
|
|
||||||
@@ -135,7 +123,7 @@ func RenameCollectionNodes(proxies []model.ProxyNode, opts *model.RenameOptions)
|
|||||||
next := cloneProxy(info.proxy)
|
next := cloneProxy(info.proxy)
|
||||||
delete(next, "_sourceAlias")
|
delete(next, "_sourceAlias")
|
||||||
next["name"] = name
|
next["name"] = name
|
||||||
results[i] = next
|
results = append(results, next)
|
||||||
}
|
}
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|||||||
@@ -110,12 +110,8 @@ func TestRenameCollectionNodes_UnknownCountry(t *testing.T) {
|
|||||||
{"name": "Unknown Node", "type": "ss", "_sourceAlias": "A"},
|
{"name": "Unknown Node", "type": "ss", "_sourceAlias": "A"},
|
||||||
}
|
}
|
||||||
result := RenameCollectionNodes(proxies, nil)
|
result := RenameCollectionNodes(proxies, nil)
|
||||||
if len(result) != 1 {
|
if len(result) != 0 {
|
||||||
t.Fatalf("expected 1 node, got %d", len(result))
|
t.Fatalf("expected 0 nodes (geo detection failed, should be filtered), got %d", len(result))
|
||||||
}
|
|
||||||
// Should keep original name
|
|
||||||
if result[0]["name"] != "Unknown Node" {
|
|
||||||
t.Errorf("expected original name, got %q", result[0]["name"])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user