From 102465f9d11852ba5741107c93d0511c9af39b04 Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Tue, 26 May 2026 02:09:48 +0200 Subject: [PATCH] feat(frontend): protocol tab VLESS auth on InboundFormModal.new.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the protocol tab to the sibling-file rewrite — currently only the VLESS section, which lays out decryption/encryption inputs and the three buttons that drive them: Get New x25519, Get New mlkem768, Clear. getNewVlessEnc + clearVlessEnc are ported from the legacy modal as pure setFieldValue paths into ['settings', 'decryption'] / ['settings', 'encryption'] — no class methods, no inboundRef. The matchesVlessAuth helper mirrors the legacy fuzzy label-matching so the backend response shape stays the only source of truth. selectedVlessAuth derives the displayed auth label from the encryption string via Form.useWatch — same heuristic as the legacy modal (.length > 300 → mlkem768, otherwise x25519). Tab spread is conditional: the protocol tab only appears when protocol === 'vless' right now. As more protocol sections land (shadowsocks, http/mixed, tunnel, tun, wireguard) the condition will widen to cover each one. --- .../pages/inbounds/InboundFormModal.new.tsx | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/frontend/src/pages/inbounds/InboundFormModal.new.tsx b/frontend/src/pages/inbounds/InboundFormModal.new.tsx index 1c7da945..eb3f7f40 100644 --- a/frontend/src/pages/inbounds/InboundFormModal.new.tsx +++ b/frontend/src/pages/inbounds/InboundFormModal.new.tsx @@ -2,15 +2,18 @@ import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import dayjs from 'dayjs'; import { + Button, Checkbox, Form, Input, InputNumber, Modal, Select, + Space, Switch, Tabs, Tooltip, + Typography, message, } from 'antd'; @@ -36,6 +39,8 @@ import type { NodeRecord } from '@/api/queries/useNodesQuery'; // InboundsPage continues to render the old InboundFormModal.tsx until the // atomic swap at the end (Core Decision 7). +const { Text } = Typography; + const PROTOCOL_OPTIONS = Object.values(Protocols).map((p) => ({ value: p, label: p })); const TRAFFIC_RESETS = ['never', 'hourly', 'daily', 'weekly', 'monthly'] as const; const NODE_ELIGIBLE_PROTOCOLS = new Set([ @@ -89,6 +94,52 @@ export default function InboundFormModalNew({ const protocol = Form.useWatch('protocol', form) ?? ''; const isNodeEligible = NODE_ELIGIBLE_PROTOCOLS.has(protocol); const sniffingEnabled = Form.useWatch(['sniffing', 'enabled'], form) ?? false; + const vlessEncryption = Form.useWatch(['settings', 'encryption'], form) ?? ''; + + const matchesVlessAuth = ( + block: { id?: string; label?: string } | undefined | null, + authId: string, + ) => { + if (block?.id === authId) return true; + const label = (block?.label || '').toLowerCase().replace(/[-_\s]/g, ''); + if (authId === 'mlkem768') return label.includes('mlkem768'); + if (authId === 'x25519') return label.includes('x25519'); + return false; + }; + + const getNewVlessEnc = async (authId: string) => { + if (!authId) return; + setSaving(true); + try { + const msg = await HttpUtil.get('/panel/api/server/getNewVlessEnc'); + if (!msg?.success) return; + const obj = msg.obj as { + auths?: { decryption: string; encryption: string; label?: string; id?: string }[]; + }; + const block = (obj.auths || []).find((a) => matchesVlessAuth(a, authId)); + if (!block) return; + form.setFieldValue(['settings', 'decryption'], block.decryption); + form.setFieldValue(['settings', 'encryption'], block.encryption); + } finally { + setSaving(false); + } + }; + + const clearVlessEnc = () => { + form.setFieldValue(['settings', 'decryption'], 'none'); + form.setFieldValue(['settings', 'encryption'], 'none'); + }; + + const selectedVlessAuth = (() => { + const enc = typeof vlessEncryption === 'string' ? vlessEncryption : ''; + if (!enc || enc === 'none') return 'None'; + const parts = enc.split('.').filter(Boolean); + const authKey = parts[parts.length - 1] || ''; + if (!authKey) return t('pages.inbounds.vlessAuthCustom'); + return authKey.length > 300 + ? t('pages.inbounds.vlessAuthMlkem768') + : t('pages.inbounds.vlessAuthX25519'); + })(); useEffect(() => { if (!open) return; @@ -273,6 +324,35 @@ export default function InboundFormModalNew({ ); + const protocolTab = ( + <> + {protocol === Protocols.VLESS && ( + <> + + + + + + + + + + + + + + {t('pages.inbounds.vlessAuthSelected', { auth: selectedVlessAuth })} + + + + )} + + ); + const sniffingTab = ( <> @@ -357,6 +437,9 @@ export default function InboundFormModalNew({ >