mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-05 20:54:14 +00:00
feat(frontend): symmetric TCP HTTP host/path + extra sockopt knobs
OutboundFormModal: - Sockopt section gains 5 common-but-rarely-tweaked knobs: acceptProxyProtocol, tproxy (off/redirect/tproxy), tcpcongestion (bbr/cubic/reno), V6Only, tcpUserTimeout. The remaining sockopt fields (tcpKeepAliveIdle, tcpMaxSeg, tcpWindowClamp, trustedXForwardedFor) are still edit-via-JSON; they are deeply tunable and not commonly touched. InboundFormModal: - TCP HTTP camouflage gains host + path inputs symmetric to the outbound side. Switch ON seeds request with sensible defaults (version 1.1, method GET, path ['/'], empty headers). The two inputs use the same normalize/getValueProps comma-string ↔ string[] dance the outbound side uses, so the wire shape stays identical to what xray-core expects.
This commit is contained in:
parent
ad3d3937b0
commit
e62ad84bb7
2 changed files with 111 additions and 1 deletions
|
|
@ -1157,7 +1157,17 @@ export default function InboundFormModal({
|
||||||
onChange={(v) => {
|
onChange={(v) => {
|
||||||
setFieldValue(
|
setFieldValue(
|
||||||
['streamSettings', 'tcpSettings', 'header'],
|
['streamSettings', 'tcpSettings', 'header'],
|
||||||
v ? { type: 'http' } : { type: 'none' },
|
v
|
||||||
|
? {
|
||||||
|
type: 'http',
|
||||||
|
request: {
|
||||||
|
version: '1.1',
|
||||||
|
method: 'GET',
|
||||||
|
path: ['/'],
|
||||||
|
headers: {},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: { type: 'none' },
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
@ -1165,6 +1175,62 @@ export default function InboundFormModal({
|
||||||
}}
|
}}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
{/* Host + path camouflage inputs only render when the Switch
|
||||||
|
above is on. Both are string[] on the wire; normalize +
|
||||||
|
getValueProps translate to/from comma-joined input. Mirrors
|
||||||
|
the symmetric outbound side. */}
|
||||||
|
<Form.Item
|
||||||
|
noStyle
|
||||||
|
shouldUpdate={(prev, curr) =>
|
||||||
|
prev.streamSettings?.tcpSettings?.header?.type
|
||||||
|
!== curr.streamSettings?.tcpSettings?.header?.type
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{({ getFieldValue }) => {
|
||||||
|
const headerType = getFieldValue(
|
||||||
|
['streamSettings', 'tcpSettings', 'header', 'type'],
|
||||||
|
) as string | undefined;
|
||||||
|
if (headerType !== 'http') return null;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form.Item
|
||||||
|
label={t('host')}
|
||||||
|
name={[
|
||||||
|
'streamSettings', 'tcpSettings', 'header',
|
||||||
|
'request', 'headers', 'Host',
|
||||||
|
]}
|
||||||
|
normalize={(v: unknown) =>
|
||||||
|
typeof v === 'string'
|
||||||
|
? v.split(',').map((s) => s.trim()).filter(Boolean)
|
||||||
|
: Array.isArray(v) ? v : []
|
||||||
|
}
|
||||||
|
getValueProps={(v: unknown) => ({
|
||||||
|
value: Array.isArray(v) ? v.join(',') : '',
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Input placeholder="example.com,cdn.example.com" />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t('path')}
|
||||||
|
name={[
|
||||||
|
'streamSettings', 'tcpSettings', 'header',
|
||||||
|
'request', 'path',
|
||||||
|
]}
|
||||||
|
normalize={(v: unknown) =>
|
||||||
|
typeof v === 'string'
|
||||||
|
? v.split(',').map((s) => s.trim()).filter(Boolean)
|
||||||
|
: Array.isArray(v) ? v : ['/']
|
||||||
|
}
|
||||||
|
getValueProps={(v: unknown) => ({
|
||||||
|
value: Array.isArray(v) ? v.join(',') : '/',
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Input placeholder="/,/api,/static" />
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Form.Item>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ import {
|
||||||
OutboundDomainStrategies,
|
OutboundDomainStrategies,
|
||||||
OutboundProtocols as Protocols,
|
OutboundProtocols as Protocols,
|
||||||
SNIFFING_OPTION,
|
SNIFFING_OPTION,
|
||||||
|
TCP_CONGESTION_OPTION,
|
||||||
TLS_FLOW_CONTROL,
|
TLS_FLOW_CONTROL,
|
||||||
USERS_SECURITY,
|
USERS_SECURITY,
|
||||||
UTLS_FINGERPRINT,
|
UTLS_FINGERPRINT,
|
||||||
|
|
@ -1516,6 +1517,49 @@ export default function OutboundFormModal({
|
||||||
>
|
>
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label="TProxy"
|
||||||
|
name={['streamSettings', 'sockopt', 'tproxy']}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
options={[
|
||||||
|
{ value: 'off', label: 'off' },
|
||||||
|
{ value: 'redirect', label: 'redirect' },
|
||||||
|
{ value: 'tproxy', label: 'tproxy' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label="TCP congestion"
|
||||||
|
name={['streamSettings', 'sockopt', 'tcpcongestion']}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
options={Object.values(TCP_CONGESTION_OPTION).map((v) => ({
|
||||||
|
value: v,
|
||||||
|
label: v,
|
||||||
|
}))}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label="IPv6 only"
|
||||||
|
name={['streamSettings', 'sockopt', 'V6Only']}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label="Accept proxy protocol"
|
||||||
|
name={['streamSettings', 'sockopt', 'acceptProxyProtocol']}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label="TCP user timeout (ms)"
|
||||||
|
name={['streamSettings', 'sockopt', 'tcpUserTimeout']}
|
||||||
|
>
|
||||||
|
<InputNumber min={0} style={{ width: '100%' }} />
|
||||||
|
</Form.Item>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue