refactor(frontend): fold HysteriaMasqueradeForm into the hysteria forms

Inline the masquerade fields directly into both hysteria transport forms
(inbounds/form/protocols/hysteria + xray/outbounds/transport/hysteria)
and delete the shared lib/xray/forms/transport/HysteriaMasqueradeForm so
each hysteria form is self-contained. The masquerade JSX is unchanged;
form is typed as the untyped FormInstance (as the shared component was)
so the masquerade name paths still resolve. No behavior change.
This commit is contained in:
MHSanaei 2026-05-30 21:04:37 +02:00
parent 8a34eeedc9
commit 8cddff2c41
No known key found for this signature in database
GPG key ID: 7E4060F2FBE5AB7A
4 changed files with 213 additions and 131 deletions

View file

@ -1,120 +0,0 @@
import { useTranslation } from 'react-i18next';
import { Form, Input, InputNumber, Select, Switch } from 'antd';
import type { FormInstance } from 'antd';
import { HeaderMapEditor } from '@/components/form';
const MASQ_PATH = ['streamSettings', 'hysteriaSettings', 'masquerade'];
interface HysteriaMasqueradeFormProps {
form: FormInstance;
}
export default function HysteriaMasqueradeForm({ form }: HysteriaMasqueradeFormProps) {
const { t } = useTranslation();
return (
<>
<Form.Item label={t('pages.inbounds.form.masquerade')}>
<Form.Item shouldUpdate noStyle>
{() => {
const m = form.getFieldValue(MASQ_PATH);
return (
<Switch
checked={!!m}
onChange={(checked) =>
form.setFieldValue(
MASQ_PATH,
checked
? {
type: '', dir: '', url: '',
rewriteHost: false, insecure: false,
content: '', headers: {}, statusCode: 0,
}
: undefined,
)
}
/>
);
}}
</Form.Item>
</Form.Item>
<Form.Item shouldUpdate noStyle>
{() => {
const m = form.getFieldValue(MASQ_PATH) as { type?: string } | undefined;
if (!m) return null;
return (
<>
<Form.Item
label={t('pages.inbounds.form.type')}
name={[...MASQ_PATH, 'type']}
>
<Select
options={[
{ value: '', label: 'default (404 page)' },
{ value: 'proxy', label: 'proxy (reverse proxy)' },
{ value: 'file', label: 'file (serve directory)' },
{ value: 'string', label: 'string (fixed body)' },
]}
/>
</Form.Item>
{m.type === 'proxy' && (
<>
<Form.Item
label={t('pages.inbounds.form.upstreamUrl')}
name={[...MASQ_PATH, 'url']}
>
<Input placeholder="https://www.example.com" />
</Form.Item>
<Form.Item
label={t('pages.inbounds.form.rewriteHost')}
name={[...MASQ_PATH, 'rewriteHost']}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t('pages.inbounds.form.skipTlsVerify')}
name={[...MASQ_PATH, 'insecure']}
valuePropName="checked"
>
<Switch />
</Form.Item>
</>
)}
{m.type === 'file' && (
<Form.Item
label={t('pages.inbounds.form.directory')}
name={[...MASQ_PATH, 'dir']}
>
<Input placeholder="/var/www/html" />
</Form.Item>
)}
{m.type === 'string' && (
<>
<Form.Item
label={t('pages.inbounds.form.statusCode')}
name={[...MASQ_PATH, 'statusCode']}
>
<InputNumber min={0} max={599} style={{ width: '100%' }} />
</Form.Item>
<Form.Item
label={t('pages.inbounds.form.body')}
name={[...MASQ_PATH, 'content']}
>
<Input.TextArea autoSize={{ minRows: 3 }} />
</Form.Item>
<Form.Item
label={t('pages.inbounds.form.headers')}
name={[...MASQ_PATH, 'headers']}
>
<HeaderMapEditor mode="v1" />
</Form.Item>
</>
)}
</>
);
}}
</Form.Item>
</>
);
}

View file

@ -1,2 +1 @@
export { default as FinalMaskForm } from './FinalMaskForm';
export { default as HysteriaMasqueradeForm } from './HysteriaMasqueradeForm';

View file

