From b79abc8bc99e64af29116af07891a7f4c7056b3a Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Thu, 14 May 2026 20:32:38 +0200 Subject: [PATCH 01/11] refactor: remove legacy advancedJson state --- .../src/pages/inbounds/InboundFormModal.vue | 358 ++++++++++++++++-- 1 file changed, 327 insertions(+), 31 deletions(-) diff --git a/frontend/src/pages/inbounds/InboundFormModal.vue b/frontend/src/pages/inbounds/InboundFormModal.vue index c01b8323..45351144 100644 --- a/frontend/src/pages/inbounds/InboundFormModal.vue +++ b/frontend/src/pages/inbounds/InboundFormModal.vue @@ -70,8 +70,11 @@ const FLOW_OPTIONS = Object.values(TLS_FLOW_CONTROL); const inbound = ref(null); const dbForm = ref(null); const saving = ref(false); -const advancedJson = ref({ stream: '', sniffing: '', settings: '' }); +const advancedStreamText = ref(''); +const advancedSniffingText = ref(''); +const advancedSettingsText = ref(''); const activeTabKey = ref('basic'); +const advancedSectionKey = ref('all'); // Cached default cert/key paths from /panel/setting/defaultSettings — // powers the "Set default cert" button on the TLS form. const defaultCert = ref(''); @@ -224,13 +227,13 @@ function freshDbForm() { function primeAdvancedJson() { if (!inbound.value) return; try { - advancedJson.value.stream = JSON.stringify(JSON.parse(inbound.value.stream.toString()), null, 2); + advancedStreamText.value = JSON.stringify(JSON.parse(inbound.value.stream.toString()), null, 2); } catch (_e) { /* keep prior text */ } try { - advancedJson.value.sniffing = JSON.stringify(JSON.parse(inbound.value.sniffing.toString()), null, 2); + advancedSniffingText.value = JSON.stringify(JSON.parse(inbound.value.sniffing.toString()), null, 2); } catch (_e) { /* keep prior text */ } try { - advancedJson.value.settings = JSON.stringify(JSON.parse(inbound.value.settings.toString()), null, 2); + advancedSettingsText.value = JSON.stringify(JSON.parse(inbound.value.settings.toString()), null, 2); } catch (_e) { /* keep prior text */ } } @@ -244,6 +247,7 @@ watch(() => props.open, (next) => { primeAdvancedJson(); } activeTabKey.value = 'basic'; + advancedSectionKey.value = 'all'; fetchDefaultCertSettings(); }); @@ -253,18 +257,18 @@ function applyAdvancedJsonToBasic() { let parsedStream; let parsedSniffing; try { - parsedSettings = advancedJson.value.settings.trim() - ? JSON.parse(advancedJson.value.settings) + parsedSettings = advancedSettingsText.value.trim() + ? JSON.parse(advancedSettingsText.value) : inbound.value.settings?.toJson?.(); } catch (e) { message.error(`Settings JSON invalid: ${e.message}`); return false; } try { - parsedStream = advancedJson.value.stream.trim() - ? JSON.parse(advancedJson.value.stream) + parsedStream = advancedStreamText.value.trim() + ? JSON.parse(advancedStreamText.value) : inbound.value.stream?.toJson?.(); } catch (e) { message.error(`Stream JSON invalid: ${e.message}`); return false; } try { - parsedSniffing = advancedJson.value.sniffing.trim() - ? JSON.parse(advancedJson.value.sniffing) + parsedSniffing = advancedSniffingText.value.trim() + ? JSON.parse(advancedSniffingText.value) : inbound.value.sniffing?.toJson?.(); } catch (e) { message.error(`Sniffing JSON invalid: ${e.message}`); return false; } @@ -324,6 +328,203 @@ function onNetworkChange(next) { } } +function parseAdvancedSliceOrFallback(rawText, fallbackValue) { + if (!rawText?.trim()) return fallbackValue; + return JSON.parse(rawText); +} + +function unwrapWrappedObject(parsed, key) { + if ( + parsed + && typeof parsed === 'object' + && !Array.isArray(parsed) + && parsed[key] !== undefined + ) { + return parsed[key]; + } + return parsed; +} + +const advancedAllConfig = computed({ + get: () => { + if (!inbound.value) return ''; + try { + const settings = parseAdvancedSliceOrFallback( + advancedSettingsText.value, + inbound.value.settings?.toJson?.() || {}, + ); + const streamSettings = parseAdvancedSliceOrFallback( + advancedStreamText.value, + inbound.value.stream?.toJson?.() || {}, + ); + const sniffing = parseAdvancedSliceOrFallback( + advancedSniffingText.value, + inbound.value.sniffing?.toJson?.() || {}, + ); + return JSON.stringify({ + listen: inbound.value.listen, + port: inbound.value.port, + protocol: inbound.value.protocol, + settings, + sniffing, + streamSettings, + tag: inbound.value.tag, + }, null, 2); + } catch (_e) { + return ''; + } + }, + set: (next) => { + let parsed; + try { + parsed = JSON.parse(next); + } catch (e) { + message.error(`All JSON invalid: ${e.message}`); + return; + } + if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) { + message.error('All JSON must be an inbound object.'); + return; + } + + try { + if (typeof parsed.listen === 'string') { + inbound.value.listen = parsed.listen; + } + if (parsed.port !== undefined) { + const parsedPort = Number(parsed.port); + if (!Number.isNaN(parsedPort) && Number.isFinite(parsedPort)) { + inbound.value.port = parsedPort; + } + } + if (typeof parsed.protocol === 'string' && PROTOCOLS.includes(parsed.protocol)) { + inbound.value.protocol = parsed.protocol; + } + if (typeof parsed.tag === 'string') { + inbound.value.tag = parsed.tag; + } + + const existingSettings = parseAdvancedSliceOrFallback( + advancedSettingsText.value, + inbound.value?.settings?.toJson?.() || {}, + ); + const settings = parsed.settings ?? existingSettings; + const streamSettings = parsed.streamSettings ?? (inbound.value?.stream?.toJson?.() || {}); + const sniffing = parsed.sniffing ?? (inbound.value?.sniffing?.toJson?.() || {}); + advancedSettingsText.value = JSON.stringify(settings, null, 2); + advancedStreamText.value = JSON.stringify(streamSettings, null, 2); + advancedSniffingText.value = JSON.stringify(sniffing, null, 2); + } catch (e) { + message.error(`All JSON invalid: ${e.message}`); + } + }, +}); + +const advancedSettingsConfig = computed({ + get: () => { + if (!inbound.value) return ''; + try { + const settings = parseAdvancedSliceOrFallback( + advancedSettingsText.value, + inbound.value.settings?.toJson?.() || {}, + ); + return JSON.stringify({ + settings, + }, null, 2); + } catch (_e) { + return ''; + } + }, + set: (next) => { + let parsed; + try { + parsed = JSON.parse(next); + } catch (e) { + message.error(`Settings JSON invalid: ${e.message}`); + return; + } + const unwrapped = unwrapWrappedObject(parsed, 'settings'); + if (!unwrapped || typeof unwrapped !== 'object' || Array.isArray(unwrapped)) { + message.error('Settings JSON must be an object or { settings: { ... } }.'); + return; + } + + try { + advancedSettingsText.value = JSON.stringify(unwrapped, null, 2); + } catch (e) { + message.error(`Settings JSON invalid: ${e.message}`); + } + }, +}); + +const advancedSniffingConfig = computed({ + get: () => { + if (!inbound.value) return ''; + try { + const sniffing = parseAdvancedSliceOrFallback( + advancedSniffingText.value, + inbound.value.sniffing?.toJson?.() || {}, + ); + return JSON.stringify({ sniffing }, null, 2); + } catch (_e) { + return ''; + } + }, + set: (next) => { + let parsed; + try { + parsed = JSON.parse(next); + } catch (e) { + message.error(`Sniffing JSON invalid: ${e.message}`); + return; + } + const unwrapped = unwrapWrappedObject(parsed, 'sniffing'); + if (!unwrapped || typeof unwrapped !== 'object' || Array.isArray(unwrapped)) { + message.error('Sniffing JSON must be an object or { sniffing: { ... } }.'); + return; + } + try { + advancedSniffingText.value = JSON.stringify(unwrapped, null, 2); + } catch (e) { + message.error(`Sniffing JSON invalid: ${e.message}`); + } + }, +}); + +const advancedStreamConfig = computed({ + get: () => { + if (!inbound.value) return ''; + try { + const streamSettings = parseAdvancedSliceOrFallback( + advancedStreamText.value, + inbound.value.stream?.toJson?.() || {}, + ); + return JSON.stringify({ streamSettings }, null, 2); + } catch (_e) { + return ''; + } + }, + set: (next) => { + let parsed; + try { + parsed = JSON.parse(next); + } catch (e) { + message.error(`Stream JSON invalid: ${e.message}`); + return; + } + const unwrapped = unwrapWrappedObject(parsed, 'streamSettings'); + if (!unwrapped || typeof unwrapped !== 'object' || Array.isArray(unwrapped)) { + message.error('Stream JSON must be an object or { streamSettings: { ... } }.'); + return; + } + try { + advancedStreamText.value = JSON.stringify(unwrapped, null, 2); + } catch (e) { + message.error(`Stream JSON invalid: ${e.message}`); + } + }, +}); + // === Random helpers wired to the form's sync icons ================== function randomEmail(target) { if (target) target.email = RandomUtil.randomLowerAndNum(9); @@ -525,16 +726,16 @@ async function submit() { let settings; try { streamSettings = canEnableStream.value - ? JSON.stringify(JSON.parse(advancedJson.value.stream)) + ? JSON.stringify(JSON.parse(advancedStreamText.value)) : (inbound.value.stream?.sockopt ? JSON.stringify({ sockopt: inbound.value.stream.sockopt.toJson() }) : ''); } catch (e) { message.error(`Stream JSON invalid: ${e.message}`); return; } try { - sniffing = JSON.stringify(JSON.parse(advancedJson.value.sniffing || inbound.value.sniffing.toString())); + sniffing = JSON.stringify(JSON.parse(advancedSniffingText.value || inbound.value.sniffing.toString())); } catch (e) { message.error(`Sniffing JSON invalid: ${e.message}`); return; } try { - settings = JSON.stringify(JSON.parse(advancedJson.value.settings || inbound.value.settings.toString())); + settings = JSON.stringify(JSON.parse(advancedSettingsText.value || inbound.value.settings.toString())); } catch (e) { message.error(`Settings JSON invalid: ${e.message}`); return; } // The structured form mutates `inbound.stream` directly when the @@ -598,7 +799,7 @@ watch( () => { if (!inbound.value?.stream) return; try { - advancedJson.value.stream = JSON.stringify(JSON.parse(inbound.value.stream.toString()), null, 2); + advancedStreamText.value = JSON.stringify(JSON.parse(inbound.value.stream.toString()), null, 2); } catch (_e) { /* leave as is */ } }, ); @@ -607,7 +808,7 @@ watch( () => { if (!inbound.value?.sniffing) return; try { - advancedJson.value.sniffing = JSON.stringify(JSON.parse(inbound.value.sniffing.toString()), null, 2); + advancedSniffingText.value = JSON.stringify(JSON.parse(inbound.value.sniffing.toString()), null, 2); } catch (_e) { /* leave as is */ } }, ); @@ -616,7 +817,7 @@ watch( () => { if (!inbound.value?.settings) return; try { - advancedJson.value.settings = JSON.stringify(JSON.parse(inbound.value.settings.toString()), null, 2); + advancedSettingsText.value = JSON.stringify(JSON.parse(inbound.value.settings.toString()), null, 2); } catch (_e) { /* leave as is */ } }, ); @@ -1005,7 +1206,7 @@ watch( @@ -1944,20 +2145,48 @@ watch( - - - - - - - - - - - - +
+
+
+
+
Inbound JSON sections
+
+ Full inbound JSON and focused editors for settings, sniffing, and streamSettings. +
+
+
+ + + +
+ Full inbound object with all fields in one editor. +
+ +
+ +
+ Xray settings block wrapper: + { settings: { ... } }. +
+ +
+ +
+ Xray sniffing block wrapper: + { sniffing: { ... } }. +
+ +
+ +
+ Xray stream block wrapper: + { streamSettings: { ... } }. +
+ +
+
+
+
@@ -2033,6 +2262,73 @@ watch( margin-top: 4px; } +.advanced-shell { + display: flex; + flex-direction: column; + gap: 12px; +} + +.advanced-panel { + padding: 14px; + border: 1px solid rgba(128, 128, 128, 0.18); + border-radius: 12px; + background: rgba(128, 128, 128, 0.04); +} + +.advanced-panel__header { + margin-bottom: 12px; +} + +.advanced-panel__title { + font-size: 14px; + font-weight: 600; + line-height: 1.4; +} + +.advanced-panel__subtitle { + margin-top: 4px; + color: rgba(0, 0, 0, 0.6); + line-height: 1.5; +} + +.advanced-inner-tabs :deep(.ant-tabs-nav) { + margin-bottom: 12px; +} + +.advanced-inner-tabs :deep(.ant-tabs-tab) { + padding-inline: 14px; +} + +.advanced-editor-meta { + margin-bottom: 10px; + color: rgba(0, 0, 0, 0.65); + line-height: 1.5; +} + +@media (max-width: 768px) { + .advanced-panel { + padding: 12px; + border-radius: 10px; + } + + .advanced-inner-tabs :deep(.ant-tabs-tab) { + padding-inline: 10px; + } +} + +:global(.dark) .advanced-panel__subtitle, +:global(.dark) .advanced-editor-meta, +:global(.ultra) .advanced-panel__subtitle, +:global(.ultra) .advanced-editor-meta { + color: rgba(255, 255, 255, 0.65); +} + +:global(.dark) .advanced-panel, +:global(.ultra) .advanced-panel { + border-color: rgba(255, 255, 255, 0.12); + background: rgba(255, 255, 255, 0.03); +} + .section-heading { font-weight: 500; margin: 12px 0 6px; From 6badd829df54744b7813a4188f441c755a18605b Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Thu, 14 May 2026 23:18:23 +0200 Subject: [PATCH 02/11] Remove streamSettings for protocols that don't support it - Frontend: Only include streamSettings in toJson() for vmess, vless, trojan, shadowsocks, and hysteria protocols - Frontend: Hide Stream tab in Advanced section for unsupported protocols - Frontend: Clear streamSettings in Advanced tab when switching to unsupported protocols - Frontend: Add CodeMirror JSON editor to config view in index page with mobile responsive design - Backend: Add normalizeStreamSettings() to clear streamSettings for tunnel, mixed, http, tun, and wireguard protocols - Backend: Apply normalization in AddInbound() and UpdateInbound() - Backend: Add omitempty JSON tag to StreamSettings field to exclude null values from Xray config --- frontend/package-lock.json | 156 +++++++++--------- frontend/src/models/inbound.js | 17 +- .../src/pages/inbounds/InboundFormModal.vue | 52 +++++- frontend/src/pages/index/IndexPage.vue | 42 ++++- web/service/inbound.go | 23 +++ xray/config.go | 12 +- xray/inbound.go | 4 +- 7 files changed, 200 insertions(+), 106 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index aa68ab6d..7da007d9 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -210,9 +210,9 @@ } }, "node_modules/@codemirror/view": { - "version": "6.42.1", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.42.1.tgz", - "integrity": "sha512-ToN3oFc0nsxNUYVF5P0ztLgbC4UPPjPtA9aKYhkOKQaZASpOUo6ISXyQLP66ctVwlDc+j6Jv0uK5IFALkiXztg==", + "version": "6.43.0", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.0.tgz", + "integrity": "sha512-V7ZCLQO3Jus9hzh2jVCCPW3mO4IBMr43O37PqSUYautJSnnJF41YlgLw21x0fLJTYvJ+Vkm6Gp+qKGH9pltgXA==", "license": "MIT", "dependencies": { "@codemirror/state": "^6.6.0", @@ -610,9 +610,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.129.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.129.0.tgz", - "integrity": "sha512-3oz8m3FGdr2nDXVqmFUw7jolKliC4MoyXYIG2c7gpjBnzUWQpUGIYcXYKxTdTi+N2jusvt610ckTMkxdwHkYEg==", + "version": "0.130.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.130.0.tgz", + "integrity": "sha512-ibD2usx9JRu7f5pu2tMKMI4cpA4NgXJQoYRP4pQ7Pxmn1l6k/53qWtQWZayhYy3X4QZkt90Ot+mJEaeXouio6Q==", "dev": true, "license": "MIT", "funding": { @@ -620,9 +620,9 @@ } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0.tgz", - "integrity": "sha512-TWMZnRLMe63C2Lhyicviu7ZHaU4kxa6PS3rofvc9GmcvptzNN11BcfQ4Sl7MwTOsisQoa2keB/EBdNCAnUo8vA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.1.tgz", + "integrity": "sha512-fJI3I0r3C3Oj/zdBCpaCmBRZYf07xpaq4yCfDDoSFm+beWNzbIl26puW8RraUdugoJw/95zerNOn6jasAhzSmg==", "cpu": [ "arm64" ], @@ -637,9 +637,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0.tgz", - "integrity": "sha512-6XcD+8k0gPVItNagEw78/qqcBDwKcwDYS8V2hRmVsfUSIrd8cWe/CBvRDI5toqFyPfj+FJr6t8U6Xj2P2prEew==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.1.tgz", + "integrity": "sha512-cKnAhWEsV7TPcA/5EAteDp6KcJZBQ2G+BqE7zayMMi7kMvwRsbv7WT9aOnn0WNl4SKEIf43vjS31iUPu80nzXg==", "cpu": [ "arm64" ], @@ -654,9 +654,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0.tgz", - "integrity": "sha512-iN/tWVXRQDWvmZlKdceP1Dwug9GDpEymhb9p4xnEe6zvCg5lFmzVljl+1qR1NVx3yfGpr2Na+CuLmv5IU8uzfQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.1.tgz", + "integrity": "sha512-YKrVwQjIRBPo+5G/u03wGjbdy4q7pyzCe93DK9VJ7zkVmeg8LJ7GbgsiHWdR4xSoe4CAXRD7Bcjgbtr64bkXNg==", "cpu": [ "x64" ], @@ -671,9 +671,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0.tgz", - "integrity": "sha512-jjQMDvvwSOuhOwMszD/klSOjyWMM3zI64hWTj9KT5x4MxRbZAf+7vLQ6qouRhtsLVFHr3f0ILaJAfgENPiQdAQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.1.tgz", + "integrity": "sha512-z/oBsREo46SsFqBwYtFe0kpJeBijAT48O/WXLI4suiCLBkr03RTtTJMCzSdDd2znlh8VJizL09XVkQgk8IZonw==", "cpu": [ "x64" ], @@ -688,9 +688,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0.tgz", - "integrity": "sha512-d//Dtg2x6/m3mbV64yUGNnDGNZaDGRpDLLNGerHQUVObuNaIQaaDp25yUiqGXtHEXX+NP2d0wAlmKgpYgIAJ2A==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.1.tgz", + "integrity": "sha512-ik8q7GM11zxvYxFc2PeDcT6TBvhCQMaUxfph/M5l9sKuTs/Sjg3L+Byw0F7w0ZVLBZmx30P+gG0ECzzN+MFcmQ==", "cpu": [ "arm" ], @@ -705,9 +705,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0.tgz", - "integrity": "sha512-n7Ofp0mx+aB2cC+Sdy5YtMnXtY9lchnHbY+3Yt0uq9JsWQExf4f5Whu0tK0R8Jdc9S6RchTHjIFY7uc92puOVQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.1.tgz", + "integrity": "sha512-QoSx2EkyrrdZ6kcyE8stqZ62t0Yra8Fs5ia9lOxJrh6TMQJK7gQKmscdTHf7pOXKREKrVwOtJcQG3qVSfc866A==", "cpu": [ "arm64" ], @@ -725,9 +725,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0.tgz", - "integrity": "sha512-EIVjy2cgd7uuMMo94FVkBp7F6DhcZAUwNURkSG3RwUmvAXR6s0ISxM81U+IydcZByPG0pZIHsf1b6kTxoFDgJA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.1.tgz", + "integrity": "sha512-uwNwFpwKeNiZawfAWBgg0VIztPTV3ihhh1vV334h9ivnNLorxnQMU6Fz8wG1Zb4Qh9LC1/MkcyT3YlDXG3Rsgg==", "cpu": [ "arm64" ], @@ -745,9 +745,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0.tgz", - "integrity": "sha512-JEwwOPcwTLAcpDQlqSmjEmfs63xJnSiUNIGvLcDLUHCWK4XowpS/7c7tUsUH6uT/ct6bMUTdXKfI8967FYj6mg==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.1.tgz", + "integrity": "sha512-zY1bul7OWr7DFBiJ++wofXvnr8B45ce3QsQUhKrIhXsygAh7bTkwyeM1bi1a2g5C/yC/N8TZyGDEoMfm/l9mpg==", "cpu": [ "ppc64" ], @@ -765,9 +765,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0.tgz", - "integrity": "sha512-0wjCFhLrihtAubnT9iA0N++0pSV0z5Hg7tNGdNJ4RFaINceHadoF+kiFGyY1qSSNVIAZtLotG8Ju1bgDPkjnFA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.1.tgz", + "integrity": "sha512-0frlsT/f4Ft6I7SMESTKnF3cZsdicQn1dCMkF/jT9wDLE+gGoiQfv1nmT9e+s7s/fekvvy6tZM2jHvI2tkbJDQ==", "cpu": [ "s390x" ], @@ -785,9 +785,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0.tgz", - "integrity": "sha512-Dfn7iak9BcMMePxcoJfpSbWqnEyrp/dRF63/8qW/eHBdOZov6x5aShLLEYGYdIeSJ6vMLK/XCVB+lGIxm41bQA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.1.tgz", + "integrity": "sha512-XABVmGp9Tg0WspTVvwduTc4fpqy6JnAUrSQe6OuyqD/03nI7r0O9OWUkMIwFrjKAIqolvqoA4ZrJppgwE0Gxmw==", "cpu": [ "x64" ], @@ -805,9 +805,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0.tgz", - "integrity": "sha512-5/utzzDmD/pD/bmuaUcbTf/sZYy0aztwIVlfpoW1fTjCZ0BaPOMVWGZL1zvgxyi7ZIVYWlxKONHmSbHuiOh8Jw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.1.tgz", + "integrity": "sha512-bV4fzswuzVcKD90o/VM6QqKxnxlDq0g2BISDLNVmxrnhpv1DDbyPhCIjYfvzYLV+MvkKKnQt2Q6AO86SEBULUQ==", "cpu": [ "x64" ], @@ -825,9 +825,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0.tgz", - "integrity": "sha512-ouJs8VcUomfLfpbUECqFMRqdV4x6aeAK3MA4m6vTrJJjKyWTV5KnxZx7Jd9G+GlDaQQxubcba00x16OyJ1meig==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.1.tgz", + "integrity": "sha512-/Mh0Zhq3OP7fVs0kcQHZP6lZEthMGTaSf8UBQYSFEZDWGXXlEC+nJ6EqenaK2t4LBXMe3A+K/G2BVXXdtOr4PQ==", "cpu": [ "arm64" ], @@ -842,9 +842,9 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0.tgz", - "integrity": "sha512-E+oHKGiDA+lsKMmFtffDDw91EryDT7uJocrIuCHqhm6bCTM6xFK+3gaCkYOHfPwQr0cCNarSM2xaELoQDz9jJg==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.1.tgz", + "integrity": "sha512-+1xc9X45l8ufsBAm6Gjvx2qDRIY9lTVt0cgWNcJ+1gdhXvkbxePA60yRTwSTuXL09CMhyJmjpV7E3NoyxbqFQQ==", "cpu": [ "wasm32" ], @@ -861,9 +861,9 @@ } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0.tgz", - "integrity": "sha512-yYK02n8Rngo+gbm1y6G0+7jk1sJ/2Wt7K0me0Y7k/ErBpyf+LJ2gFpqWVTcRV1rUepBlQRmpgWkTQCiiwrK0Ow==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.1.tgz", + "integrity": "sha512-1D+UqZdfnuR+Jy1GgMJwi85bD40H21uNmOPRWQhw4oRSuolZ/B5rixZ45DK2KXOTCvmVCecauWgEhbw8bI7tOw==", "cpu": [ "arm64" ], @@ -878,9 +878,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0.tgz", - "integrity": "sha512-14bpChMahXRRXiTwahSl+zzHPW6qQTXtkMuJBFlbo+pqSAews2d4BdCSHfrJ/MBsCZtpmTafsY+1QhBzitcmdg==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.1.tgz", + "integrity": "sha512-INAycaWuhlOK3wk4mRHGsdgwYWmd9cChdPdE9bwWmy6rn9VqVNYNFGhOdXrofXUxwHIncSiPNb8tNm8knDVIeQ==", "cpu": [ "x64" ], @@ -2715,14 +2715,14 @@ "license": "MIT" }, "node_modules/rolldown": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0.tgz", - "integrity": "sha512-yD986aXDESFGS95spT1LAv0jssywP4npMEjmMHyN2/5+eE8qQJUype2AaKkRiLgBgyD0LFlubwAht7VmY8rGoA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.1.tgz", + "integrity": "sha512-X0KQHljNnEkWNqqiz9zJrGunh1B0HgOxLXvnFpCOcadzcy5qohZ3tqMEUg00vncoRovXuK3ZqCT9KnnKzoInFQ==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.129.0", - "@rolldown/pluginutils": "1.0.0" + "@oxc-project/types": "=0.130.0", + "@rolldown/pluginutils": "^1.0.0" }, "bin": { "rolldown": "bin/cli.mjs" @@ -2731,27 +2731,27 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0", - "@rolldown/binding-darwin-arm64": "1.0.0", - "@rolldown/binding-darwin-x64": "1.0.0", - "@rolldown/binding-freebsd-x64": "1.0.0", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0", - "@rolldown/binding-linux-arm64-gnu": "1.0.0", - "@rolldown/binding-linux-arm64-musl": "1.0.0", - "@rolldown/binding-linux-ppc64-gnu": "1.0.0", - "@rolldown/binding-linux-s390x-gnu": "1.0.0", - "@rolldown/binding-linux-x64-gnu": "1.0.0", - "@rolldown/binding-linux-x64-musl": "1.0.0", - "@rolldown/binding-openharmony-arm64": "1.0.0", - "@rolldown/binding-wasm32-wasi": "1.0.0", - "@rolldown/binding-win32-arm64-msvc": "1.0.0", - "@rolldown/binding-win32-x64-msvc": "1.0.0" + "@rolldown/binding-android-arm64": "1.0.1", + "@rolldown/binding-darwin-arm64": "1.0.1", + "@rolldown/binding-darwin-x64": "1.0.1", + "@rolldown/binding-freebsd-x64": "1.0.1", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.1", + "@rolldown/binding-linux-arm64-gnu": "1.0.1", + "@rolldown/binding-linux-arm64-musl": "1.0.1", + "@rolldown/binding-linux-ppc64-gnu": "1.0.1", + "@rolldown/binding-linux-s390x-gnu": "1.0.1", + "@rolldown/binding-linux-x64-gnu": "1.0.1", + "@rolldown/binding-linux-x64-musl": "1.0.1", + "@rolldown/binding-openharmony-arm64": "1.0.1", + "@rolldown/binding-wasm32-wasi": "1.0.1", + "@rolldown/binding-win32-arm64-msvc": "1.0.1", + "@rolldown/binding-win32-x64-msvc": "1.0.1" } }, "node_modules/rolldown/node_modules/@rolldown/pluginutils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0.tgz", - "integrity": "sha512-aKs/3GSWyV0mrhNmt/96/Z3yczC3yvrzYATCiCXQebBsGyYzjNdUphRVLeJQ67ySKVXRfMxt2lm12pmXvbPFQQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", "dev": true, "license": "MIT" }, @@ -2964,16 +2964,16 @@ "license": "MIT" }, "node_modules/vite": { - "version": "8.0.12", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.12.tgz", - "integrity": "sha512-w2dDofOWv2QB09ZITZBsvKTVAlYvPR4IAmrY/v0ir9KvLs0xybR7i48wxhM1/oyBWO34wPns+bPGw5ZrZqDpZg==", + "version": "8.0.13", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.13.tgz", + "integrity": "sha512-MFtjBYgzmSxmgA4RAfjIyXWpGe1oALnjgUTzzV7QLx/TKxCzjtMH6Fd9/eVK+5Fg1qNoz5VAwsmMs/NofrmJvw==", "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.14", - "rolldown": "1.0.0", + "rolldown": "1.0.1", "tinyglobby": "^0.2.16" }, "bin": { diff --git a/frontend/src/models/inbound.js b/frontend/src/models/inbound.js index cd789023..830dc9a9 100644 --- a/frontend/src/models/inbound.js +++ b/frontend/src/models/inbound.js @@ -2412,20 +2412,25 @@ export class Inbound extends XrayCommonClass { } toJson() { - let streamSettings; - if (this.canEnableStream() || this.stream?.sockopt) { - streamSettings = this.stream.toJson(); - } - return { + // Only these protocols use streamSettings + const streamProtocols = [Protocols.VLESS, Protocols.VMESS, Protocols.TROJAN, Protocols.SHADOWSOCKS, Protocols.HYSTERIA]; + + const result = { port: this.port, listen: this.listen, protocol: this.protocol, settings: this.settings instanceof XrayCommonClass ? this.settings.toJson() : this.settings, - streamSettings: streamSettings, tag: this.tag, sniffing: this.sniffing.toJson(), clientStats: this.clientStats }; + + // Only add streamSettings if protocol supports it + if (streamProtocols.includes(this.protocol)) { + result.streamSettings = this.stream.toJson(); + } + + return result; } } diff --git a/frontend/src/pages/inbounds/InboundFormModal.vue b/frontend/src/pages/inbounds/InboundFormModal.vue index 45351144..5da9d341 100644 --- a/frontend/src/pages/inbounds/InboundFormModal.vue +++ b/frontend/src/pages/inbounds/InboundFormModal.vue @@ -226,9 +226,14 @@ function freshDbForm() { function primeAdvancedJson() { if (!inbound.value) return; - try { - advancedStreamText.value = JSON.stringify(JSON.parse(inbound.value.stream.toString()), null, 2); - } catch (_e) { /* keep prior text */ } + // Only set stream text for protocols that support it + if (canEnableStream.value) { + try { + advancedStreamText.value = JSON.stringify(JSON.parse(inbound.value.stream.toString()), null, 2); + } catch (_e) { /* keep prior text */ } + } else { + advancedStreamText.value = '{}'; + } try { advancedSniffingText.value = JSON.stringify(JSON.parse(inbound.value.sniffing.toString()), null, 2); } catch (_e) { /* keep prior text */ } @@ -361,15 +366,22 @@ const advancedAllConfig = computed({ advancedSniffingText.value, inbound.value.sniffing?.toJson?.() || {}, ); - return JSON.stringify({ + + const result = { listen: inbound.value.listen, port: inbound.value.port, protocol: inbound.value.protocol, settings, sniffing, - streamSettings, tag: inbound.value.tag, - }, null, 2); + }; + + // Only include streamSettings for protocols that support it + if (canEnableStream.value) { + result.streamSettings = streamSettings; + } + + return JSON.stringify(result, null, 2); } catch (_e) { return ''; } @@ -409,11 +421,17 @@ const advancedAllConfig = computed({ inbound.value?.settings?.toJson?.() || {}, ); const settings = parsed.settings ?? existingSettings; - const streamSettings = parsed.streamSettings ?? (inbound.value?.stream?.toJson?.() || {}); const sniffing = parsed.sniffing ?? (inbound.value?.sniffing?.toJson?.() || {}); advancedSettingsText.value = JSON.stringify(settings, null, 2); - advancedStreamText.value = JSON.stringify(streamSettings, null, 2); advancedSniffingText.value = JSON.stringify(sniffing, null, 2); + + // Only update stream settings if protocol supports it + if (canEnableStream.value) { + const streamSettings = parsed.streamSettings ?? (inbound.value?.stream?.toJson?.() || {}); + advancedStreamText.value = JSON.stringify(streamSettings, null, 2); + } else { + advancedStreamText.value = '{}'; + } } catch (e) { message.error(`All JSON invalid: ${e.message}`); } @@ -798,6 +816,11 @@ watch( () => inbound.value && JSON.stringify(inbound.value.stream?.toJson?.() || {}), () => { if (!inbound.value?.stream) return; + // Only update stream text for protocols that support it + if (!canEnableStream.value) { + advancedStreamText.value = '{}'; + return; + } try { advancedStreamText.value = JSON.stringify(JSON.parse(inbound.value.stream.toString()), null, 2); } catch (_e) { /* leave as is */ } @@ -821,6 +844,17 @@ watch( } catch (_e) { /* leave as is */ } }, ); + +// Watch protocol changes to clear stream settings for protocols that don't support it +watch( + () => inbound.value?.protocol, + () => { + if (!inbound.value) return; + if (!canEnableStream.value) { + advancedStreamText.value = '{}'; + } + }, +);