From 57d66ec9ff8f5db27702e08e6e0c68c0fd5f707a Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Sat, 30 May 2026 20:14:16 +0200 Subject: [PATCH] refactor(frontend): extract inbound transport forms into transport/ folder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the six inbound stream-transport blocks (tcp/raw, ws, grpc, xhttp, httpupgrade, kcp) out of InboundFormModal into presentational components under inbounds/form/transport/. XhttpForm takes the form instance and re-derives its mode/obfs/placement watches internally; the rest are declarative. InboundFormModal drops from 2566 to 2105 lines. No behavior change — per-protocol field-label snapshots unchanged. --- .../pages/inbounds/form/InboundFormModal.tsx | 489 +----------------- .../pages/inbounds/form/transport/grpc.tsx | 29 ++ .../inbounds/form/transport/httpupgrade.tsx | 37 ++ .../pages/inbounds/form/transport/index.ts | 6 + .../src/pages/inbounds/form/transport/kcp.tsx | 34 ++ .../src/pages/inbounds/form/transport/raw.tsx | 164 ++++++ .../src/pages/inbounds/form/transport/ws.tsx | 37 ++ .../pages/inbounds/form/transport/xhttp.tsx | 218 ++++++++ 8 files changed, 539 insertions(+), 475 deletions(-) create mode 100644 frontend/src/pages/inbounds/form/transport/grpc.tsx create mode 100644 frontend/src/pages/inbounds/form/transport/httpupgrade.tsx create mode 100644 frontend/src/pages/inbounds/form/transport/index.ts create mode 100644 frontend/src/pages/inbounds/form/transport/kcp.tsx create mode 100644 frontend/src/pages/inbounds/form/transport/raw.tsx create mode 100644 frontend/src/pages/inbounds/form/transport/ws.tsx create mode 100644 frontend/src/pages/inbounds/form/transport/xhttp.tsx diff --git a/frontend/src/pages/inbounds/form/InboundFormModal.tsx b/frontend/src/pages/inbounds/form/InboundFormModal.tsx index 02908bd9..8e2fc459 100644 --- a/frontend/src/pages/inbounds/form/InboundFormModal.tsx +++ b/frontend/src/pages/inbounds/form/InboundFormModal.tsx @@ -75,7 +75,6 @@ import { HttpUpgradeStreamSettingsSchema } from '@/schemas/protocols/stream/http import { XHttpStreamSettingsSchema } from '@/schemas/protocols/stream/xhttp'; import { DateTimePicker } from '@/components/form'; import { FinalMaskForm } from '@/lib/xray/forms/transport'; -import { HeaderMapEditor } from '@/components/form'; import { InputAddon } from '@/components/ui'; import './InboundFormModal.css'; @@ -90,6 +89,14 @@ import { VlessFields, WireguardFields, } from './protocols'; +import { + GrpcForm, + HttpUpgradeForm, + KcpForm, + RawForm, + WsForm, + XhttpForm, +} from './transport'; const { TextArea } = Input; import { coerceInboundJsonField, type DBInbound } from '@/models/dbinbound'; @@ -503,11 +510,6 @@ export default function InboundFormModal({ } } }; - const xhttpMode = Form.useWatch(['streamSettings', 'xhttpSettings', 'mode'], form); - const xhttpObfsMode = Form.useWatch(['streamSettings', 'xhttpSettings', 'xPaddingObfsMode'], form) ?? false; - const xhttpSessionPlacement = Form.useWatch(['streamSettings', 'xhttpSettings', 'sessionPlacement'], form); - const xhttpSeqPlacement = Form.useWatch(['streamSettings', 'xhttpSettings', 'seqPlacement'], form); - const xhttpUplinkPlacement = Form.useWatch(['streamSettings', 'xhttpSettings', 'uplinkDataPlacement'], form); const toggleExternalProxy = (on: boolean) => { if (on) { @@ -1048,480 +1050,17 @@ export default function InboundFormModal({ HTTP server when probed. */} {protocol === Protocols.HYSTERIA && } - {network === 'tcp' && ( - <> - - - - - - prev.streamSettings?.tcpSettings?.header?.type - !== curr.streamSettings?.tcpSettings?.header?.type - } - > - {({ getFieldValue, setFieldValue }) => { - const headerType = getFieldValue( - ['streamSettings', 'tcpSettings', 'header', 'type'], - ) as string | undefined; - return ( - { - setFieldValue( - ['streamSettings', 'tcpSettings', 'header'], - v - ? { - type: 'http', - request: { - version: '1.1', - method: 'GET', - path: ['/'], - headers: {}, - }, - response: { - version: '1.1', - status: '200', - reason: 'OK', - headers: {}, - }, - } - : { type: 'none' }, - ); - }} - /> - ); - }} - - - {/* Per Xray docs (transports/raw.html#httpheaderobject), the - `request` object is honored only by outbound proxies; the - inbound listener reads `response`. Showing Host / Path / - Method / Version / request-headers on the inbound side was - a regression from this modal's earlier iteration — those - inputs wrote to the wire but xray-core ignored them. The - inbound modal now only exposes the response side. */} - - 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 ( - <> - - - - - - - ({ value: Array.isArray(v) ? v.join(',') : v })} - getValueFromEvent={(e) => { - const raw = (e?.target?.value ?? '') as string; - const parts = raw.split(',').map((s) => s.trim()).filter(Boolean); - return parts.length > 0 ? parts : ['/']; - }} - > - - - - - - - - - - - - - - - - - - - ); - }} - - - )} + {network === 'tcp' && } - {network === 'ws' && ( - <> - - - - - - - - - - - - - - - - - )} + {network === 'ws' && } - {network === 'grpc' && ( - <> - - - - - - - - - - - )} + {network === 'grpc' && } - {network === 'xhttp' && ( - <> - - - - - - - - - - - )} - {xhttpMode === 'stream-up' && ( - - - - )} - - - - - - - - - - - - - - - - - - - - )} - - - - )} - - - - )} - {xhttpMode === 'packet-up' && ( - <> - - - - )} - - )} - - - - - )} + {network === 'xhttp' && } - {network === 'httpupgrade' && ( - <> - - - - - - - - - - - - - - )} + {network === 'httpupgrade' && } - {network === 'kcp' && ( - <> - - - - - - - - - - - - - - - - - - - - )} + {network === 'kcp' && } + + + + + + + + + + + ); +} diff --git a/frontend/src/pages/inbounds/form/transport/httpupgrade.tsx b/frontend/src/pages/inbounds/form/transport/httpupgrade.tsx new file mode 100644 index 00000000..cb56e1ad --- /dev/null +++ b/frontend/src/pages/inbounds/form/transport/httpupgrade.tsx @@ -0,0 +1,37 @@ +import { useTranslation } from 'react-i18next'; +import { Form, Input, Switch } from 'antd'; + +import { HeaderMapEditor } from '@/components/form'; + +export default function HttpUpgradeForm() { + const { t } = useTranslation(); + return ( + <> + + + + + + + + + + + + + + ); +} diff --git a/frontend/src/pages/inbounds/form/transport/index.ts b/frontend/src/pages/inbounds/form/transport/index.ts new file mode 100644 index 00000000..ebcfb747 --- /dev/null +++ b/frontend/src/pages/inbounds/form/transport/index.ts @@ -0,0 +1,6 @@ +export { default as RawForm } from './raw'; +export { default as WsForm } from './ws'; +export { default as GrpcForm } from './grpc'; +export { default as XhttpForm } from './xhttp'; +export { default as HttpUpgradeForm } from './httpupgrade'; +export { default as KcpForm } from './kcp'; diff --git a/frontend/src/pages/inbounds/form/transport/kcp.tsx b/frontend/src/pages/inbounds/form/transport/kcp.tsx new file mode 100644 index 00000000..e688cd50 --- /dev/null +++ b/frontend/src/pages/inbounds/form/transport/kcp.tsx @@ -0,0 +1,34 @@ +import { useTranslation } from 'react-i18next'; +import { Form, InputNumber } from 'antd'; + +export default function KcpForm() { + const { t } = useTranslation(); + return ( + <> + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/frontend/src/pages/inbounds/form/transport/raw.tsx b/frontend/src/pages/inbounds/form/transport/raw.tsx new file mode 100644 index 00000000..b1731514 --- /dev/null +++ b/frontend/src/pages/inbounds/form/transport/raw.tsx @@ -0,0 +1,164 @@ +import { useTranslation } from 'react-i18next'; +import { Form, Input, Switch } from 'antd'; + +import { HeaderMapEditor } from '@/components/form'; + +export default function RawForm() { + const { t } = useTranslation(); + return ( + <> + + + + + + prev.streamSettings?.tcpSettings?.header?.type + !== curr.streamSettings?.tcpSettings?.header?.type + } + > + {({ getFieldValue, setFieldValue }) => { + const headerType = getFieldValue( + ['streamSettings', 'tcpSettings', 'header', 'type'], + ) as string | undefined; + return ( + { + setFieldValue( + ['streamSettings', 'tcpSettings', 'header'], + v + ? { + type: 'http', + request: { + version: '1.1', + method: 'GET', + path: ['/'], + headers: {}, + }, + response: { + version: '1.1', + status: '200', + reason: 'OK', + headers: {}, + }, + } + : { type: 'none' }, + ); + }} + /> + ); + }} + + + {/* Per Xray docs (transports/raw.html#httpheaderobject), the + `request` object is honored only by outbound proxies; the + inbound listener reads `response`. Showing Host / Path / + Method / Version / request-headers on the inbound side was + a regression from this modal's earlier iteration — those + inputs wrote to the wire but xray-core ignored them. The + inbound modal now only exposes the response side. */} + + 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 ( + <> + + + + + + + ({ value: Array.isArray(v) ? v.join(',') : v })} + getValueFromEvent={(e) => { + const raw = (e?.target?.value ?? '') as string; + const parts = raw.split(',').map((s) => s.trim()).filter(Boolean); + return parts.length > 0 ? parts : ['/']; + }} + > + + + + + + + + + + + + + + + + + + + ); + }} + + + ); +} diff --git a/frontend/src/pages/inbounds/form/transport/ws.tsx b/frontend/src/pages/inbounds/form/transport/ws.tsx new file mode 100644 index 00000000..86b8a3af --- /dev/null +++ b/frontend/src/pages/inbounds/form/transport/ws.tsx @@ -0,0 +1,37 @@ +import { useTranslation } from 'react-i18next'; +import { Form, Input, InputNumber, Switch } from 'antd'; + +import { HeaderMapEditor } from '@/components/form'; + +export default function WsForm() { + const { t } = useTranslation(); + return ( + <> + + + + + + + + + + + + + + + + + ); +} diff --git a/frontend/src/pages/inbounds/form/transport/xhttp.tsx b/frontend/src/pages/inbounds/form/transport/xhttp.tsx new file mode 100644 index 00000000..eb6ba700 --- /dev/null +++ b/frontend/src/pages/inbounds/form/transport/xhttp.tsx @@ -0,0 +1,218 @@ +import { useTranslation } from 'react-i18next'; +import { Form, Input, InputNumber, Select, Switch, type FormInstance } from 'antd'; + +import { HeaderMapEditor } from '@/components/form'; +import type { InboundFormValues } from '@/schemas/forms/inbound-form'; + +export default function XhttpForm({ form }: { form: FormInstance }) { + const { t } = useTranslation(); + const xhttpMode = Form.useWatch(['streamSettings', 'xhttpSettings', 'mode'], form); + const xhttpObfsMode = Form.useWatch(['streamSettings', 'xhttpSettings', 'xPaddingObfsMode'], form) ?? false; + const xhttpSessionPlacement = Form.useWatch(['streamSettings', 'xhttpSettings', 'sessionPlacement'], form); + const xhttpSeqPlacement = Form.useWatch(['streamSettings', 'xhttpSettings', 'seqPlacement'], form); + const xhttpUplinkPlacement = Form.useWatch(['streamSettings', 'xhttpSettings', 'uplinkDataPlacement'], form); + return ( + <> + + + + + + + + + + + )} + {xhttpMode === 'stream-up' && ( + + + + )} + + + + + + + + + + + + + + + + + + + + )} + + + + )} + + + + )} + {xhttpMode === 'packet-up' && ( + <> + + + + )} + + )} + + + + + ); +}