Ver código fonte

fix(node): let a node-reported tag outrank a stale adopted alias

The alias re-application added in 0775fcaa wrote every adoptedAliases entry
onto the rebuilt map unconditionally, so an alias could override the id the
node itself reported for that same central tag. adoptedAliases is never
pruned — cacheDel clears remoteIDByTag and pushedFP only — so the entry
outlives the pairing that created it.

That inverts the intended precedence: once a push renames a node inbound to
the central tag, the node reports it directly, and a stale alias pointing at
some other inbound reusing the old name would win. Every state-changing op on
that inbound then targets the wrong one, overwriting or deleting an inbound
the operator created separately.

The alias now only fills a gap: a central tag the node already reports is
left alone.
Sanaei 19 horas atrás
pai
commit
5fc4b9f463
2 arquivos alterados com 37 adições e 3 exclusões
  1. 5 0
      internal/web/runtime/remote.go
  2. 32 3
      internal/web/runtime/remote_test.go

+ 5 - 0
internal/web/runtime/remote.go

@@ -407,6 +407,11 @@ func (r *Remote) refreshRemoteIDs(ctx context.Context) error {
 	// 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 {
+		// A tag the node reports itself is authoritative; the alias only fills
+		// the gap left for a central tag the node knows under another name.
+		if _, reported := next[centralTag]; reported {
+			continue
+		}
 		if id, ok := next[nodeTag]; ok {
 			next[centralTag] = id
 		}

+ 32 - 3
internal/web/runtime/remote_test.go

@@ -441,9 +441,8 @@ 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.
+// refreshRemoteIDs rebuilds the cache from node-reported tags only, so an
+// adopted 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")
@@ -472,3 +471,33 @@ func TestRemoteAdoptedAliasSurvivesRefresh(t *testing.T) {
 		t.Fatalf("adopted alias resolved to %d, want 5", id)
 	}
 }
+
+// A stale alias must never outrank the node's own report: once the node lists
+// an inbound under the central tag itself, that id is the authoritative one.
+func TestRemoteAdoptedAliasYieldsToNodeReportedTag(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":"central-in"},{"id":7,"tag":"legacy-in"},{"id":9,"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: 7, Tag: "legacy-in"})
+
+	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): %v", central.Tag, err)
+	}
+	if id != 5 {
+		t.Fatalf("central tag resolved to %d via a stale alias, want 5 (the id the node reports)", id)
+	}
+}