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';
|
import { ClientFormSchema, ClientCreateFormSchema } from '@/schemas/client';
|
||||||
|
|
||||||
const FLOW_OPTIONS = Object.values(TLS_FLOW_CONTROL);
|
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([
|
const MULTI_CLIENT_PROTOCOLS = new Set([
|
||||||
'shadowsocks', 'vless', 'vmess', 'trojan', 'hysteria',
|
'shadowsocks', 'vless', 'vmess', 'trojan', 'hysteria',
|
||||||
|
|
@ -77,6 +78,7 @@ interface FormState {
|
||||||
password: string;
|
password: string;
|
||||||
auth: string;
|
auth: string;
|
||||||
flow: string;
|
flow: string;
|
||||||
|
security: string;
|
||||||
reverseTag: string;
|
reverseTag: string;
|
||||||
totalGB: number;
|
totalGB: number;
|
||||||
expiryDate: Dayjs | null;
|
expiryDate: Dayjs | null;
|
||||||
|
|
@ -99,6 +101,7 @@ function emptyForm(): FormState {
|
||||||
password: '',
|
password: '',
|
||||||
auth: '',
|
auth: '',
|
||||||
flow: '',
|
flow: '',
|
||||||
|
security: 'auto',
|
||||||
reverseTag: '',
|
reverseTag: '',
|
||||||
totalGB: 0,
|
totalGB: 0,
|
||||||
expiryDate: null,
|
expiryDate: null,
|
||||||
|
|
@ -163,6 +166,7 @@ export default function ClientFormModal({
|
||||||
password: client.password || '',
|
password: client.password || '',
|
||||||
auth: client.auth || '',
|
auth: client.auth || '',
|
||||||
flow: client.flow || '',
|
flow: client.flow || '',
|
||||||
|
security: client.security || 'auto',
|
||||||
reverseTag: client.reverse?.tag || '',
|
reverseTag: client.reverse?.tag || '',
|
||||||
totalGB: bytesToGB(client.totalGB || 0),
|
totalGB: bytesToGB(client.totalGB || 0),
|
||||||
reset: Number(client.reset) || 0,
|
reset: Number(client.reset) || 0,
|
||||||
|
|
@ -214,6 +218,14 @@ export default function ClientFormModal({
|
||||||
return ids;
|
return ids;
|
||||||
}, [inbounds]);
|
}, [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(
|
const showFlow = useMemo(
|
||||||
() => (form.inboundIds || []).some((id) => flowCapableIds.has(id)),
|
() => (form.inboundIds || []).some((id) => flowCapableIds.has(id)),
|
||||||
[form.inboundIds, flowCapableIds],
|
[form.inboundIds, flowCapableIds],
|
||||||
|
|
@ -224,6 +236,11 @@ export default function ClientFormModal({
|
||||||
[form.inboundIds, vlessLikeIds],
|
[form.inboundIds, vlessLikeIds],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const showSecurity = useMemo(
|
||||||
|
() => (form.inboundIds || []).some((id) => vmessIds.has(id)),
|
||||||
|
[form.inboundIds, vmessIds],
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!showFlow && form.flow) {
|
if (!showFlow && form.flow) {
|
||||||
|
|
||||||
|
|
@ -286,6 +303,7 @@ export default function ClientFormModal({
|
||||||
password: form.password,
|
password: form.password,
|
||||||
auth: form.auth,
|
auth: form.auth,
|
||||||
flow: form.flow,
|
flow: form.flow,
|
||||||
|
security: form.security,
|
||||||
reverseTag: form.reverseTag,
|
reverseTag: form.reverseTag,
|
||||||
totalGB: form.totalGB,
|
totalGB: form.totalGB,
|
||||||
delayedStart: form.delayedStart,
|
delayedStart: form.delayedStart,
|
||||||
|
|
@ -313,6 +331,7 @@ export default function ClientFormModal({
|
||||||
password: form.password,
|
password: form.password,
|
||||||
auth: form.auth,
|
auth: form.auth,
|
||||||
flow: showFlow ? (form.flow || '') : '',
|
flow: showFlow ? (form.flow || '') : '',
|
||||||
|
security: showSecurity ? (form.security || 'auto') : 'auto',
|
||||||
totalGB: gbToBytes(form.totalGB),
|
totalGB: gbToBytes(form.totalGB),
|
||||||
expiryTime,
|
expiryTime,
|
||||||
reset: Number(form.reset) || 0,
|
reset: Number(form.reset) || 0,
|
||||||
|
|
@ -497,6 +516,17 @@ export default function ClientFormModal({
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</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>
|
||||||
|
|
||||||
<Row gutter={16}>
|
<Row gutter={16}>
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,7 @@ export const ClientFormSchema = z.object({
|
||||||
password: z.string(),
|
password: z.string(),
|
||||||
auth: z.string(),
|
auth: z.string(),
|
||||||
flow: z.string(),
|
flow: z.string(),
|
||||||
|
security: z.string(),
|
||||||
reverseTag: z.string(),
|
reverseTag: z.string(),
|
||||||
totalGB: z.number().min(0),
|
totalGB: z.number().min(0),
|
||||||
delayedStart: z.boolean(),
|
delayedStart: z.boolean(),
|
||||||
|
|
|
||||||
|
|
@ -545,6 +545,7 @@
|
||||||
"hysteriaAuth": "Hysteria Auth",
|
"hysteriaAuth": "Hysteria Auth",
|
||||||
"uuid": "UUID",
|
"uuid": "UUID",
|
||||||
"flow": "Flow",
|
"flow": "Flow",
|
||||||
|
"vmessSecurity": "VMess Security",
|
||||||
"reverseTag": "Reverse tag",
|
"reverseTag": "Reverse tag",
|
||||||
"reverseTagPlaceholder": "Optional reverse tag",
|
"reverseTagPlaceholder": "Optional reverse tag",
|
||||||
"telegramId": "Telegram user ID",
|
"telegramId": "Telegram user ID",
|
||||||
|
|
|
||||||
|
|
@ -509,6 +509,7 @@
|
||||||
"hysteriaAuth": "Auth (هیستریا)",
|
"hysteriaAuth": "Auth (هیستریا)",
|
||||||
"uuid": "UUID",
|
"uuid": "UUID",
|
||||||
"flow": "Flow",
|
"flow": "Flow",
|
||||||
|
"vmessSecurity": "امنیت VMess",
|
||||||
"reverseTag": "Reverse tag",
|
"reverseTag": "Reverse tag",
|
||||||
"reverseTagPlaceholder": "Reverse tag اختیاری",
|
"reverseTagPlaceholder": "Reverse tag اختیاری",
|
||||||
"telegramId": "شناسه کاربر تلگرام",
|
"telegramId": "شناسه کاربر تلگرام",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue