mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-05 12:44:22 +00:00
feat(clients): per-client VMess security in client form
Restores the VMess `security` selector on the client form (auto, aes-128-gcm, chacha20-poly1305, none, zero) and surfaces it only when at least one attached inbound is VMess. The value rides into the share link via the existing `scy=` field in genVmessLink; the panel persists it on ClientRecord and in the inbound's settings.clients so the link generator can read it back. Adds the pages.clients.vmessSecurity i18n key in en-US and fa-IR.
This commit is contained in:
parent
5f9528862b
commit
a9b8458bde
4 changed files with 33 additions and 0 deletions
|
|
@ -26,6 +26,7 @@ import type { ClientRecord, InboundOption } from '@/hooks/useClients';
|
|||
import { ClientFormSchema, ClientCreateFormSchema } from '@/schemas/client';
|
||||
|
||||
const FLOW_OPTIONS = Object.values(TLS_FLOW_CONTROL);
|
||||
const VMESS_SECURITY_OPTIONS = ['auto', 'aes-128-gcm', 'chacha20-poly1305', 'none', 'zero'] as const;
|
||||
|
||||
const MULTI_CLIENT_PROTOCOLS = new Set([
|
||||
'shadowsocks', 'vless', 'vmess', 'trojan', 'hysteria',
|
||||
|
|
@ -77,6 +78,7 @@ interface FormState {
|
|||
password: string;
|
||||
auth: string;
|
||||
flow: string;
|
||||
security: string;
|
||||
reverseTag: string;
|
||||
totalGB: number;
|
||||
expiryDate: Dayjs | null;
|
||||
|
|
@ -99,6 +101,7 @@ function emptyForm(): FormState {
|
|||
password: '',
|
||||
auth: '',
|
||||
flow: '',
|
||||
security: 'auto',
|
||||
reverseTag: '',
|
||||
totalGB: 0,
|
||||
expiryDate: null,
|
||||
|
|
@ -163,6 +166,7 @@ export default function ClientFormModal({
|
|||
password: client.password || '',
|
||||
auth: client.auth || '',
|
||||
flow: client.flow || '',
|
||||
security: client.security || 'auto',
|
||||
reverseTag: client.reverse?.tag || '',
|
||||
totalGB: bytesToGB(client.totalGB || 0),
|
||||
reset: Number(client.reset) || 0,
|
||||
|
|
@ -214,6 +218,14 @@ export default function ClientFormModal({
|
|||
return ids;
|
||||
}, [inbounds]);
|
||||
|
||||
const vmessIds = useMemo(() => {
|
||||
const ids = new Set<number>();
|
||||
for (const row of inbounds || []) {
|
||||
if (row && row.protocol === 'vmess') ids.add(row.id);
|
||||
}
|
||||
return ids;
|
||||
}, [inbounds]);
|
||||
|
||||
const showFlow = useMemo(
|
||||
() => (form.inboundIds || []).some((id) => flowCapableIds.has(id)),
|
||||
[form.inboundIds, flowCapableIds],
|
||||
|
|
@ -224,6 +236,11 @@ export default function ClientFormModal({
|
|||
[form.inboundIds, vlessLikeIds],
|
||||
);
|
||||
|
||||
const showSecurity = useMemo(
|
||||
() => (form.inboundIds || []).some((id) => vmessIds.has(id)),
|
||||
[form.inboundIds, vmessIds],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!showFlow && form.flow) {
|
||||
|
||||
|
|
@ -286,6 +303,7 @@ export default function ClientFormModal({
|
|||
password: form.password,
|
||||
auth: form.auth,
|
||||
flow: form.flow,
|
||||
security: form.security,
|
||||
reverseTag: form.reverseTag,
|
||||
totalGB: form.totalGB,
|
||||
delayedStart: form.delayedStart,
|
||||
|
|
@ -313,6 +331,7 @@ export default function ClientFormModal({
|
|||
password: form.password,
|
||||
auth: form.auth,
|
||||
flow: showFlow ? (form.flow || '') : '',
|
||||
security: showSecurity ? (form.security || 'auto') : 'auto',
|
||||
totalGB: gbToBytes(form.totalGB),
|
||||
expiryTime,
|
||||
reset: Number(form.reset) || 0,
|
||||
|
|
@ -497,6 +516,17 @@ export default function ClientFormModal({
|
|||
</Form.Item>
|
||||
</Col>
|
||||
)}
|
||||
{showSecurity && (
|
||||
<Col xs={24} md={12}>
|
||||
<Form.Item label={t('pages.clients.vmessSecurity')}>
|
||||
<Select
|
||||
value={form.security}
|
||||
onChange={(v) => update('security', v)}
|
||||
options={VMESS_SECURITY_OPTIONS.map((k) => ({ value: k, label: k }))}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
)}
|
||||
</Row>
|
||||
|
||||
<Row gutter={16}>
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ export const ClientFormSchema = z.object({
|
|||
password: z.string(),
|
||||
auth: z.string(),
|
||||
flow: z.string(),
|
||||
security: z.string(),
|
||||
reverseTag: z.string(),
|
||||
totalGB: z.number().min(0),
|
||||
delayedStart: z.boolean(),
|
||||
|
|
|
|||
|
|
@ -545,6 +545,7 @@
|
|||
"hysteriaAuth": "Hysteria Auth",
|
||||
"uuid": "UUID",
|
||||
"flow": "Flow",
|
||||
"vmessSecurity": "VMess Security",
|
||||
"reverseTag": "Reverse tag",
|
||||
"reverseTagPlaceholder": "Optional reverse tag",
|
||||
"telegramId": "Telegram user ID",
|
||||
|
|
|
|||
|
|
@ -509,6 +509,7 @@
|
|||
"hysteriaAuth": "Auth (هیستریا)",
|
||||
"uuid": "UUID",
|
||||
"flow": "Flow",
|
||||
"vmessSecurity": "امنیت VMess",
|
||||
"reverseTag": "Reverse tag",
|
||||
"reverseTagPlaceholder": "Reverse tag اختیاری",
|
||||
"telegramId": "شناسه کاربر تلگرام",
|
||||
|
|
|
|||
Loading…
Reference in a new issue