From 52cbcfb99ea9fc73bc04edd45a2d7cbdd1e10006 Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Sat, 30 May 2026 20:03:22 +0200 Subject: [PATCH] refactor(frontend): split inbound vless/http/mixed/hysteria protocol forms Extract the remaining inbound protocol blocks into inbounds/form/protocols/: vless (auth handlers/state as props), http + mixed (shared accounts-list), hysteria. Drop now-unused HysteriaMasqueradeForm/Typography/Text imports from the modal. InboundFormModal.tsx 2841 -> 2478. Inbound snapshots unchanged -> no behavior change. typecheck/lint/build green. --- .../pages/inbounds/form/InboundFormModal.tsx | 147 ++---------------- .../inbounds/form/protocols/accounts-list.tsx | 47 ++++++ .../pages/inbounds/form/protocols/http.tsx | 20 +++ .../inbounds/form/protocols/hysteria.tsx | 27 ++++ .../pages/inbounds/form/protocols/index.ts | 4 + .../pages/inbounds/form/protocols/mixed.tsx | 33 ++++ .../pages/inbounds/form/protocols/vless.tsx | 60 +++++++ 7 files changed, 205 insertions(+), 133 deletions(-) create mode 100644 frontend/src/pages/inbounds/form/protocols/accounts-list.tsx create mode 100644 frontend/src/pages/inbounds/form/protocols/http.tsx create mode 100644 frontend/src/pages/inbounds/form/protocols/hysteria.tsx create mode 100644 frontend/src/pages/inbounds/form/protocols/mixed.tsx create mode 100644 frontend/src/pages/inbounds/form/protocols/vless.tsx diff --git a/frontend/src/pages/inbounds/form/InboundFormModal.tsx b/frontend/src/pages/inbounds/form/InboundFormModal.tsx index 571a7286..02908bd9 100644 --- a/frontend/src/pages/inbounds/form/InboundFormModal.tsx +++ b/frontend/src/pages/inbounds/form/InboundFormModal.tsx @@ -16,7 +16,6 @@ import { Switch, Tabs, Tooltip, - Typography, message, } from 'antd'; import { @@ -77,12 +76,20 @@ 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 { HysteriaMasqueradeForm } from '@/lib/xray/forms/protocols/shared'; import { InputAddon } from '@/components/ui'; import './InboundFormModal.css'; import { AdvancedAllEditor, AdvancedSliceEditor } from './advanced-editors'; -import { ShadowsocksFields, TunFields, TunnelFields, WireguardFields } from './protocols'; +import { + HttpFields, + HysteriaFields, + MixedFields, + ShadowsocksFields, + TunFields, + TunnelFields, + VlessFields, + WireguardFields, +} from './protocols'; const { TextArea } = Input; import { coerceInboundJsonField, type DBInbound } from '@/models/dbinbound'; @@ -93,8 +100,6 @@ import type { NodeRecord } from '@/api/queries/useNodesQuery'; // InboundsPage continues to render the old InboundFormModal.tsx until the // atomic swap at the end (Core Decision 7). -const { Text } = Typography; - const PROTOCOL_OPTIONS = Object.values(Protocols).map((p) => ({ value: p, label: p })); const TRAFFIC_RESETS = ['never', 'hourly', 'daily', 'weekly', 'monthly'] as const; @@ -952,119 +957,12 @@ export default function InboundFormModal({ {protocol === Protocols.TUNNEL && } - {(protocol === Protocols.HTTP || protocol === Protocols.MIXED) && ( - <> - - {(fields, { add, remove }) => ( - <> - - - - {fields.length > 0 && ( - - {fields.map((field, idx) => ( - - {String(idx + 1)} - - - - - - - - - ))} - - )} - - )} - - {protocol === Protocols.HTTP && ( - - - - )} - {protocol === Protocols.MIXED && ( - <> - - - - )} - - )} - - )} + {protocol === Protocols.HTTP && } + {protocol === Protocols.MIXED && } {protocol === Protocols.SHADOWSOCKS && } - {protocol === Protocols.VLESS && ( - <> - - - - - - - - - - - - - - {t('pages.inbounds.vlessAuthSelected', { auth: selectedVlessAuth })} - - - {network === 'tcp' && (security === 'tls' || security === 'reality') && ( - - - {[900, 500, 900, 256].map((def, i) => ( - - - - ))} - - - )} - - )} + {protocol === Protocols.VLESS && } {isFallbackHost && fallbacksCard} @@ -1148,24 +1046,7 @@ export default function InboundFormModal({ auth + udpIdleTimeout are required, masquerade is an optional sub-object that lets xray-core disguise the listener as an HTTP server when probed. */} - {protocol === Protocols.HYSTERIA && ( - <> - - - - - - - - - - )} + {protocol === Protocols.HYSTERIA && } {network === 'tcp' && ( <> diff --git a/frontend/src/pages/inbounds/form/protocols/accounts-list.tsx b/frontend/src/pages/inbounds/form/protocols/accounts-list.tsx new file mode 100644 index 00000000..850dd3ba --- /dev/null +++ b/frontend/src/pages/inbounds/form/protocols/accounts-list.tsx @@ -0,0 +1,47 @@ +import { useTranslation } from 'react-i18next'; +import { Button, Form, Input, Space } from 'antd'; +import { MinusOutlined, PlusOutlined } from '@ant-design/icons'; + +import { RandomUtil } from '@/utils'; +import { InputAddon } from '@/components/ui'; + +export default function AccountsList() { + const { t } = useTranslation(); + return ( + + {(fields, { add, remove }) => ( + <> + + + + {fields.length > 0 && ( + + {fields.map((field, idx) => ( + + {String(idx + 1)} + + + + + + + + + ))} + + )} + + )} + + ); +} diff --git a/frontend/src/pages/inbounds/form/protocols/http.tsx b/frontend/src/pages/inbounds/form/protocols/http.tsx new file mode 100644 index 00000000..11806fae --- /dev/null +++ b/frontend/src/pages/inbounds/form/protocols/http.tsx @@ -0,0 +1,20 @@ +import { useTranslation } from 'react-i18next'; +import { Form, Switch } from 'antd'; + +import AccountsList from './accounts-list'; + +export default function HttpFields() { + const { t } = useTranslation(); + return ( + <> + + + + + + ); +} diff --git a/frontend/src/pages/inbounds/form/protocols/hysteria.tsx b/frontend/src/pages/inbounds/form/protocols/hysteria.tsx new file mode 100644 index 00000000..a0b78c24 --- /dev/null +++ b/frontend/src/pages/inbounds/form/protocols/hysteria.tsx @@ -0,0 +1,27 @@ +import { useTranslation } from 'react-i18next'; +import { Form, InputNumber, type FormInstance } from 'antd'; + +import { HysteriaMasqueradeForm } from '@/lib/xray/forms/protocols/shared'; +import type { InboundFormValues } from '@/schemas/forms/inbound-form'; + +export default function HysteriaFields({ form }: { form: FormInstance }) { + const { t } = useTranslation(); + return ( + <> + + + + + + + + + + ); +} diff --git a/frontend/src/pages/inbounds/form/protocols/index.ts b/frontend/src/pages/inbounds/form/protocols/index.ts index f44af7eb..a92744e6 100644 --- a/frontend/src/pages/inbounds/form/protocols/index.ts +++ b/frontend/src/pages/inbounds/form/protocols/index.ts @@ -2,3 +2,7 @@ export { default as TunFields } from './tun'; export { default as TunnelFields } from './tunnel'; export { default as ShadowsocksFields } from './shadowsocks'; export { default as WireguardFields } from './wireguard'; +export { default as HysteriaFields } from './hysteria'; +export { default as HttpFields } from './http'; +export { default as MixedFields } from './mixed'; +export { default as VlessFields } from './vless'; diff --git a/frontend/src/pages/inbounds/form/protocols/mixed.tsx b/frontend/src/pages/inbounds/form/protocols/mixed.tsx new file mode 100644 index 00000000..274aa03d --- /dev/null +++ b/frontend/src/pages/inbounds/form/protocols/mixed.tsx @@ -0,0 +1,33 @@ +import { useTranslation } from 'react-i18next'; +import { Form, Input, Select, Switch } from 'antd'; + +import AccountsList from './accounts-list'; + +export default function MixedFields({ mixedUdpOn }: { mixedUdpOn: boolean }) { + const { t } = useTranslation(); + return ( + <> + + + + + )} + + ); +} diff --git a/frontend/src/pages/inbounds/form/protocols/vless.tsx b/frontend/src/pages/inbounds/form/protocols/vless.tsx new file mode 100644 index 00000000..d924c880 --- /dev/null +++ b/frontend/src/pages/inbounds/form/protocols/vless.tsx @@ -0,0 +1,60 @@ +import { useTranslation } from 'react-i18next'; +import { Button, Form, Input, InputNumber, Space, Typography } from 'antd'; + +interface VlessFieldsProps { + saving: boolean; + selectedVlessAuth: string; + network: string; + security: string; + getNewVlessEnc: (kind: 'x25519' | 'mlkem768') => void; + clearVlessEnc: () => void; +} + +export default function VlessFields({ + saving, + selectedVlessAuth, + network, + security, + getNewVlessEnc, + clearVlessEnc, +}: VlessFieldsProps) { + const { t } = useTranslation(); + return ( + <> + + + + + + + + + + + + + + {t('pages.inbounds.vlessAuthSelected', { auth: selectedVlessAuth })} + + + {network === 'tcp' && (security === 'tls' || security === 'reality') && ( + + + {[900, 500, 900, 256].map((def, i) => ( + + + + ))} + + + )} + + ); +}