@ -1,10 +1,11 @@
import { useTranslation } from 'react-i18next';
import { Form, InputNumber, type FormInstance } from 'antd';
import { Form, Input, InputNumber, Select, Switch, type FormInstance } from 'antd';
import { HysteriaMasqueradeForm } from '@/lib/xray/forms/transport';
import type { InboundFormValues } from '@/schemas/forms/inbound-form';
import { HeaderMapEditor } from '@/components/form';
export default function HysteriaFields({ form }: { form: FormInstance<InboundFormValues> }) {
const MASQ_PATH = ['streamSettings', 'hysteriaSettings', 'masquerade'];
export default function HysteriaFields({ form }: { form: FormInstance }) {
const { t } = useTranslation();
return (
<>
@ -21,7 +22,107 @@ export default function HysteriaFields({ form }: { form: FormInstance<InboundFor
<InputNumber min={1} style={{ width: '100%' }} />
</Form.Item>
<HysteriaMasqueradeForm form={form} />
<Form.Item label={t('pages.inbounds.form.masquerade')}>
<Form.Item shouldUpdate noStyle>
{() => {
const m = form.getFieldValue(MASQ_PATH);
return (
<Switch
checked={!!m}
onChange={(checked) =>
form.setFieldValue(
MASQ_PATH,
checked
? {
type: '', dir: '', url: '',
rewriteHost: false, insecure: false,
content: '', headers: {}, statusCode: 0,
}
: undefined,
)
}
/>
);
}}
</Form.Item>
</Form.Item>
<Form.Item shouldUpdate noStyle>
{() => {
const m = form.getFieldValue(MASQ_PATH) as { type?: string } | undefined;
if (!m) return null;
return (
<>
<Form.Item
label={t('pages.inbounds.form.type')}
name={[...MASQ_PATH, 'type']}
>
<Select
options={[
{ value: '', label: 'default (404 page)' },
{ value: 'proxy', label: 'proxy (reverse proxy)' },
{ value: 'file', label: 'file (serve directory)' },
{ value: 'string', label: 'string (fixed body)' },
]}
/>
</Form.Item>
{m.type === 'proxy' && (
<>
<Form.Item
label={t('pages.inbounds.form.upstreamUrl')}
name={[...MASQ_PATH, 'url']}
>
<Input placeholder="https://www.example.com" />
</Form.Item>
<Form.Item
label={t('pages.inbounds.form.rewriteHost')}
name={[...MASQ_PATH, 'rewriteHost']}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t('pages.inbounds.form.skipTlsVerify')}
name={[...MASQ_PATH, 'insecure']}
valuePropName="checked"
>
<Switch />
</Form.Item>
</>
)}
{m.type === 'file' && (
<Form.Item
label={t('pages.inbounds.form.directory')}
name={[...MASQ_PATH, 'dir']}
>
<Input placeholder="/var/www/html" />
</Form.Item>
)}
{m.type === 'string' && (
<>
<Form.Item
label={t('pages.inbounds.form.statusCode')}
name={[...MASQ_PATH, 'statusCode']}
>
<InputNumber min={0} max={599} style={{ width: '100%' }} />
</Form.Item>
<Form.Item
label={t('pages.inbounds.form.body')}
name={[...MASQ_PATH, 'content']}
>
<Input.TextArea autoSize={{ minRows: 3 }} />
</Form.Item>
<Form.Item
label={t('pages.inbounds.form.headers')}
name={[...MASQ_PATH, 'headers']}
>
<HeaderMapEditor mode="v1" />
</Form.Item>
</>
)}
</>
);
}}
</Form.Item>
</>
);
}

View file

@ -1,10 +1,11 @@
import { useTranslation } from 'react-i18next';
import { Form, Input, InputNumber, type FormInstance } from 'antd';
import { Form, Input, InputNumber, Select, Switch, type FormInstance } from 'antd';
import { HysteriaMasqueradeForm } from '@/lib/xray/forms/transport';
import type { OutboundFormValues } from '@/schemas/forms/outbound-form';
import { HeaderMapEditor } from '@/components/form';
export default function HysteriaForm({ form }: { form: FormInstance<OutboundFormValues> }) {
const MASQ_PATH = ['streamSettings', 'hysteriaSettings', 'masquerade'];
export default function HysteriaForm({ form }: { form: FormInstance }) {
const { t } = useTranslation();
return (
<>
@ -26,7 +27,108 @@ export default function HysteriaForm({ form }: { form: FormInstance<OutboundForm
>
<InputNumber min={1} style={{ width: '100%' }} />
</Form.Item>
<HysteriaMasqueradeForm form={form} />
<Form.Item label={t('pages.inbounds.form.masquerade')}>
<Form.Item shouldUpdate noStyle>
{() => {
const m = form.getFieldValue(MASQ_PATH);
return (
<Switch
checked={!!m}
onChange={(checked) =>
form.setFieldValue(
MASQ_PATH,
checked
? {
type: '', dir: '', url: '',
rewriteHost: false, insecure: false,
content: '', headers: {}, statusCode: 0,
}
: undefined,
)
}
/>
);
}}
</Form.Item>
</Form.Item>
<Form.Item shouldUpdate noStyle>
{() => {
const m = form.getFieldValue(MASQ_PATH) as { type?: string } | undefined;
if (!m) return null;
return (
<>
<Form.Item
label={t('pages.inbounds.form.type')}
name={[...MASQ_PATH, 'type']}
>
<Select
options={[
{ value: '', label: 'default (404 page)' },
{ value: 'proxy', label: 'proxy (reverse proxy)' },
{ value: 'file', label: 'file (serve directory)' },
{ value: 'string', label: 'string (fixed body)' },
]}
/>
</Form.Item>
{m.type === 'proxy' && (
<>
<Form.Item
label={t('pages.inbounds.form.upstreamUrl')}
name={[...MASQ_PATH, 'url']}
>
<Input placeholder="https://www.example.com" />
</Form.Item>
<Form.Item
label={t('pages.inbounds.form.rewriteHost')}
name={[...MASQ_PATH, 'rewriteHost']}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t('pages.inbounds.form.skipTlsVerify')}
name={[...MASQ_PATH, 'insecure']}
valuePropName="checked"
>
<Switch />
</Form.Item>
</>
)}
{m.type === 'file' && (
<Form.Item
label={t('pages.inbounds.form.directory')}
name={[...MASQ_PATH, 'dir']}
>
<Input placeholder="/var/www/html" />
</Form.Item>
)}
{m.type === 'string' && (
<>
<Form.Item
label={t('pages.inbounds.form.statusCode')}
name={[...MASQ_PATH, 'statusCode']}
>
<InputNumber min={0} max={599} style={{ width: '100%' }} />
</Form.Item>
<Form.Item
label={t('pages.inbounds.form.body')}
name={[...MASQ_PATH, 'content']}
>
<Input.TextArea autoSize={{ minRows: 3 }} />
</Form.Item>
<Form.Item
label={t('pages.inbounds.form.headers')}
name={[...MASQ_PATH, 'headers']}
>
<HeaderMapEditor mode="v1" />
</Form.Item>
</>
)}
</>
);
}}
</Form.Item>
</>
);
}