From b1ccf915db5137a506351e67e7ceac36f5ec877f Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Tue, 26 May 2026 02:19:28 +0200 Subject: [PATCH] feat(frontend): protocol tab Wireguard section (Pattern A) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the Wireguard sub-form: server secretKey input with regen icon, derived disabled public-key display, mtu, noKernelTun toggle, and a Form.List of peers — each peer having its own privateKey (regen icon), publicKey, preSharedKey, allowedIPs (nested Form.List for the string array), keepAlive. pubKey is purely derived (computed via Wireguard.generateKeypair from the watched secretKey) and is NOT stored in the form value — the schema omits it from the wire shape on purpose. The disabled display shows the live derivation without polluting form state. regenInboundWg generates a fresh keypair and writes only the secretKey path; pubKey re-derives automatically. regenWgPeerKeypair writes both privateKey and publicKey at the peer's path index. The preSharedKey wire-shape name is used instead of the legacy class's internal psk — matches WireguardInboundPeerSchema. Tab visibility widens to Wireguard. --- .../pages/inbounds/InboundFormModal.new.tsx | 123 +++++++++++++++++- 1 file changed, 122 insertions(+), 1 deletion(-) diff --git a/frontend/src/pages/inbounds/InboundFormModal.new.tsx b/frontend/src/pages/inbounds/InboundFormModal.new.tsx index 49768a0c..dc1cb908 100644 --- a/frontend/src/pages/inbounds/InboundFormModal.new.tsx +++ b/frontend/src/pages/inbounds/InboundFormModal.new.tsx @@ -18,7 +18,7 @@ import { } from 'antd'; import { MinusOutlined, PlusOutlined, SyncOutlined } from '@ant-design/icons'; -import { HttpUtil, NumberFormatter, RandomUtil, SizeFormatter } from '@/utils'; +import { HttpUtil, NumberFormatter, RandomUtil, SizeFormatter, Wireguard } from '@/utils'; import { rawInboundToFormValues, formValuesToWirePayload, @@ -105,6 +105,21 @@ export default function InboundFormModalNew({ settings: typeof ssMethod === 'string' ? { method: ssMethod } : {}, }); const mixedUdpOn = Form.useWatch(['settings', 'udp'], form) ?? false; + const wgSecretKey = Form.useWatch(['settings', 'secretKey'], form); + const wgPubKey = typeof wgSecretKey === 'string' && wgSecretKey.length > 0 + ? Wireguard.generateKeypair(wgSecretKey).publicKey + : ''; + + const regenInboundWg = () => { + const kp = Wireguard.generateKeypair(); + form.setFieldValue(['settings', 'secretKey'], kp.privateKey); + }; + + const regenWgPeerKeypair = (peerName: number) => { + const kp = Wireguard.generateKeypair(); + form.setFieldValue(['settings', 'peers', peerName, 'privateKey'], kp.privateKey); + form.setFieldValue(['settings', 'peers', peerName, 'publicKey'], kp.publicKey); + }; const matchesVlessAuth = ( block: { id?: string; label?: string } | undefined | null, @@ -336,6 +351,111 @@ export default function InboundFormModalNew({ const protocolTab = ( <> + {protocol === Protocols.WIREGUARD && ( + <> + + Secret key{' '} + + + } + > + + + + + + + + + + + + + {(fields, { add, remove }) => ( + <> + + + + {fields.map((field, idx) => ( +
+ + {fields.length > 1 && ( + + )} + + + Secret key{' '} + regenWgPeerKeypair(field.name)} + /> + + } + > + + + + + + + + + + {(ipFields, { add: addIp, remove: removeIp }) => ( + + + {ipFields.map((ipField) => ( + + + + + {ipFields.length > 1 && ( + + )} + + ))} + + )} + + + + +
+ ))} + + )} +
+ + )} + {protocol === Protocols.TUN && ( <> @@ -715,6 +835,7 @@ export default function InboundFormModalNew({ Protocols.MIXED, Protocols.TUNNEL, Protocols.TUN, + Protocols.WIREGUARD, ] as string[]).includes(protocol) ? [{ key: 'protocol', label: t('pages.inbounds.protocol'), children: protocolTab }] : []),