diff --git a/frontend/src/lib/xray/inbound-link.ts b/frontend/src/lib/xray/inbound-link.ts index 4059c9abc..decc72721 100644 --- a/frontend/src/lib/xray/inbound-link.ts +++ b/frontend/src/lib/xray/inbound-link.ts @@ -317,7 +317,7 @@ export function genVlessLink(input: GenVlessLinkInput): string { const security = forceTls === 'same' ? stream.security : forceTls; const params = new URLSearchParams(); - params.set('type', stream.network); + params.set('type', stream.network ?? 'tcp'); params.set('encryption', inbound.settings.encryption); if (stream.network === 'tcp') { @@ -501,7 +501,7 @@ export function genTrojanLink(input: GenTrojanLinkInput): string { const security = forceTls === 'same' ? stream.security : forceTls; const params = new URLSearchParams(); - params.set('type', stream.network); + params.set('type', stream.network ?? 'tcp'); writeNetworkParams(stream, params); applyFinalMaskToParams(stream.finalmask, params); @@ -558,7 +558,7 @@ export function genShadowsocksLink(input: GenShadowsocksLinkInput): string { const security = forceTls === 'same' ? stream.security : forceTls; const params = new URLSearchParams(); - params.set('type', stream.network); + params.set('type', stream.network ?? 'tcp'); writeNetworkParams(stream, params); applyFinalMaskToParams(stream.finalmask, params); diff --git a/frontend/src/lib/xray/protocol-capabilities.ts b/frontend/src/lib/xray/protocol-capabilities.ts index 574f513ea..fced61282 100644 --- a/frontend/src/lib/xray/protocol-capabilities.ts +++ b/frontend/src/lib/xray/protocol-capabilities.ts @@ -9,7 +9,7 @@ const TLS_ELIGIBLE_PROTOCOLS = ['vmess', 'vless', 'trojan', 'shadowsocks']; const TLS_NETWORKS = ['tcp', 'ws', 'http', 'grpc', 'httpupgrade', 'xhttp']; const REALITY_ELIGIBLE_PROTOCOLS = ['vless', 'trojan']; const REALITY_NETWORKS = ['tcp', 'http', 'grpc', 'xhttp']; -const STREAM_PROTOCOLS = ['vmess', 'vless', 'trojan', 'shadowsocks', 'hysteria', 'wireguard']; +const STREAM_PROTOCOLS = ['vmess', 'vless', 'trojan', 'shadowsocks', 'hysteria', 'wireguard', 'tunnel']; const VISION_FLOW = 'xtls-rprx-vision'; const SS_2022_PREFIX = '2022'; const SS_BLAKE3_CHACHA20 = '2022-blake3-chacha20-poly1305'; diff --git a/frontend/src/pages/inbounds/form/InboundFormModal.tsx b/frontend/src/pages/inbounds/form/InboundFormModal.tsx index 725107d69..caf7f9fd5 100644 --- a/frontend/src/pages/inbounds/form/InboundFormModal.tsx +++ b/frontend/src/pages/inbounds/form/InboundFormModal.tsx @@ -162,6 +162,15 @@ export default function InboundFormModal({ const security = Form.useWatch(['streamSettings', 'security'], form) ?? 'none'; const streamEnabled = canEnableStream({ protocol }); const sniffingSupported = canEnableSniffing({ protocol }); + // Wireguard (always a UDP listener) and Tunnel (dokodemo-door) expose no + // user-selectable transport — their stream tab is just sockopt, which is all + // Tunnel's TProxy/redirect mode needs (sockopt.tproxy). Hysteria carries its + // own dedicated transport form. For all of these the RAW/mKCP/WS/... network + // picker and the per-network sub-forms are hidden. + const hasSelectableTransport = + protocol !== Protocols.HYSTERIA + && protocol !== Protocols.WIREGUARD + && protocol !== Protocols.TUNNEL; const wPort = Form.useWatch('port', form); const wListen = (Form.useWatch('listen', form) ?? '') as string; @@ -372,11 +381,13 @@ export default function InboundFormModal({ }], }, }); - } else if (next === Protocols.WIREGUARD) { - // Wireguard has no user-selectable transport: the listener is always - // UDP and only finalmask/sockopt from streamSettings apply. Drop the - // leftover network/transport slices so the stream tab doesn't render - // a TCP sub-form and the wire payload carries no dead tcpSettings. + } else if (next === Protocols.WIREGUARD || next === Protocols.TUNNEL) { + // Wireguard and Tunnel (dokodemo-door) have no user-selectable + // transport: wireguard is always a UDP listener, and tunnel only needs + // `sockopt.tproxy` for its TProxy/redirect mode. Drop the leftover + // network/transport slices so the stream tab doesn't render a TCP + // sub-form and the wire payload carries no dead tcpSettings — the + // sockopt section (with TProxy) stays available. form.setFieldValue('streamSettings', { security: 'none' }); } else { const current = form.getFieldValue('streamSettings') as { network?: string } | undefined; @@ -651,7 +662,7 @@ export default function InboundFormModal({ const streamTab = ( <> - {protocol !== Protocols.HYSTERIA && protocol !== Protocols.WIREGUARD && ( + {hasSelectableTransport && (