From 7765fb39fe3aee31e0913d0b6244e1f93513105d Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Tue, 26 May 2026 12:19:13 +0200 Subject: [PATCH] feat(frontend): OutboundFormModal.new.tsx sockopt + mux sections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Sockopts: Switch toggles streamSettings.sockopt between undefined and a populated default object (17 fields with sane bbr/UseIP defaults). Only the 8 most-used fields are rendered (dialer proxy, domain strategy, keep alive interval, TFO, MPTCP, penetrate, mark, interface). The remaining sockopt knobs (acceptProxyProtocol, tcpUserTimeout, tcpcongestion, tcpKeepAliveIdle, tcpMaxSeg, tcpWindowClamp, V6Only, trustedXForwardedFor, tproxy) are still in the wire payload — edit them via the JSON tab. - Mux: gated by isMuxAllowed(protocol, flow, network) — VMess/VLESS/ Trojan/SS/HTTP/SOCKS, no flow set, no xhttp transport. Sub-fields (concurrency / xudpConcurrency / xudpProxyUDP443) only render when enabled is true. - Sockopt section visible only when streamAllowed AND network is set — non-stream protocols (freedom/blackhole/dns/loopback) still edit sockopt via the JSON tab. --- .../src/pages/xray/OutboundFormModal.new.tsx | 168 ++++++++++++++++++ 1 file changed, 168 insertions(+) diff --git a/frontend/src/pages/xray/OutboundFormModal.new.tsx b/frontend/src/pages/xray/OutboundFormModal.new.tsx index 02876046..b8993dc6 100644 --- a/frontend/src/pages/xray/OutboundFormModal.new.tsx +++ b/frontend/src/pages/xray/OutboundFormModal.new.tsx @@ -32,6 +32,7 @@ import { } from '@/schemas/forms/outbound-form'; import { ALPN_OPTION, + Address_Port_Strategy, DNSRuleActions, MODE_OPTION, OutboundDomainStrategies, @@ -72,6 +73,20 @@ const SS_METHOD_OPTIONS = SSMethodSchema.options.map((v) => ({ value: v, label: const MODE_OPTIONS = Object.values(MODE_OPTION).map((v) => ({ value: v, label: v })); const UTLS_OPTIONS = Object.values(UTLS_FINGERPRINT).map((v) => ({ value: v, label: v })); const ALPN_OPTIONS = Object.values(ALPN_OPTION).map((v) => ({ value: v, label: v })); +const ADDRESS_PORT_STRATEGY_OPTIONS = Object.values(Address_Port_Strategy).map((v) => ({ + value: v, + label: v, +})); + +// canEnableMux mirrors the adapter's helper but lives here so the modal +// can show/hide the Mux section without going through the adapter. +const MUX_PROTOCOLS = new Set(['vmess', 'vless', 'trojan', 'shadowsocks', 'http', 'socks']); +function isMuxAllowed(protocol: string, flow: string, network: string): boolean { + if (!MUX_PROTOCOLS.has(protocol)) return false; + if (protocol === 'vless' && flow) return false; + if (network === 'xhttp') return false; + return true; +} const NETWORK_OPTIONS: { value: string; label: string }[] = [ { value: 'tcp', label: 'TCP (RAW)' }, @@ -1289,6 +1304,159 @@ export default function OutboundFormModalNew({ )} + + {streamAllowed && network && ( + + {() => { + const hasSockopt = !!form.getFieldValue([ + 'streamSettings', + 'sockopt', + ]); + return ( + <> + + { + form.setFieldValue( + ['streamSettings', 'sockopt'], + checked + ? { + acceptProxyProtocol: false, + tcpFastOpen: false, + mark: 0, + tproxy: 'off', + tcpMptcp: false, + penetrate: false, + domainStrategy: 'UseIP', + tcpMaxSeg: 1440, + dialerProxy: '', + tcpKeepAliveInterval: 0, + tcpKeepAliveIdle: 300, + tcpUserTimeout: 10000, + tcpcongestion: 'bbr', + V6Only: false, + tcpWindowClamp: 600, + interfaceName: '', + trustedXForwardedFor: [], + } + : undefined, + ); + }} + /> + + {hasSockopt && ( + <> + + + + + + + + )} + + ); + }} + + )} + + {(() => { + const flow = (form.getFieldValue(['settings', 'flow']) ?? '') as string; + if (!isMuxAllowed(protocol, flow, network)) return null; + return ( + + {() => { + const muxEnabled = !!form.getFieldValue(['mux', 'enabled']); + return ( + <> + + + + {muxEnabled && ( + <> + + + + + + + +