mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-05 12:44:22 +00:00
refactor(frontend): split outbound transport forms into per-transport files
Extract the tcp(raw)/kcp/ws/grpc/httpupgrade transport blocks into outbounds/transport/ per-file components (RawForm, KcpForm, WsForm, GrpcForm, HttpUpgradeForm). xhttp + hysteria transport remain inline for a follow-up. Verbatim relocation; outbound snapshots unchanged -> no behavior change. typecheck/lint/build green.
This commit is contained in:
parent
47a2eb7efe
commit
3271374401
7 changed files with 267 additions and 213 deletions
|
|
@ -76,6 +76,13 @@ import {
|
||||||
VmessFields,
|
VmessFields,
|
||||||
WireguardFields,
|
WireguardFields,
|
||||||
} from './protocols';
|
} from './protocols';
|
||||||
|
import {
|
||||||
|
GrpcForm,
|
||||||
|
HttpUpgradeForm,
|
||||||
|
KcpForm,
|
||||||
|
RawForm,
|
||||||
|
WsForm,
|
||||||
|
} from './transport';
|
||||||
import './OutboundFormModal.css';
|
import './OutboundFormModal.css';
|
||||||
|
|
||||||
// Pattern A rewrite of OutboundFormModal. Built as a sibling `.new.tsx`
|
// Pattern A rewrite of OutboundFormModal. Built as a sibling `.new.tsx`
|
||||||
|
|
@ -507,223 +514,15 @@ export default function OutboundFormModal({
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
{network === 'tcp' && (
|
{network === 'tcp' && <RawForm form={form} />}
|
||||||
<Form.Item shouldUpdate noStyle>
|
|
||||||
{() => {
|
|
||||||
const type =
|
|
||||||
form.getFieldValue([
|
|
||||||
'streamSettings',
|
|
||||||
'tcpSettings',
|
|
||||||
'header',
|
|
||||||
'type',
|
|
||||||
]) ?? 'none';
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Form.Item label={`HTTP ${t('camouflage')}`}>
|
|
||||||
<Switch
|
|
||||||
checked={type === 'http'}
|
|
||||||
onChange={(checked) =>
|
|
||||||
form.setFieldValue(
|
|
||||||
['streamSettings', 'tcpSettings', 'header'],
|
|
||||||
checked
|
|
||||||
? {
|
|
||||||
type: 'http',
|
|
||||||
request: {
|
|
||||||
version: '1.1',
|
|
||||||
method: 'GET',
|
|
||||||
path: ['/'],
|
|
||||||
headers: {},
|
|
||||||
},
|
|
||||||
response: {
|
|
||||||
version: '1.1',
|
|
||||||
status: '200',
|
|
||||||
reason: 'OK',
|
|
||||||
headers: {},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
: { type: 'none' },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
{type === 'http' && (
|
|
||||||
<>
|
|
||||||
<Form.Item
|
|
||||||
label={t('pages.inbounds.form.requestMethod')}
|
|
||||||
name={[
|
|
||||||
'streamSettings', 'tcpSettings', 'header',
|
|
||||||
'request', 'method',
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Input placeholder="GET" />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('pages.inbounds.form.requestVersion')}
|
|
||||||
name={[
|
|
||||||
'streamSettings', 'tcpSettings', 'header',
|
|
||||||
'request', 'version',
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Input placeholder="1.1" />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('pages.inbounds.form.requestHeaders')}
|
|
||||||
name={[
|
|
||||||
'streamSettings', 'tcpSettings', 'header',
|
|
||||||
'request', 'headers',
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<HeaderMapEditor mode="v2" />
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item
|
{network === 'kcp' && <KcpForm />}
|
||||||
label={t('pages.inbounds.form.responseVersion')}
|
|
||||||
name={[
|
|
||||||
'streamSettings', 'tcpSettings', 'header',
|
|
||||||
'response', 'version',
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Input placeholder="1.1" />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('pages.inbounds.form.responseStatus')}
|
|
||||||
name={[
|
|
||||||
'streamSettings', 'tcpSettings', 'header',
|
|
||||||
'response', 'status',
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Input placeholder="200" />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('pages.inbounds.form.responseReason')}
|
|
||||||
name={[
|
|
||||||
'streamSettings', 'tcpSettings', 'header',
|
|
||||||
'response', 'reason',
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Input placeholder="OK" />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('pages.inbounds.form.responseHeaders')}
|
|
||||||
name={[
|
|
||||||
'streamSettings', 'tcpSettings', 'header',
|
|
||||||
'response', 'headers',
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<HeaderMapEditor mode="v2" />
|
|
||||||
</Form.Item>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</Form.Item>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{network === 'kcp' && (
|
{network === 'ws' && <WsForm />}
|
||||||
<>
|
|
||||||
<Form.Item label="MTU" name={['streamSettings', 'kcpSettings', 'mtu']}>
|
|
||||||
<InputNumber min={0} />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t('pages.inbounds.form.ttiMs')} name={['streamSettings', 'kcpSettings', 'tti']}>
|
|
||||||
<InputNumber min={0} />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('pages.inbounds.form.uplinkMbps')}
|
|
||||||
name={['streamSettings', 'kcpSettings', 'uplinkCapacity']}
|
|
||||||
>
|
|
||||||
<InputNumber min={0} />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('pages.inbounds.form.downlinkMbps')}
|
|
||||||
name={['streamSettings', 'kcpSettings', 'downlinkCapacity']}
|
|
||||||
>
|
|
||||||
<InputNumber min={0} />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('pages.inbounds.form.cwndMultiplier')}
|
|
||||||
name={['streamSettings', 'kcpSettings', 'cwndMultiplier']}
|
|
||||||
>
|
|
||||||
<InputNumber min={1} />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('pages.inbounds.form.maxSendingWindow')}
|
|
||||||
name={['streamSettings', 'kcpSettings', 'maxSendingWindow']}
|
|
||||||
>
|
|
||||||
<InputNumber min={0} />
|
|
||||||
</Form.Item>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{network === 'ws' && (
|
{network === 'grpc' && <GrpcForm />}
|
||||||
<>
|
|
||||||
<Form.Item label={t('host')} name={['streamSettings', 'wsSettings', 'host']}>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t('path')} name={['streamSettings', 'wsSettings', 'path']}>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('pages.inbounds.form.heartbeatPeriod')}
|
|
||||||
name={['streamSettings', 'wsSettings', 'heartbeatPeriod']}
|
|
||||||
>
|
|
||||||
<InputNumber min={0} />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('pages.inbounds.form.headers')}
|
|
||||||
name={['streamSettings', 'wsSettings', 'headers']}
|
|
||||||
>
|
|
||||||
<HeaderMapEditor mode="v1" />
|
|
||||||
</Form.Item>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{network === 'grpc' && (
|
{network === 'httpupgrade' && <HttpUpgradeForm />}
|
||||||
<>
|
|
||||||
<Form.Item
|
|
||||||
label={t('pages.inbounds.form.serviceName')}
|
|
||||||
name={['streamSettings', 'grpcSettings', 'serviceName']}
|
|
||||||
>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('pages.inbounds.form.authority')}
|
|
||||||
name={['streamSettings', 'grpcSettings', 'authority']}
|
|
||||||
>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('pages.inbounds.form.multiMode')}
|
|
||||||
name={['streamSettings', 'grpcSettings', 'multiMode']}
|
|
||||||
valuePropName="checked"
|
|
||||||
>
|
|
||||||
<Switch />
|
|
||||||
</Form.Item>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{network === 'httpupgrade' && (
|
|
||||||
<>
|
|
||||||
<Form.Item
|
|
||||||
label={t('host')}
|
|
||||||
name={['streamSettings', 'httpupgradeSettings', 'host']}
|
|
||||||
>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('path')}
|
|
||||||
name={['streamSettings', 'httpupgradeSettings', 'path']}
|
|
||||||
>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('pages.inbounds.form.headers')}
|
|
||||||
name={['streamSettings', 'httpupgradeSettings', 'headers']}
|
|
||||||
>
|
|
||||||
<HeaderMapEditor mode="v1" />
|
|
||||||
</Form.Item>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{network === 'xhttp' && (
|
{network === 'xhttp' && (
|
||||||
<>
|
<>
|
||||||
|
|
|
||||||
29
frontend/src/pages/xray/outbounds/transport/grpc.tsx
Normal file
29
frontend/src/pages/xray/outbounds/transport/grpc.tsx
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { Form, Input, Switch } from 'antd';
|
||||||
|
|
||||||
|
export default function GrpcForm() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form.Item
|
||||||
|
label={t('pages.inbounds.form.serviceName')}
|
||||||
|
name={['streamSettings', 'grpcSettings', 'serviceName']}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t('pages.inbounds.form.authority')}
|
||||||
|
name={['streamSettings', 'grpcSettings', 'authority']}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t('pages.inbounds.form.multiMode')}
|
||||||
|
name={['streamSettings', 'grpcSettings', 'multiMode']}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
30
frontend/src/pages/xray/outbounds/transport/httpupgrade.tsx
Normal file
30
frontend/src/pages/xray/outbounds/transport/httpupgrade.tsx
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { Form, Input } from 'antd';
|
||||||
|
|
||||||
|
import { HeaderMapEditor } from '@/components/form';
|
||||||
|
|
||||||
|
export default function HttpUpgradeForm() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form.Item
|
||||||
|
label={t('host')}
|
||||||
|
name={['streamSettings', 'httpupgradeSettings', 'host']}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t('path')}
|
||||||
|
name={['streamSettings', 'httpupgradeSettings', 'path']}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t('pages.inbounds.form.headers')}
|
||||||
|
name={['streamSettings', 'httpupgradeSettings', 'headers']}
|
||||||
|
>
|
||||||
|
<HeaderMapEditor mode="v1" />
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
5
frontend/src/pages/xray/outbounds/transport/index.ts
Normal file
5
frontend/src/pages/xray/outbounds/transport/index.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
export { default as RawForm } from './raw';
|
||||||
|
export { default as KcpForm } from './kcp';
|
||||||
|
export { default as WsForm } from './ws';
|
||||||
|
export { default as GrpcForm } from './grpc';
|
||||||
|
export { default as HttpUpgradeForm } from './httpupgrade';
|
||||||
40
frontend/src/pages/xray/outbounds/transport/kcp.tsx
Normal file
40
frontend/src/pages/xray/outbounds/transport/kcp.tsx
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { Form, InputNumber } from 'antd';
|
||||||
|
|
||||||
|
export default function KcpForm() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form.Item label="MTU" name={['streamSettings', 'kcpSettings', 'mtu']}>
|
||||||
|
<InputNumber min={0} />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label={t('pages.inbounds.form.ttiMs')} name={['streamSettings', 'kcpSettings', 'tti']}>
|
||||||
|
<InputNumber min={0} />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t('pages.inbounds.form.uplinkMbps')}
|
||||||
|
name={['streamSettings', 'kcpSettings', 'uplinkCapacity']}
|
||||||
|
>
|
||||||
|
<InputNumber min={0} />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t('pages.inbounds.form.downlinkMbps')}
|
||||||
|
name={['streamSettings', 'kcpSettings', 'downlinkCapacity']}
|
||||||
|
>
|
||||||
|
<InputNumber min={0} />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t('pages.inbounds.form.cwndMultiplier')}
|
||||||
|
name={['streamSettings', 'kcpSettings', 'cwndMultiplier']}
|
||||||
|
>
|
||||||
|
<InputNumber min={1} />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t('pages.inbounds.form.maxSendingWindow')}
|
||||||
|
name={['streamSettings', 'kcpSettings', 'maxSendingWindow']}
|
||||||
|
>
|
||||||
|
<InputNumber min={0} />
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
121
frontend/src/pages/xray/outbounds/transport/raw.tsx
Normal file
121
frontend/src/pages/xray/outbounds/transport/raw.tsx
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { Form, Input, Switch, type FormInstance } from 'antd';
|
||||||
|
|
||||||
|
import { HeaderMapEditor } from '@/components/form';
|
||||||
|
import type { OutboundFormValues } from '@/schemas/forms/outbound-form';
|
||||||
|
|
||||||
|
export default function RawForm({ form }: { form: FormInstance<OutboundFormValues> }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return (
|
||||||
|
<Form.Item shouldUpdate noStyle>
|
||||||
|
{() => {
|
||||||
|
const type =
|
||||||
|
form.getFieldValue([
|
||||||
|
'streamSettings',
|
||||||
|
'tcpSettings',
|
||||||
|
'header',
|
||||||
|
'type',
|
||||||
|
]) ?? 'none';
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form.Item label={`HTTP ${t('camouflage')}`}>
|
||||||
|
<Switch
|
||||||
|
checked={type === 'http'}
|
||||||
|
onChange={(checked) =>
|
||||||
|
form.setFieldValue(
|
||||||
|
['streamSettings', 'tcpSettings', 'header'],
|
||||||
|
checked
|
||||||
|
? {
|
||||||
|
type: 'http',
|
||||||
|
request: {
|
||||||
|
version: '1.1',
|
||||||
|
method: 'GET',
|
||||||
|
path: ['/'],
|
||||||
|
headers: {},
|
||||||
|
},
|
||||||
|
response: {
|
||||||
|
version: '1.1',
|
||||||
|
status: '200',
|
||||||
|
reason: 'OK',
|
||||||
|
headers: {},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: { type: 'none' },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
{type === 'http' && (
|
||||||
|
<>
|
||||||
|
<Form.Item
|
||||||
|
label={t('pages.inbounds.form.requestMethod')}
|
||||||
|
name={[
|
||||||
|
'streamSettings', 'tcpSettings', 'header',
|
||||||
|
'request', 'method',
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input placeholder="GET" />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t('pages.inbounds.form.requestVersion')}
|
||||||
|
name={[
|
||||||
|
'streamSettings', 'tcpSettings', 'header',
|
||||||
|
'request', 'version',
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input placeholder="1.1" />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t('pages.inbounds.form.requestHeaders')}
|
||||||
|
name={[
|
||||||
|
'streamSettings', 'tcpSettings', 'header',
|
||||||
|
'request', 'headers',
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<HeaderMapEditor mode="v2" />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
label={t('pages.inbounds.form.responseVersion')}
|
||||||
|
name={[
|
||||||
|
'streamSettings', 'tcpSettings', 'header',
|
||||||
|
'response', 'version',
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input placeholder="1.1" />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t('pages.inbounds.form.responseStatus')}
|
||||||
|
name={[
|
||||||
|
'streamSettings', 'tcpSettings', 'header',
|
||||||
|
'response', 'status',
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input placeholder="200" />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t('pages.inbounds.form.responseReason')}
|
||||||
|
name={[
|
||||||
|
'streamSettings', 'tcpSettings', 'header',
|
||||||
|
'response', 'reason',
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input placeholder="OK" />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t('pages.inbounds.form.responseHeaders')}
|
||||||
|
name={[
|
||||||
|
'streamSettings', 'tcpSettings', 'header',
|
||||||
|
'response', 'headers',
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<HeaderMapEditor mode="v2" />
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Form.Item>
|
||||||
|
);
|
||||||
|
}
|
||||||
30
frontend/src/pages/xray/outbounds/transport/ws.tsx
Normal file
30
frontend/src/pages/xray/outbounds/transport/ws.tsx
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { Form, Input, InputNumber } from 'antd';
|
||||||
|
|
||||||
|
import { HeaderMapEditor } from '@/components/form';
|
||||||
|
|
||||||
|
export default function WsForm() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form.Item label={t('host')} name={['streamSettings', 'wsSettings', 'host']}>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label={t('path')} name={['streamSettings', 'wsSettings', 'path']}>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t('pages.inbounds.form.heartbeatPeriod')}
|
||||||
|
name={['streamSettings', 'wsSettings', 'heartbeatPeriod']}
|
||||||
|
>
|
||||||
|
<InputNumber min={0} />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t('pages.inbounds.form.headers')}
|
||||||
|
name={['streamSettings', 'wsSettings', 'headers']}
|
||||||
|
>
|
||||||
|
<HeaderMapEditor mode="v1" />
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue