mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-05-31 18:24:10 +00:00
feat(inbound-form): salamander auto-seed for Hysteria + modernize random buttons
Picking Hysteria from the protocol select used to leave finalmask.udp
empty, so the listener went out without obfs unless the admin added
the salamander wrapper by hand. Hook into onValuesChange so switching
to Hysteria seeds finalmask.udp with
{type: 'salamander', settings: {password: <random>}} alongside the
hysteriaSettings / tlsSettings reset already happening there.
Also modernise the SyncOutlined-in-label "random" affordances on
Shadowsocks password, WireGuard secret key (server + per-peer), and
Reality target / SNI / shortIds into proper icon buttons inside a
Space.Compact next to the field. The old pattern dropped a tiny
clickable icon into the form-item label, which was easy to miss and
inconsistent with the other action buttons in the modal.
This commit is contained in:
parent
222e000b3b
commit
9d2a4f217e
2 changed files with 86 additions and 75 deletions
|
|
@ -424,8 +424,19 @@ function UdpMaskItem({
|
||||||
const type = getFieldValue([...absolutePath, 'type']) as string | undefined;
|
const type = getFieldValue([...absolutePath, 'type']) as string | undefined;
|
||||||
if (type === 'mkcp-aes128gcm' || type === 'salamander') {
|
if (type === 'mkcp-aes128gcm' || type === 'salamander') {
|
||||||
return (
|
return (
|
||||||
<Form.Item label="Password" name={[fieldName, 'settings', 'password']}>
|
<Form.Item label="Password">
|
||||||
<Input placeholder="Obfuscation password" />
|
<Space.Compact block>
|
||||||
|
<Form.Item name={[fieldName, 'settings', 'password']} noStyle>
|
||||||
|
<Input placeholder="Obfuscation password" style={{ width: 'calc(100% - 32px)' }} />
|
||||||
|
</Form.Item>
|
||||||
|
<Button
|
||||||
|
icon={<ReloadOutlined />}
|
||||||
|
onClick={() => form.setFieldValue(
|
||||||
|
[...absolutePath, 'settings', 'password'],
|
||||||
|
RandomUtil.randomLowerAndNum(16),
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Space.Compact>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import {
|
||||||
DeleteOutlined,
|
DeleteOutlined,
|
||||||
MinusOutlined,
|
MinusOutlined,
|
||||||
PlusOutlined,
|
PlusOutlined,
|
||||||
SyncOutlined,
|
ReloadOutlined,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
|
|
||||||
import { HttpUtil, NumberFormatter, RandomUtil, SizeFormatter, Wireguard } from '@/utils';
|
import { HttpUtil, NumberFormatter, RandomUtil, SizeFormatter, Wireguard } from '@/utils';
|
||||||
|
|
@ -762,6 +762,18 @@ export default function InboundFormModal({
|
||||||
security: 'tls',
|
security: 'tls',
|
||||||
hysteriaSettings: HysteriaStreamSettingsSchema.parse({}),
|
hysteriaSettings: HysteriaStreamSettingsSchema.parse({}),
|
||||||
tlsSettings: tls,
|
tlsSettings: tls,
|
||||||
|
// Hysteria2 needs an obfs wrapper on the FinalMask side; seed
|
||||||
|
// it with salamander + a random password so the listener boots
|
||||||
|
// with a usable default. Re-selecting Hysteria from another
|
||||||
|
// protocol re-runs this and refreshes the password — that's
|
||||||
|
// intentional, the form was already being reset.
|
||||||
|
finalmask: {
|
||||||
|
tcp: [],
|
||||||
|
udp: [{
|
||||||
|
type: 'salamander',
|
||||||
|
settings: { password: RandomUtil.randomLowerAndNum(16) },
|
||||||
|
}],
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const current = form.getFieldValue('streamSettings') as { network?: string } | undefined;
|
const current = form.getFieldValue('streamSettings') as { network?: string } | undefined;
|
||||||
|
|
@ -1048,16 +1060,13 @@ export default function InboundFormModal({
|
||||||
<>
|
<>
|
||||||
{protocol === Protocols.WIREGUARD && (
|
{protocol === Protocols.WIREGUARD && (
|
||||||
<>
|
<>
|
||||||
<Form.Item
|
<Form.Item label="Secret key">
|
||||||
name={['settings', 'secretKey']}
|
<Space.Compact block>
|
||||||
label={
|
<Form.Item name={['settings', 'secretKey']} noStyle>
|
||||||
<>
|
<Input style={{ width: 'calc(100% - 32px)' }} />
|
||||||
Secret key{' '}
|
</Form.Item>
|
||||||
<SyncOutlined className="random-icon" onClick={regenInboundWg} />
|
<Button icon={<ReloadOutlined />} onClick={regenInboundWg} />
|
||||||
</>
|
</Space.Compact>
|
||||||
}
|
|
||||||
>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="Public key">
|
<Form.Item label="Public key">
|
||||||
<Input value={wgPubKey} disabled />
|
<Input value={wgPubKey} disabled />
|
||||||
|
|
@ -1106,19 +1115,16 @@ export default function InboundFormModal({
|
||||||
)}
|
)}
|
||||||
</Space>
|
</Space>
|
||||||
</Divider>
|
</Divider>
|
||||||
<Form.Item
|
<Form.Item label="Secret key">
|
||||||
name={[field.name, 'privateKey']}
|
<Space.Compact block>
|
||||||
label={
|
<Form.Item name={[field.name, 'privateKey']} noStyle>
|
||||||
<>
|
<Input style={{ width: 'calc(100% - 32px)' }} />
|
||||||
Secret key{' '}
|
</Form.Item>
|
||||||
<SyncOutlined
|
<Button
|
||||||
className="random-icon"
|
icon={<ReloadOutlined />}
|
||||||
onClick={() => regenWgPeerKeypair(field.name)}
|
onClick={() => regenWgPeerKeypair(field.name)}
|
||||||
/>
|
/>
|
||||||
</>
|
</Space.Compact>
|
||||||
}
|
|
||||||
>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item name={[field.name, 'publicKey']} label="Public key">
|
<Form.Item name={[field.name, 'publicKey']} label="Public key">
|
||||||
<Input />
|
<Input />
|
||||||
|
|
@ -1362,13 +1368,13 @@ export default function InboundFormModal({
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
{isSSWith2022 && (
|
{isSSWith2022 && (
|
||||||
<Form.Item
|
<Form.Item label="Password">
|
||||||
name={['settings', 'password']}
|
<Space.Compact block>
|
||||||
label={
|
<Form.Item name={['settings', 'password']} noStyle>
|
||||||
<>
|
<Input style={{ width: 'calc(100% - 32px)' }} />
|
||||||
Password{' '}
|
</Form.Item>
|
||||||
<SyncOutlined
|
<Button
|
||||||
className="random-icon"
|
icon={<ReloadOutlined />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const method = form.getFieldValue(['settings', 'method']);
|
const method = form.getFieldValue(['settings', 'method']);
|
||||||
form.setFieldValue(
|
form.setFieldValue(
|
||||||
|
|
@ -1377,10 +1383,7 @@ export default function InboundFormModal({
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</>
|
</Space.Compact>
|
||||||
}
|
|
||||||
>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
<Form.Item name={['settings', 'network']} label="Network">
|
<Form.Item name={['settings', 'network']} label="Network">
|
||||||
|
|
@ -2759,27 +2762,24 @@ export default function InboundFormModal({
|
||||||
options={Object.values(UTLS_FINGERPRINT).map((fp) => ({ value: fp, label: fp }))}
|
options={Object.values(UTLS_FINGERPRINT).map((fp) => ({ value: fp, label: fp }))}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item label="Target">
|
||||||
name={['streamSettings', 'realitySettings', 'target']}
|
<Space.Compact block>
|
||||||
label={
|
<Form.Item name={['streamSettings', 'realitySettings', 'target']} noStyle>
|
||||||
<>
|
<Input style={{ width: 'calc(100% - 32px)' }} />
|
||||||
Target{' '}
|
|
||||||
<SyncOutlined className="random-icon" onClick={randomizeRealityTarget} />
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Button icon={<ReloadOutlined />} onClick={randomizeRealityTarget} />
|
||||||
|
</Space.Compact>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="SNI">
|
||||||
|
<Space.Compact block style={{ display: 'flex' }}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={['streamSettings', 'realitySettings', 'serverNames']}
|
name={['streamSettings', 'realitySettings', 'serverNames']}
|
||||||
label={
|
noStyle
|
||||||
<>
|
|
||||||
SNI{' '}
|
|
||||||
<SyncOutlined className="random-icon" onClick={randomizeRealityTarget} />
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<Select mode="tags" tokenSeparators={[',']} style={{ width: '100%' }} />
|
<Select mode="tags" tokenSeparators={[',']} style={{ flex: 1 }} />
|
||||||
|
</Form.Item>
|
||||||
|
<Button icon={<ReloadOutlined />} onClick={randomizeRealityTarget} />
|
||||||
|
</Space.Compact>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={['streamSettings', 'realitySettings', 'maxTimediff']}
|
name={['streamSettings', 'realitySettings', 'maxTimediff']}
|
||||||
|
|
@ -2799,16 +2799,16 @@ export default function InboundFormModal({
|
||||||
>
|
>
|
||||||
<Input placeholder="25.9.11" />
|
<Input placeholder="25.9.11" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Form.Item label="Short IDs">
|
||||||
|
<Space.Compact block style={{ display: 'flex' }}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={['streamSettings', 'realitySettings', 'shortIds']}
|
name={['streamSettings', 'realitySettings', 'shortIds']}
|
||||||
label={
|
noStyle
|
||||||
<>
|
|
||||||
Short IDs{' '}
|
|
||||||
<SyncOutlined className="random-icon" onClick={randomizeShortIds} />
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<Select mode="tags" tokenSeparators={[',']} style={{ width: '100%' }} />
|
<Select mode="tags" tokenSeparators={[',']} style={{ flex: 1 }} />
|
||||||
|
</Form.Item>
|
||||||
|
<Button icon={<ReloadOutlined />} onClick={randomizeShortIds} />
|
||||||
|
</Space.Compact>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={['streamSettings', 'realitySettings', 'settings', 'spiderX']}
|
name={['streamSettings', 'realitySettings', 'settings', 'spiderX']}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue