diff --git a/frontend/src/pages/xray/OutboundFormModal.tsx b/frontend/src/pages/xray/OutboundFormModal.tsx index fc9b6dfe..fffd5e15 100644 --- a/frontend/src/pages/xray/OutboundFormModal.tsx +++ b/frontend/src/pages/xray/OutboundFormModal.tsx @@ -203,6 +203,26 @@ export default function OutboundFormModal({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [streamAllowed, network]); + // Wireguard pubKey is a UI-only field derived from secretKey on every + // edit. The legacy modal did the same on every keystroke. We re-derive + // here so paste-in secret keys immediately surface the matching pub. + const wgSecretKey = Form.useWatch(['settings', 'secretKey'], form) as string | undefined; + useEffect(() => { + if (protocol !== 'wireguard') return; + const sk = (wgSecretKey ?? '').trim(); + if (!sk) { + form.setFieldValue(['settings', 'pubKey'], ''); + return; + } + try { + const { publicKey } = Wireguard.generateKeypair(sk); + form.setFieldValue(['settings', 'pubKey'], publicKey); + } catch { + form.setFieldValue(['settings', 'pubKey'], ''); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [protocol, wgSecretKey]); + // Switching protocol resets the settings sub-object to fresh defaults // so leftover fields from the previous protocol do not bleed through. // The adapter's rawOutboundToFormValues seeds whatever the new protocol @@ -1054,12 +1074,72 @@ export default function OutboundFormModal({ form.setFieldValue( ['streamSettings', 'tcpSettings', 'header'], checked - ? { type: 'http', request: undefined, response: undefined } + ? { + type: 'http', + request: { + version: '1.1', + method: 'GET', + path: ['/'], + headers: {}, + }, + response: undefined, + } : { type: 'none' }, ) } /> + {type === 'http' && ( + <> + {/* Host is stored as a string[] on the + wire (V2 header map: { Host: [...] }). + The form-level normalize/getValueProps + translate to/from a comma-joined input + so the user types one Host:contentReference[oaicite:0]{index=0} value per + server they want camouflaged. */} +