Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 41 additions & 1 deletion models/proxy_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ func (pc *ProxyConfig) GenerateStableID() string {
var idComponents []string

idComponents = append(idComponents, pc.Protocol)

idComponents = append(idComponents, pc.Server)
idComponents = append(idComponents, fmt.Sprintf("%d", pc.Port))
idComponents = append(idComponents, pc.Name)
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GenerateStableID() now incorporates pc.Name. Since Name is a user-facing label that can change without the underlying proxy transport/credentials changing (e.g., subscription providers often rename entries), this will change stable IDs and can trigger unnecessary config reloads / break any consumers that expect the ID to be stable across renames. Consider removing Name from the identity hash (or introducing a separate, explicitly "display" field) so identity is derived only from connection-defining fields.

Suggested change
idComponents = append(idComponents, pc.Name)

Copilot uses AI. Check for mistakes.

switch pc.Protocol {
case "vless", "vmess":
Expand All @@ -96,6 +96,14 @@ func (pc *ProxyConfig) GenerateStableID() string {
}
}

if pc.Flow != "" {
idComponents = append(idComponents, pc.Flow)
}

if pc.Encryption != "" {
idComponents = append(idComponents, pc.Encryption)
}

if pc.SNI != "" {
idComponents = append(idComponents, pc.SNI)
}
Expand All @@ -112,6 +120,38 @@ func (pc *ProxyConfig) GenerateStableID() string {
idComponents = append(idComponents, pc.PublicKey)
}

if pc.Fingerprint != "" {
idComponents = append(idComponents, pc.Fingerprint)
Comment on lines 120 to +124
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GenerateStableID() is now used for config change detection (IsConfigsEqual) and for disambiguating entries, but it still omits some connection-impacting fields that are parsed and used in Xray config generation (e.g., AllowInsecure is set from subscription links and used in xray/config.go TLS stream settings). If AllowInsecure changes, IsConfigsEqual may incorrectly treat configs as unchanged and skip reconfiguration. Consider including AllowInsecure (and any other stream-settings fields that affect the generated Xray config) in the stable ID input.

Copilot uses AI. Check for mistakes.
}

if pc.ShortID != "" {
idComponents = append(idComponents, pc.ShortID)
}

if pc.HeaderType != "" {
idComponents = append(idComponents, pc.HeaderType)
}

if pc.Path != "" {
idComponents = append(idComponents, pc.Path)
}

if pc.Host != "" {
idComponents = append(idComponents, pc.Host)
}

if pc.Mode != "" {
idComponents = append(idComponents, pc.Mode)
}

if pc.ServiceName != "" {
idComponents = append(idComponents, pc.ServiceName)
}

if len(pc.ALPN) > 0 {
idComponents = append(idComponents, strings.Join(pc.ALPN, ","))
}

idString := strings.Join(idComponents, "|")

Comment on lines +151 to 156
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idString is built by joining components with the literal "|", but several newly-added components (e.g., Name, Path, Host, ServiceName, potentially ALPN entries) can legally contain |. That makes the concatenation ambiguous and can reintroduce collisions where different component lists produce the same idString. Use an unambiguous encoding (e.g., length-prefix each component, or JSON encode a struct/array) before hashing.

Copilot uses AI. Check for mistakes.
hash := sha256.Sum256([]byte(idString))
Expand Down
35 changes: 19 additions & 16 deletions xray/utils.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
package xray

import (
"fmt"
"xray-checker/models"
)

func PrepareProxyConfigs(proxies []*models.ProxyConfig) {
seenIDs := make(map[string]int, len(proxies))

for i := range proxies {
proxies[i].Index = i

if proxies[i].StableID == "" {
proxies[i].StableID = proxies[i].GenerateStableID()
baseID := proxies[i].GenerateStableID()
seenIDs[baseID]++

if seenIDs[baseID] == 1 {
proxies[i].StableID = baseID
continue
}

proxies[i].StableID = fmt.Sprintf("%s-%d", baseID, seenIDs[baseID])
}
}

Expand All @@ -19,31 +28,25 @@ func IsConfigsEqual(old, new []*models.ProxyConfig) bool {
return false
}

oldMap := make(map[string]bool)
newMap := make(map[string]bool)
oldCounts := make(map[string]int, len(old))
newCounts := make(map[string]int, len(new))

for _, cfg := range old {
if cfg.StableID == "" {
cfg.StableID = cfg.GenerateStableID()
}
oldMap[cfg.StableID] = true
oldCounts[cfg.GenerateStableID()]++
}

for _, cfg := range new {
if cfg.StableID == "" {
cfg.StableID = cfg.GenerateStableID()
}
newMap[cfg.StableID] = true
newCounts[cfg.GenerateStableID()]++
Comment on lines 34 to +39
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IsConfigsEqual now compares configs via GenerateStableID() counts. If GenerateStableID() includes mutable/non-identity fields (notably Name per this PR), a pure rename will be treated as a config change and will trigger a full reconfigure/restart. Consider comparing on a dedicated identity function that excludes display-only fields, or ensure GenerateStableID() is strictly connection-defining.

Copilot uses AI. Check for mistakes.
}

for id := range oldMap {
if !newMap[id] {
for id, count := range oldCounts {
if newCounts[id] != count {
return false
}
}

for id := range newMap {
if !oldMap[id] {
for id, count := range newCounts {
if oldCounts[id] != count {
return false
}
}
Expand Down
Loading