From e20d73ba7eee7b9fc3df8acdfcfc2fb686689987 Mon Sep 17 00:00:00 2001 From: Amirmohammad Sadat Shokouhi Date: Mon, 11 May 2026 09:53:30 +0330 Subject: [PATCH] add loopback and dns servers tag to inbound lists in RuleFormModal (#4244) * add loopback and dns servers tag to inbound lists in RuleFormModal * fix: remove clientIp from dns section when its empty --- frontend/src/pages/xray/DnsTab.vue | 29 +++++++++++++++--- frontend/src/pages/xray/OutboundFormModal.vue | 7 ++--- frontend/src/pages/xray/OutboundsTab.vue | 11 +------ frontend/src/pages/xray/RoutingTab.vue | 30 ++++++++++++++----- 4 files changed, 50 insertions(+), 27 deletions(-) diff --git a/frontend/src/pages/xray/DnsTab.vue b/frontend/src/pages/xray/DnsTab.vue index 8deae677..e1be1912 100644 --- a/frontend/src/pages/xray/DnsTab.vue +++ b/frontend/src/pages/xray/DnsTab.vue @@ -22,6 +22,14 @@ const props = defineProps({ const STRATEGIES = ['UseSystem', 'UseIP', 'UseIPv4', 'UseIPv6']; +const dnsFieldOmit = Object.freeze(Object.create(null)); + +function dnsValueEmptyForOmit(v) { + if (v === undefined || v === null) return true; + if (typeof v === 'string') return v.trim() === ''; + return false; +} + const enableDNS = computed({ get: () => !!props.templateSettings?.dns, set: (next) => { @@ -29,7 +37,6 @@ const enableDNS = computed({ if (next) { props.templateSettings.dns = { tag: 'dns_inbound', - clientIp: '', queryStrategy: 'UseIP', disableCache: false, disableFallback: false, @@ -50,16 +57,30 @@ const enableDNS = computed({ }); function dnsField(field, fallback) { + const omitWhenUnset = fallback === dnsFieldOmit; return computed({ - get: () => props.templateSettings?.dns?.[field] ?? fallback, + get: () => { + const raw = props.templateSettings?.dns?.[field]; + if (fallback === dnsFieldOmit) return raw ?? ''; + return raw ?? fallback; + }, set: (v) => { - if (props.templateSettings?.dns) props.templateSettings.dns[field] = v; + if (!props.templateSettings?.dns) return; + if (omitWhenUnset) { + if (dnsValueEmptyForOmit(v)) { + if (field in props.templateSettings.dns) delete props.templateSettings.dns[field]; + } else { + props.templateSettings.dns[field] = v; + } + } else { + props.templateSettings.dns[field] = v; + } }, }); } const dnsTag = dnsField('tag', 'dns_inbound'); -const dnsClientIp = dnsField('clientIp', ''); +const dnsClientIp = dnsField('clientIp', dnsFieldOmit); const dnsStrategy = dnsField('queryStrategy', 'UseIP'); const dnsDisableCache = dnsField('disableCache', false); const dnsDisableFallback = dnsField('disableFallback', false); diff --git a/frontend/src/pages/xray/OutboundFormModal.vue b/frontend/src/pages/xray/OutboundFormModal.vue index 155ff86f..4310b067 100644 --- a/frontend/src/pages/xray/OutboundFormModal.vue +++ b/frontend/src/pages/xray/OutboundFormModal.vue @@ -34,7 +34,6 @@ const props = defineProps({ open: { type: Boolean, default: false }, outbound: { type: Object, default: null }, existingTags: { type: Array, default: () => [] }, - inboundTags: { type: Array, default: () => [] }, }); const emit = defineEmits(['update:open', 'confirm']); @@ -318,10 +317,8 @@ function regenerateWgKeys() { diff --git a/frontend/src/pages/xray/OutboundsTab.vue b/frontend/src/pages/xray/OutboundsTab.vue index 11f3b898..71139a2f 100644 --- a/frontend/src/pages/xray/OutboundsTab.vue +++ b/frontend/src/pages/xray/OutboundsTab.vue @@ -35,15 +35,6 @@ const props = defineProps({ isMobile: { type: Boolean, default: false }, }); -const inboundTagOptions = computed(() => { - const out = new Set(); - for (const ib of props.templateSettings?.inbounds || []) { - if (ib.tag) out.add(ib.tag); - } - for (const t of props.inboundTags || []) out.add(t); - return [...out]; -}); - const emit = defineEmits(['reset-traffic', 'test', 'test-all', 'show-warp', 'show-nord', 'delete']); const testMode = ref('tcp'); @@ -443,7 +434,7 @@ const rows = computed(() => { + @confirm="onConfirm" /> diff --git a/frontend/src/pages/xray/RoutingTab.vue b/frontend/src/pages/xray/RoutingTab.vue index 27ae41d3..e7ce5344 100644 --- a/frontend/src/pages/xray/RoutingTab.vue +++ b/frontend/src/pages/xray/RoutingTab.vue @@ -65,18 +65,32 @@ const editingRule = ref(null); const editingIndex = ref(null); const inboundTagOptions = computed(() => { - const out = new Set(); + const seen = new Set(); + const out = []; + + function pushUnique(tag) { + if (!tag) return; + if (seen.has(tag)) return; + seen.add(tag); + out.push(tag); + } + for (const ib of props.templateSettings?.inbounds || []) { - if (ib.tag) out.add(ib.tag); + pushUnique(ib.tag); + } + for (const t of props.inboundTags || []) { + pushUnique(t); } - for (const t of props.inboundTags || []) out.add(t); for (const ob of props.templateSettings?.outbounds || []) { - const rt = ob?.reverse?.tag || ob?.settings?.reverse?.tag; - if (rt) out.add(rt); + const rt = ob?.reverse?.tag || ob?.settings?.reverse?.tag || ob?.settings?.inboundTag; + pushUnique(rt); } - // dnsTag if DNS is configured. - const dt = props.templateSettings?.dns?.tag; - if (dt) out.add(dt); + pushUnique(props.templateSettings?.dns?.tag); + + for (const s of props.templateSettings?.dns?.servers || []) { + if (typeof s === 'object' && s?.tag) pushUnique(s.tag); + } + return [...out]; });