diff --git a/frontend/src/models/outbound.js b/frontend/src/models/outbound.js
index ca5f5d36..645f6100 100644
--- a/frontend/src/models/outbound.js
+++ b/frontend/src/models/outbound.js
@@ -12,6 +12,7 @@ export const Protocols = {
Hysteria: "hysteria",
Socks: "socks",
HTTP: "http",
+ Loopback: "loopback",
};
export const SSMethods = {
@@ -1586,6 +1587,7 @@ Outbound.Settings = class extends CommonClass {
case Protocols.HTTP: return new Outbound.HttpSettings();
case Protocols.Wireguard: return new Outbound.WireguardSettings();
case Protocols.Hysteria: return new Outbound.HysteriaSettings();
+ case Protocols.Loopback: return new Outbound.LoopbackSettings();
default: return null;
}
}
@@ -1603,6 +1605,7 @@ Outbound.Settings = class extends CommonClass {
case Protocols.HTTP: return Outbound.HttpSettings.fromJson(json);
case Protocols.Wireguard: return Outbound.WireguardSettings.fromJson(json);
case Protocols.Hysteria: return Outbound.HysteriaSettings.fromJson(json);
+ case Protocols.Loopback: return Outbound.LoopbackSettings.fromJson(json);
default: return null;
}
}
@@ -1782,6 +1785,23 @@ Outbound.BlackholeSettings = class extends CommonClass {
}
};
+Outbound.LoopbackSettings = class extends CommonClass {
+ constructor(inboundTag = '') {
+ super();
+ this.inboundTag = inboundTag;
+ }
+
+ static fromJson(json = {}) {
+ return new Outbound.LoopbackSettings(json.inboundTag || '');
+ }
+
+ toJson() {
+ return {
+ inboundTag: this.inboundTag || undefined,
+ };
+ }
+};
+
Outbound.DNSRule = class extends CommonClass {
constructor(action = 'direct', qtype = '', domain = '') {
super();
diff --git a/frontend/src/pages/xray/OutboundFormModal.vue b/frontend/src/pages/xray/OutboundFormModal.vue
index 324d91d2..b49c3c31 100644
--- a/frontend/src/pages/xray/OutboundFormModal.vue
+++ b/frontend/src/pages/xray/OutboundFormModal.vue
@@ -34,6 +34,7 @@ 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']);
@@ -199,6 +200,7 @@ const isBlackhole = computed(() => proto.value === Protocols.Blackhole);
const isDNS = computed(() => proto.value === Protocols.DNS);
const isWireguard = computed(() => proto.value === Protocols.Wireguard);
const isHysteria = computed(() => proto.value === Protocols.Hysteria);
+const isLoopback = computed(() => proto.value === Protocols.Loopback);
function regenerateWgKeys() {
if (!outbound.value?.settings) return;
@@ -311,6 +313,16 @@ function regenerateWgKeys() {
+
+
+
+
+
+
+
diff --git a/frontend/src/pages/xray/OutboundsTab.vue b/frontend/src/pages/xray/OutboundsTab.vue
index cef4ab4a..53e25cc9 100644
--- a/frontend/src/pages/xray/OutboundsTab.vue
+++ b/frontend/src/pages/xray/OutboundsTab.vue
@@ -35,9 +35,19 @@ const props = defineProps({
templateSettings: { type: Object, default: null },
outboundsTraffic: { type: Array, default: () => [] },
outboundTestStates: { type: Object, default: () => ({}) },
+ inboundTags: { type: Array, default: () => [] },
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', 'show-warp', 'show-nord']);
// === Modal state ====================================================
@@ -129,7 +139,9 @@ function outboundAddresses(o) {
}
function isUntestable(o) {
- return o.protocol === 'blackhole' || o.tag === 'blocked';
+ return o.protocol === Protocols.Blackhole
+ || o.protocol === Protocols.Loopback
+ || o.tag === 'blocked';
}
function isTesting(idx) {
return !!props.outboundTestStates?.[idx]?.testing;
@@ -377,6 +389,7 @@ const rows = computed(() => {
v-model:open="modalOpen"
:outbound="editingOutbound"
:existing-tags="existingTags"
+ :inbound-tags="inboundTagOptions"
@confirm="onConfirm"
/>
diff --git a/frontend/src/pages/xray/XrayPage.vue b/frontend/src/pages/xray/XrayPage.vue
index bf5ed8b9..88085563 100644
--- a/frontend/src/pages/xray/XrayPage.vue
+++ b/frontend/src/pages/xray/XrayPage.vue
@@ -294,6 +294,7 @@ function confirmRestart() {
:template-settings="templateSettings"
:outbounds-traffic="outboundsTraffic"
:outbound-test-states="outboundTestStates"
+ :inbound-tags="inboundTags"
:is-mobile="isMobile"
@reset-traffic="resetOutboundsTraffic"
@test="onTestOutbound"