mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-05 12:44:22 +00:00
feat(frontend): OutboundFormModal.new.tsx sockopt + mux sections
- 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.
This commit is contained in:
parent
bfc9c12c05
commit
7765fb39fe
1 changed files with 168 additions and 0 deletions
|
|
@ -32,6 +32,7 @@ import {
|
||||||
} from '@/schemas/forms/outbound-form';
|
} from '@/schemas/forms/outbound-form';
|
||||||
import {
|
import {
|
||||||
ALPN_OPTION,
|
ALPN_OPTION,
|
||||||
|
Address_Port_Strategy,
|
||||||
DNSRuleActions,
|
DNSRuleActions,
|
||||||
MODE_OPTION,
|
MODE_OPTION,
|
||||||
OutboundDomainStrategies,
|
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 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 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 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<string>(['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 }[] = [
|
const NETWORK_OPTIONS: { value: string; label: string }[] = [
|
||||||
{ value: 'tcp', label: 'TCP (RAW)' },
|
{ value: 'tcp', label: 'TCP (RAW)' },
|
||||||
|
|
@ -1289,6 +1304,159 @@ export default function OutboundFormModalNew({
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{streamAllowed && network && (
|
||||||
|
<Form.Item shouldUpdate noStyle>
|
||||||
|
{() => {
|
||||||
|
const hasSockopt = !!form.getFieldValue([
|
||||||
|
'streamSettings',
|
||||||
|
'sockopt',
|
||||||
|
]);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form.Item label="Sockopts">
|
||||||
|
<Switch
|
||||||
|
checked={hasSockopt}
|
||||||
|
onChange={(checked) => {
|
||||||
|
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,
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
{hasSockopt && (
|
||||||
|
<>
|
||||||
|
<Form.Item
|
||||||
|
label="Dialer proxy"
|
||||||
|
name={['streamSettings', 'sockopt', 'dialerProxy']}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label="Domain strategy"
|
||||||
|
name={['streamSettings', 'sockopt', 'domainStrategy']}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
options={ADDRESS_PORT_STRATEGY_OPTIONS}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label="Keep alive interval"
|
||||||
|
name={['streamSettings', 'sockopt', 'tcpKeepAliveInterval']}
|
||||||
|
>
|
||||||
|
<InputNumber min={0} />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label="TCP Fast Open"
|
||||||
|
name={['streamSettings', 'sockopt', 'tcpFastOpen']}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label="Multipath TCP"
|
||||||
|
name={['streamSettings', 'sockopt', 'tcpMptcp']}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label="Penetrate"
|
||||||
|
name={['streamSettings', 'sockopt', 'penetrate']}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label="Mark (fwmark)"
|
||||||
|
name={['streamSettings', 'sockopt', 'mark']}
|
||||||
|
>
|
||||||
|
<InputNumber min={0} />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label="Interface"
|
||||||
|
name={['streamSettings', 'sockopt', 'interfaceName']}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{(() => {
|
||||||
|
const flow = (form.getFieldValue(['settings', 'flow']) ?? '') as string;
|
||||||
|
if (!isMuxAllowed(protocol, flow, network)) return null;
|
||||||
|
return (
|
||||||
|
<Form.Item shouldUpdate noStyle>
|
||||||
|
{() => {
|
||||||
|
const muxEnabled = !!form.getFieldValue(['mux', 'enabled']);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form.Item
|
||||||
|
label={t('pages.settings.mux')}
|
||||||
|
name={['mux', 'enabled']}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
{muxEnabled && (
|
||||||
|
<>
|
||||||
|
<Form.Item
|
||||||
|
label="Concurrency"
|
||||||
|
name={['mux', 'concurrency']}
|
||||||
|
>
|
||||||
|
<InputNumber min={-1} max={1024} />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label="xudp concurrency"
|
||||||
|
name={['mux', 'xudpConcurrency']}
|
||||||
|
>
|
||||||
|
<InputNumber min={-1} max={1024} />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label="xudp UDP 443"
|
||||||
|
name={['mux', 'xudpProxyUDP443']}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
options={['reject', 'allow', 'skip'].map((v) => ({
|
||||||
|
value: v,
|
||||||
|
label: v,
|
||||||
|
}))}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Form.Item>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue