Explorar o código

refactor(inbound-tag): add short protocol segment, rename tcpudp suffix

Tag shape becomes "[n<id>-]inbound-[<listen>:]<port>-<proto>-<net>"
where <proto> is a 2-char alias (vmess→vm, vless→vl, trojan→tr,
shadowsocks→ss, mixed→mx, wireguard→wg, hysteria→hy, tunnel→tn;
http stays as "http"), and <net> uses "tcpudp" for the TCP+UDP combo
instead of the previous "mixed" (which clashed visually with the
mixed protocol name).

Examples:
  local VLESS TCP 443        → inbound-443-vl-tcp
  local Hysteria UDP 443     → inbound-443-hy-udp
  local Mixed protocol dual  → inbound-22912-mx-tcpudp
  local Tunnel allow=tcp,udp → inbound-51542-tn-tcpudp
  node 1 VLESS TCP 443       → n1-inbound-443-vl-tcp

protocolShortName returns the raw protocol identifier for anything not
in the table, so future protocols still get a tag without a code edit.
Existing inbound tags are left alone — only newly generated tags adopt
the shape.
MHSanaei hai 4 horas
pai
achega
3046d96145
Modificáronse 2 ficheiros con 77 adicións e 51 borrados
  1. 44 12
      web/service/port_conflict.go
  2. 33 39
      web/service/port_conflict_test.go

+ 44 - 12
web/service/port_conflict.go

@@ -223,14 +223,14 @@ func sameNode(a, b *int) bool {
 	return *a == *b
 }
 
-// baseInboundTag is the legacy "inbound-<port>" / "inbound-<listen>:<port>"
-// shape still emitted by node-side xray imports that pre-date the
-// transport-aware naming; kept as a probe shape in setRemoteTrafficLocked.
+// baseInboundTag is the "in-<port>" / "in-<listen>:<port>" core used
+// by composeInboundTag and as a probe shape in setRemoteTrafficLocked
+// for node-side xray imports that pre-date the canonical naming.
 func baseInboundTag(listen string, port int) string {
 	if isAnyListen(listen) {
-		return fmt.Sprintf("inbound-%v", port)
+		return fmt.Sprintf("in-%v", port)
 	}
-	return fmt.Sprintf("inbound-%v:%v", listen, port)
+	return fmt.Sprintf("in-%v:%v", listen, port)
 }
 
 func transportTagSuffix(b transportBits) string {
@@ -240,7 +240,7 @@ func transportTagSuffix(b transportBits) string {
 	case transportUDP:
 		return "udp"
 	case transportTCP | transportUDP:
-		return "mixed"
+		return "tcpudp"
 	}
 	return "any"
 }
@@ -255,12 +255,44 @@ func nodeTagPrefix(nodeID *int) string {
 	return fmt.Sprintf("n%d-", *nodeID)
 }
 
+// protocolShortName collapses the full protocol identifier into a 2–4
+// char tag-friendly token (shadowsocks → ss, wireguard → wg, …). Falls
+// back to the raw identifier for anything not in the table so future
+// protocols don't need a code change just to get a tag.
+func protocolShortName(p model.Protocol) string {
+	switch p {
+	case model.VMESS:
+		return "vm"
+	case model.VLESS:
+		return "vl"
+	case model.Trojan:
+		return "tr"
+	case model.Shadowsocks:
+		return "ss"
+	case model.Mixed:
+		return "mx"
+	case model.WireGuard:
+		return "wg"
+	case model.Hysteria:
+		return "hy"
+	case model.Tunnel:
+		return "tn"
+	case model.HTTP:
+		return "http"
+	}
+	if p == "" {
+		return "any"
+	}
+	return string(p)
+}
+
 // composeInboundTag returns the canonical
