mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-09-06 10:27:12 +00:00
fix(node): keep an adopted inbound alias across a remote id cache refresh
AdoptInboundAlias maps a central tag onto a node inbound that carries a different name, recording the pairing in both remoteIDByTag and adoptedAliases. refreshRemoteIDs then rebuilt remoteIDByTag from the tags the node reports and nothing else, so the central-tag entry was dropped on the next cache miss for any other tag. After that every op on the adopted inbound failed to resolve, and UpdateInbound falls back to AddInbound — creating a duplicate inbound on the node at the same port. cacheGetTag only recovers an n<id>- prefix flip, never an arbitrary alias, so the pairing could not be rediscovered until a master restart. The rebuild now re-applies adoptedAliases onto the fresh map, which keeps the map the single place a tag is resolved from.
This commit is contained in:
@@ -404,6 +404,13 @@ func (r *Remote) refreshRemoteIDs(ctx context.Context) error {
|
|||||||
next[ib.Tag] = ib.Id
|
next[ib.Tag] = ib.Id
|
||||||
}
|
}
|
||||||
r.mu.Lock()
|
r.mu.Lock()
|
||||||
|
// A rebuild sees only node-reported tags, so the adopted aliases must be
|
||||||
|
// re-applied or a later op on an adopted inbound re-creates it as a duplicate.
|
||||||
|
for centralTag, nodeTag := range r.adoptedAliases {
|
||||||
|
if id, ok := next[nodeTag]; ok {
|
||||||
|
next[centralTag] = id
|
||||||
|
}
|
||||||
|
}
|
||||||
r.remoteIDByTag = next
|
r.remoteIDByTag = next
|
||||||
r.mu.Unlock()
|
r.mu.Unlock()
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -440,3 +440,35 @@ func TestSanitizeStreamSettingsForRemote(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// An adopted alias maps a central tag onto a differently-named node inbound.
|
||||||
|
// refreshRemoteIDs rebuilds the cache from node-reported tags only, so the
|
||||||
|
// alias must be re-applied or every later op on that inbound misses.
|
||||||
|
func TestRemoteAdoptedAliasSurvivesRefresh(t *testing.T) {
|
||||||
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
if req.URL.Path == "/panel/api/inbounds/list" {
|
||||||
|
_, _ = w.Write([]byte(`{"success":true,"obj":[{"id":5,"tag":"legacy-in"},{"id":6,"tag":"in-2"}]}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
http.NotFound(w, req)
|
||||||
|
}))
|
||||||
|
defer srv.Close()
|
||||||
|
|
||||||
|
r := NewRemote(nodeForPlainServer(t, srv, "verify", "tok"), nil)
|
||||||
|
central := &model.Inbound{Tag: "central-in", Settings: `{"clients":[]}`}
|
||||||
|
r.AdoptInboundAlias(central, RemoteInboundOption{Id: 5, Tag: "legacy-in"})
|
||||||
|
|
||||||
|
// Resolving a different tag misses the cache and forces a full refresh.
|
||||||
|
if _, err := r.resolveRemoteID(context.Background(), "in-2"); err != nil {
|
||||||
|
t.Fatalf("resolveRemoteID(in-2): %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := r.resolveRemoteID(context.Background(), central.Tag)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("resolveRemoteID(%s) after refresh: %v", central.Tag, err)
|
||||||
|
}
|
||||||
|
if id != 5 {
|
||||||
|
t.Fatalf("adopted alias resolved to %d, want 5", id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user