From 84c5aef4a111f95c92280e91f78ad38c239b3a47 Mon Sep 17 00:00:00 2001 From: BlindMaster24 <375291171150z@gmail.com> Date: Mon, 14 Sep 2026 17:00:13 +0300 Subject: [PATCH] fix(panel): probe UDP outbounds and hide the block outbound from the mtproto egress picker (#6525) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(panel): match the probe and egress readers to what the core loads Two readers left over from the case-sensitivity sweep still disagreed with the core, both raised reviewing #6523. isUdpOutbound compared the protocol id and the transport name exactly. The core lowercases both before it resolves them (infra/conf/loader.go:46 for the id, TransportProtocol.Build at infra/conf/transport_internet.go:16-17 for the name), so an outbound spelled "WireGuard" or a stream named "KCP" still built a UDP handler but was probed with a dial-only TCP request, and Test All Outbounds reported a working outbound as down. The mtproto egress picker asked for outbound tags without excludeBlackhole, so the block outbound stayed selectable there. Choosing it looks like a working selection and discards that inbound's Telegram traffic. * fix(panel): recognise the mkcp transport alias and pin the picker's field id Review findings on #6525. TransportProtocol.Build resolves both "kcp" and "mkcp" to the same mKCP transport, so comparing the transport name against "kcp" alone left a template spelling "network": "mkcp" in the TCP lane and reported a working outbound as down — the same trigger this PR already fixed for the "KCP" capitalisation. The egress picker now carries an explicit id, the way the inbound form's protocol select does, so the test addresses that field rather than the first searchable select on the page and reuses the shared dropdown helper instead of duplicating it. --- frontend/src/hooks/useXraySetting.ts | 22 ++++--- .../pages/inbounds/form/protocols/mtproto.tsx | 3 +- .../src/test/mtproto-egress-picker.test.tsx | 58 +++++++++++++++++++ frontend/src/test/use-xray-setting.test.tsx | 45 ++++++++++++++ 4 files changed, 118 insertions(+), 10 deletions(-) create mode 100644 frontend/src/test/mtproto-egress-picker.test.tsx diff --git a/frontend/src/hooks/useXraySetting.ts b/frontend/src/hooks/useXraySetting.ts index e137a928c..71f9a40cc 100644 --- a/frontend/src/hooks/useXraySetting.ts +++ b/frontend/src/hooks/useXraySetting.ts @@ -26,20 +26,24 @@ function normalizeOutboundTestUrl(url: string) { return url || DEFAULT_TEST_URL; } +// The core lowercases a protocol id and a transport name before resolving +// either, so "WireGuard"/"KCP" still build a UDP handler a TCP dial misreports. export function isUdpOutbound(outbound: unknown): boolean { const o = outbound as - | { protocol?: string; streamSettings?: { network?: string } } + | { protocol?: unknown; streamSettings?: { network?: unknown } } | null | undefined; - const p = o?.protocol; - const n = o?.streamSettings?.network; + const rawNetwork = o?.streamSettings?.network; + const network = typeof rawNetwork === 'string' ? rawNetwork.toLowerCase() : ''; return ( - p === 'wireguard' || - p === 'hysteria' || - p === 'amneziawg' || - n === 'hysteria' || - n === 'kcp' || - n === 'quic' + isOutboundProtocol(o, 'wireguard') || + isOutboundProtocol(o, 'hysteria') || + isOutboundProtocol(o, 'amneziawg') || + network === 'hysteria' || + network === 'kcp' || + // The core resolves "kcp" and "mkcp" to the same mKCP transport. + network === 'mkcp' || + network === 'quic' ); } diff --git a/frontend/src/pages/inbounds/form/protocols/mtproto.tsx b/frontend/src/pages/inbounds/form/protocols/mtproto.tsx index 47046c3b7..12ca391e0 100644 --- a/frontend/src/pages/inbounds/form/protocols/mtproto.tsx +++ b/frontend/src/pages/inbounds/form/protocols/mtproto.tsx @@ -11,7 +11,7 @@ export default function MtprotoFields() { const routeThroughXray = useWatch({ control, name: 'settings.routeThroughXray' }) as | boolean | undefined; - const { data: outboundTags } = useOutboundTags(); + const { data: outboundTags } = useOutboundTags({ excludeBlackhole: true }); return ( <>