-// "[n<id>-]inbound-[<listen>:]<port>-<transport>" shape used for every
-// newly created inbound. The transport segment lets tcp/443 and udp/443
-// coexist; the node prefix lets the same port live on local + node.
-func composeInboundTag(listen string, port int, nodeID *int, bits transportBits) string {
-	return nodeTagPrefix(nodeID) + baseInboundTag(listen, port) + "-" + transportTagSuffix(bits)
+// "[n<id>-]inbound-[<listen>:]<port>-<protocol>-<network>" shape used
+// for every newly created inbound. The protocol + network segments
+// disambiguate tcp/443 and udp/443 sharing a listener; the node prefix
+// lets the same port live on local + node.
+func composeInboundTag(listen string, port int, protocol model.Protocol, nodeID *int, bits transportBits) string {
+	return nodeTagPrefix(nodeID) + baseInboundTag(listen, port) + "-" + protocolShortName(protocol) + "-" + transportTagSuffix(bits)
 }
 
 // generateInboundTag returns a free tag in the canonical shape. ignoreId
@@ -269,7 +301,7 @@ func composeInboundTag(listen string, port int, nodeID *int, bits transportBits)
 // should have already blocked an exact-collision insert.
 func (s *InboundService) generateInboundTag(inbound *model.Inbound, ignoreId int) (string, error) {
 	bits := inboundTransports(inbound.Protocol, inbound.StreamSettings, inbound.Settings)
-	candidate := composeInboundTag(inbound.Listen, inbound.Port, inbound.NodeID, bits)
+	candidate := composeInboundTag(inbound.Listen, inbound.Port, inbound.Protocol, inbound.NodeID, bits)
 	exists, err := s.tagExists(candidate, ignoreId)
 	if err != nil {
 		return "", err

+ 33 - 39
web/service/port_conflict_test.go

@@ -269,13 +269,11 @@ func TestCheckPortConflict_ListenOverlapPreserved(t *testing.T) {
 	}
 }
 
-// when the base "inbound-<port>" tag is already taken on a coexisting
-// transport, generateInboundTag must disambiguate with a transport
-// suffix so the unique-tag DB constraint stays satisfied.
+// even with a stale legacy tag owning "in-443", a new UDP-side
+// inbound gets a fully qualified canonical tag and does not collide.
 func TestGenerateInboundTag_DisambiguatesByTransportOnSamePort(t *testing.T) {
 	setupConflictDB(t)
-	// existing tcp inbound owns "inbound-443".
-	seedInboundConflict(t, "inbound-443", "0.0.0.0", 443, model.VLESS, `{"network":"tcp"}`, `{}`)
+	seedInboundConflict(t, "in-443", "0.0.0.0", 443, model.VLESS, `{"network":"tcp"}`, `{}`)
 
 	svc := &InboundService{}
 	udp := &model.Inbound{
@@ -287,14 +285,13 @@ func TestGenerateInboundTag_DisambiguatesByTransportOnSamePort(t *testing.T) {
 	if err != nil {
 		t.Fatalf("generateInboundTag: %v", err)
 	}
-	if got != "inbound-443-udp" {
-		t.Fatalf("expected disambiguated tag inbound-443-udp, got %q", got)
+	if got != "in-443-hy-udp" {
+		t.Fatalf("expected in-443-hy-udp, got %q", got)
 	}
 }
 
-// when the port is free, the canonical tag includes the transport
-// suffix so tcp/8443 and udp/8443 get distinct tags out of the box
-// (no collision-driven retry needed at INSERT time).
+// when the port is free, the canonical tag carries protocol + transport
+// so tcp/8443 and udp/8443 get distinct tags out of the box.
 func TestGenerateInboundTag_KeepsBaseTagWhenFree(t *testing.T) {
 	setupConflictDB(t)
 
@@ -308,21 +305,19 @@ func TestGenerateInboundTag_KeepsBaseTagWhenFree(t *testing.T) {
 	if err != nil {
 		t.Fatalf("generateInboundTag: %v", err)
 	}
-	if got != "inbound-8443-tcp" {
-		t.Fatalf("expected inbound-8443-tcp, got %q", got)
+	if got != "in-8443-vl-tcp" {
+		t.Fatalf("expected in-8443-vl-tcp, got %q", got)
 	}
 }
 
-// updating an inbound on its own port must not flag its own tag as
-// taken, that's what ignoreId is for. Seeds with the canonical
-// "inbound-<port>-<transport>" shape so the self-update returns the
-// same tag verbatim.
+// updating an inbound on its own port must not flag its own tag as taken;
+// that's what ignoreId is for.
 func TestGenerateInboundTag_IgnoresSelfOnUpdate(t *testing.T) {
 	setupConflictDB(t)
-	seedInboundConflict(t, "inbound-443-tcp", "0.0.0.0", 443, model.VLESS, `{"network":"tcp"}`, `{}`)
+	seedInboundConflict(t, "in-443-vl-tcp", "0.0.0.0", 443, model.VLESS, `{"network":"tcp"}`, `{}`)
 
 	var existing model.Inbound
-	if err := database.GetDB().Where("tag = ?", "inbound-443-tcp").First(&existing).Error; err != nil {
+	if err := database.GetDB().Where("tag = ?", "in-443-vl-tcp").First(&existing).Error; err != nil {
 		t.Fatalf("read seeded row: %v", err)
 	}
 
@@ -331,16 +326,15 @@ func TestGenerateInboundTag_IgnoresSelfOnUpdate(t *testing.T) {
 	if err != nil {
 		t.Fatalf("generateInboundTag: %v", err)
 	}
-	if got != "inbound-443-tcp" {
+	if got != "in-443-vl-tcp" {
 		t.Fatalf("self-update must keep base tag, got %q", got)
 	}
 }
 
-// specific listen address gets the listen-prefixed shape and same
-// disambiguation rules.
+// specific listen address gets the listen-prefixed shape and same suffix.
 func TestGenerateInboundTag_SpecificListenSameDisambiguation(t *testing.T) {
 	setupConflictDB(t)
-	seedInboundConflict(t, "inbound-1.2.3.4:443", "1.2.3.4", 443, model.VLESS, `{"network":"tcp"}`, `{}`)
+	seedInboundConflict(t, "in-1.2.3.4:443", "1.2.3.4", 443, model.VLESS, `{"network":"tcp"}`, `{}`)
 
 	svc := &InboundService{}
 	udp := &model.Inbound{
@@ -352,8 +346,8 @@ func TestGenerateInboundTag_SpecificListenSameDisambiguation(t *testing.T) {
 	if err != nil {
 		t.Fatalf("generateInboundTag: %v", err)
 	}
-	if got != "inbound-1.2.3.4:443-udp" {
-		t.Fatalf("expected inbound-1.2.3.4:443-udp, got %q", got)
+	if got != "in-1.2.3.4:443-hy-udp" {
+		t.Fatalf("expected in-1.2.3.4:443-hy-udp, got %q", got)
 	}
 }
 
@@ -405,12 +399,12 @@ func TestCheckPortConflict_NodeScope(t *testing.T) {
 // panels diverged, causing a UNIQUE constraint failure on sync.
 func TestResolveInboundTag_RespectsCallerTagWhenFree(t *testing.T) {
 	setupConflictDB(t)
-	seedInboundConflictNode(t, "inbound-5000", "0.0.0.0", 5000, model.VLESS, `{"network":"tcp"}`, `{}`, nil)
-	seedInboundConflictNode(t, "inbound-5000-udp", "0.0.0.0", 5000, model.Hysteria, ``, ``, nil)
+	seedInboundConflictNode(t, "in-5000-vl-tcp", "0.0.0.0", 5000, model.VLESS, `{"network":"tcp"}`, `{}`, nil)
+	seedInboundConflictNode(t, "in-5000-hy-udp", "0.0.0.0", 5000, model.Hysteria, ``, ``, nil)
 
 	svc := &InboundService{}
 	pushed := &model.Inbound{
-		Tag:            "inbound-5000-tcp",
+		Tag:            "custom-pushed-tag",
 		Listen:         "0.0.0.0",
 		Port:           5000,
 		Protocol:       model.VLESS,
@@ -421,14 +415,14 @@ func TestResolveInboundTag_RespectsCallerTagWhenFree(t *testing.T) {
 	if err != nil {
 		t.Fatalf("resolveInboundTag: %v", err)
 	}
-	if got != "inbound-5000-tcp" {
+	if got != "custom-pushed-tag" {
 		t.Fatalf("caller tag must be preserved when free, got %q", got)
 	}
 }
 
 // when the caller leaves Tag empty (the local UI path) resolveInboundTag
 // falls back to generateInboundTag, which emits the canonical
-// "inbound-<port>-<transport>" shape.
+// "in-<port>-<transport>" shape.
 func TestResolveInboundTag_GeneratesWhenTagEmpty(t *testing.T) {
 	setupConflictDB(t)
 
@@ -442,8 +436,8 @@ func TestResolveInboundTag_GeneratesWhenTagEmpty(t *testing.T) {
 	if err != nil {
 		t.Fatalf("resolveInboundTag: %v", err)
 	}
-	if got != "inbound-8443-tcp" {
-		t.Fatalf("expected generated inbound-8443-tcp, got %q", got)
+	if got != "in-8443-vl-tcp" {
+		t.Fatalf("expected generated in-8443-vl-tcp, got %q", got)
 	}
 }
 
@@ -454,11 +448,11 @@ func TestResolveInboundTag_GeneratesWhenTagEmpty(t *testing.T) {
 // tag that the central will pick up via the AddInbound response.
 func TestResolveInboundTag_RegeneratesOnCollision(t *testing.T) {
 	setupConflictDB(t)
-	seedInboundConflictNode(t, "inbound-5000-tcp", "0.0.0.0", 5000, model.VLESS, `{"network":"tcp"}`, `{}`, nil)
+	seedInboundConflictNode(t, "in-5000-vl-tcp", "0.0.0.0", 5000, model.VLESS, `{"network":"tcp"}`, `{}`, nil)
 
 	svc := &InboundService{}
 	pushed := &model.Inbound{
-		Tag:            "inbound-5000-tcp",
+		Tag:            "in-5000-vl-tcp",
 		Listen:         "0.0.0.0",
 		Port:           5000,
 		Protocol:       model.Hysteria,
@@ -469,7 +463,7 @@ func TestResolveInboundTag_RegeneratesOnCollision(t *testing.T) {
 	if err != nil {
 		t.Fatalf("resolveInboundTag: %v", err)
 	}
-	if got == "inbound-5000-tcp" {
+	if got == "in-5000-vl-tcp" {
 		t.Fatalf("colliding caller tag must be replaced, but resolver kept %q", got)
 	}
 }
@@ -492,8 +486,8 @@ func TestGenerateInboundTag_NodePrefix(t *testing.T) {
 	if err != nil {
 		t.Fatalf("generateInboundTag: %v", err)
 	}
-	if got != "n1-inbound-443-tcp" {
-		t.Fatalf("expected n1-inbound-443-tcp, got %q", got)
+	if got != "n1-in-443-vl-tcp" {
+		t.Fatalf("expected n1-in-443-vl-tcp, got %q", got)
 	}
 }
 
@@ -501,7 +495,7 @@ func TestGenerateInboundTag_NodePrefix(t *testing.T) {
 // the prefix scopes the tag to that specific node.
 func TestGenerateInboundTag_NodePrefixedDoesNotCollideWithLocal(t *testing.T) {
 	setupConflictDB(t)
-	seedInboundConflict(t, "inbound-443-tcp", "0.0.0.0", 443, model.VLESS, `{"network":"tcp"}`, `{}`)
+	seedInboundConflict(t, "in-443-vl-tcp", "0.0.0.0", 443, model.VLESS, `{"network":"tcp"}`, `{}`)
 
 	svc := &InboundService{}
 	in := &model.Inbound{
@@ -514,8 +508,8 @@ func TestGenerateInboundTag_NodePrefixedDoesNotCollideWithLocal(t *testing.T) {
 	if err != nil {
 		t.Fatalf("generateInboundTag: %v", err)
 	}
-	if got != "n1-inbound-443-tcp" {
-		t.Fatalf("expected n1-inbound-443-tcp, got %q", got)
+	if got != "n1-in-443-vl-tcp" {
+		t.Fatalf("expected n1-in-443-vl-tcp, got %q", got)
 	}
 }