diff --git a/frontend/src/lib/xray/inbound-tag.ts b/frontend/src/lib/xray/inbound-tag.ts index 6daef0cf..8f4901ae 100644 --- a/frontend/src/lib/xray/inbound-tag.ts +++ b/frontend/src/lib/xray/inbound-tag.ts @@ -49,12 +49,8 @@ function transportTagSuffix(bits: TransportBits): string { return 'any'; } -function isAnyListen(listen: string): boolean { - return listen === '' || listen === '0.0.0.0' || listen === '::' || listen === '::0'; -} - -function baseInboundTag(listen: string, port: number): string { - return isAnyListen(listen) ? `in-${port}` : `in-${listen}:${port}`; +function baseInboundTag(port: number): string { + return `in-${port}`; } function nodeTagPrefix(nodeId: number | null | undefined): string { @@ -62,7 +58,6 @@ function nodeTagPrefix(nodeId: number | null | undefined): string { } export interface InboundTagInput { - listen: string; port: number; nodeId: number | null | undefined; protocol: string; @@ -74,7 +69,7 @@ export function composeInboundTag(input: InboundTagInput): string { const bits = inboundTransports(input.protocol, input.streamSettings, input.settings); return ( nodeTagPrefix(input.nodeId) - + baseInboundTag(input.listen ?? '', input.port ?? 0) + + baseInboundTag(input.port ?? 0) + '-' + transportTagSuffix(bits) ); diff --git a/frontend/src/pages/inbounds/form/InboundFormModal.tsx b/frontend/src/pages/inbounds/form/InboundFormModal.tsx index 28cf2460..958147e8 100644 --- a/frontend/src/pages/inbounds/form/InboundFormModal.tsx +++ b/frontend/src/pages/inbounds/form/InboundFormModal.tsx @@ -160,7 +160,6 @@ export default function InboundFormModal({ const security = Form.useWatch(['streamSettings', 'security'], form) ?? 'none'; const streamEnabled = canEnableStream({ protocol }); - const wListen = Form.useWatch('listen', form) ?? ''; const wPort = Form.useWatch('port', form); const wNodeId = Form.useWatch('nodeId', form) ?? null; const wTag = Form.useWatch('tag', form) ?? ''; @@ -169,7 +168,6 @@ export default function InboundFormModal({ const autoTagRef = useRef(true); const lastWrittenTagRef = useRef(''); const currentTagInput = (): InboundTagInput => ({ - listen: typeof wListen === 'string' ? wListen : '', port: typeof wPort === 'number' ? wPort : 0, nodeId: typeof wNodeId === 'number' ? wNodeId : null, protocol, @@ -293,7 +291,6 @@ export default function InboundFormModal({ form.setFieldsValue(initial); const initialTag = (initial.tag ?? '') as string; autoTagRef.current = isAutoInboundTag(initialTag, { - listen: initial.listen ?? '', port: initial.port ?? 0, nodeId: initial.nodeId ?? null, protocol: initial.protocol, @@ -329,7 +326,7 @@ export default function InboundFormModal({ form.setFieldValue('tag', next); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [open, wListen, wPort, wNodeId, protocol, network, mixedUdpOn, wSsNetwork, wTunnelNetwork]); + }, [open, wPort, wNodeId, protocol, network, mixedUdpOn, wSsNetwork, wTunnelNetwork]); // Why: protocol picker reset cascades through the form — clearing the // settings DU branch and dropping a nodeId that no longer applies. The diff --git a/frontend/src/test/inbound-tag.test.ts b/frontend/src/test/inbound-tag.test.ts index 7f87079a..e554210c 100644 --- a/frontend/src/test/inbound-tag.test.ts +++ b/frontend/src/test/inbound-tag.test.ts @@ -7,7 +7,6 @@ import { composeInboundTag, isAutoInboundTag, type InboundTagInput } from '@/lib // tag the backend re-derives on save. describe('composeInboundTag transport suffix parity', () => { const base = (over: Partial): InboundTagInput => ({ - listen: '0.0.0.0', port: 443, nodeId: null, protocol: 'vless', @@ -36,9 +35,9 @@ describe('composeInboundTag transport suffix parity', () => { expect(composeInboundTag(input)).toBe(want); }); - it('scopes a non-any listen and node prefix', () => { - expect(composeInboundTag(base({ listen: '127.0.0.1', port: 8443, streamSettings: { network: 'tcp' } }))) - .toBe('in-127.0.0.1:8443-tcp'); + it('ignores the listen address and adds the node prefix', () => { + expect(composeInboundTag(base({ port: 8443, streamSettings: { network: 'tcp' } }))) + .toBe('in-8443-tcp'); expect(composeInboundTag(base({ nodeId: 1, port: 443, streamSettings: { network: 'tcp' } }))) .toBe('n1-in-443-tcp'); }); @@ -47,7 +46,7 @@ describe('composeInboundTag transport suffix parity', () => { // Parity with TestIsAutoGeneratedTag. describe('isAutoInboundTag', () => { const input: InboundTagInput = { - listen: '0.0.0.0', port: 443, nodeId: null, protocol: 'vless', streamSettings: { network: 'tcp' }, + port: 443, nodeId: null, protocol: 'vless', streamSettings: { network: 'tcp' }, }; it('recognises canonical, dedup-suffixed and empty as auto', () => { diff --git a/web/service/inbound.go b/web/service/inbound.go index 5210b140..a5d17681 100644 --- a/web/service/inbound.go +++ b/web/service/inbound.go @@ -762,7 +762,7 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound, tag := oldInbound.Tag oldBits := inboundTransports(oldInbound.Protocol, oldInbound.StreamSettings, oldInbound.Settings) - oldTagWasAuto := isAutoGeneratedTag(tag, oldInbound.Listen, oldInbound.Port, oldInbound.NodeID, oldBits) + oldTagWasAuto := isAutoGeneratedTag(tag, oldInbound.Port, oldInbound.NodeID, oldBits) db := database.GetDB() tx := db.Begin() diff --git a/web/service/port_conflict.go b/web/service/port_conflict.go index b7db89b3..40b3c2ea 100644 --- a/web/service/port_conflict.go +++ b/web/service/port_conflict.go @@ -171,11 +171,8 @@ func sameNode(a, b *int) bool { return *a == *b } -func baseInboundTag(listen string, port int) string { - if isAnyListen(listen) { - return fmt.Sprintf("in-%v", port) - } - return fmt.Sprintf("in-%v:%v", listen, port) +func baseInboundTag(port int) string { + return fmt.Sprintf("in-%v", port) } func transportTagSuffix(b transportBits) string { @@ -200,12 +197,12 @@ func nodeTagPrefix(nodeID *int) string { return fmt.Sprintf("n%d-", *nodeID) } -func composeInboundTag(listen string, port int, nodeID *int, bits transportBits) string { - return nodeTagPrefix(nodeID) + baseInboundTag(listen, port) + "-" + transportTagSuffix(bits) +func composeInboundTag(port int, nodeID *int, bits transportBits) string { + return nodeTagPrefix(nodeID) + baseInboundTag(port) + "-" + transportTagSuffix(bits) } -func isAutoGeneratedTag(tag, listen string, port int, nodeID *int, bits transportBits) bool { - base := composeInboundTag(listen, port, nodeID, bits) +func isAutoGeneratedTag(tag string, port int, nodeID *int, bits transportBits) bool { + base := composeInboundTag(port, nodeID, bits) if tag == base { return true } @@ -223,7 +220,7 @@ func isAutoGeneratedTag(tag, listen string, port int, nodeID *int, bits transpor 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.Port, inbound.NodeID, bits) exists, err := s.tagExists(candidate, ignoreId) if err != nil { return "", err diff --git a/web/service/port_conflict_test.go b/web/service/port_conflict_test.go index 13f81ad5..542adaaa 100644 --- a/web/service/port_conflict_test.go +++ b/web/service/port_conflict_test.go @@ -331,10 +331,11 @@ func TestGenerateInboundTag_IgnoresSelfOnUpdate(t *testing.T) { } } -// specific listen address gets the listen-prefixed shape and same suffix. -func TestGenerateInboundTag_SpecificListenSameDisambiguation(t *testing.T) { +// the listen address never appears in the tag; the transport suffix still +// keeps a udp inbound distinct from a tcp one on the same port. +func TestGenerateInboundTag_ListenIgnoredTransportDisambiguates(t *testing.T) { setupConflictDB(t) - seedInboundConflict(t, "in-1.2.3.4:443", "1.2.3.4", 443, model.VLESS, `{"network":"tcp"}`, `{}`) + seedInboundConflict(t, "in-443-tcp", "1.2.3.4", 443, model.VLESS, `{"network":"tcp"}`, `{}`) svc := &InboundService{} udp := &model.Inbound{ @@ -346,8 +347,8 @@ func TestGenerateInboundTag_SpecificListenSameDisambiguation(t *testing.T) { if err != nil { t.Fatalf("generateInboundTag: %v", err) } - if got != "in-1.2.3.4:443-udp" { - t.Fatalf("expected in-1.2.3.4:443-udp, got %q", got) + if got != "in-443-udp" { + t.Fatalf("expected in-443-udp, got %q", got) } } @@ -644,26 +645,25 @@ func TestIsAutoGeneratedTag(t *testing.T) { cases := []struct { name string tag string - listen string port int nodeID *int bits transportBits want bool }{ - {"canonical", "in-443-tcp", "0.0.0.0", 443, nil, tcp, true}, - {"canonical udp", "in-443-udp", "0.0.0.0", 443, nil, transportUDP, true}, - {"dedup suffix", "in-443-tcp-2", "0.0.0.0", 443, nil, tcp, true}, - {"listen scoped", "in-127.0.0.1:443-tcp", "127.0.0.1", 443, nil, tcp, true}, - {"node prefixed", "n1-in-443-tcp", "0.0.0.0", 443, intPtr(1), tcp, true}, - {"custom tag", "my-cool-tag", "0.0.0.0", 443, nil, tcp, false}, - {"stale port", "in-443-tcp", "0.0.0.0", 8443, nil, tcp, false}, - {"stale transport", "in-443-tcp", "0.0.0.0", 443, nil, transportUDP, false}, - {"non-numeric suffix", "in-443-tcp-x", "0.0.0.0", 443, nil, tcp, false}, - {"empty suffix", "in-443-tcp-", "0.0.0.0", 443, nil, tcp, false}, + {"canonical", "in-443-tcp", 443, nil, tcp, true}, + {"canonical udp", "in-443-udp", 443, nil, transportUDP, true}, + {"dedup suffix", "in-443-tcp-2", 443, nil, tcp, true}, + {"node prefixed", "n1-in-443-tcp", 443, intPtr(1), tcp, true}, + {"legacy listen-scoped is now custom", "in-127.0.0.1:443-tcp", 443, nil, tcp, false}, + {"custom tag", "my-cool-tag", 443, nil, tcp, false}, + {"stale port", "in-443-tcp", 8443, nil, tcp, false}, + {"stale transport", "in-443-tcp", 443, nil, transportUDP, false}, + {"non-numeric suffix", "in-443-tcp-x", 443, nil, tcp, false}, + {"empty suffix", "in-443-tcp-", 443, nil, tcp, false}, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { - if got := isAutoGeneratedTag(c.tag, c.listen, c.port, c.nodeID, c.bits); got != c.want { + if got := isAutoGeneratedTag(c.tag, c.port, c.nodeID, c.bits); got != c.want { t.Fatalf("isAutoGeneratedTag(%q) = %v, want %v", c.tag, got, c.want) } })