diff --git a/frontend/src/lib/xray/outbound-form-adapter.ts b/frontend/src/lib/xray/outbound-form-adapter.ts index 310532fb..254880e0 100644 --- a/frontend/src/lib/xray/outbound-form-adapter.ts +++ b/frontend/src/lib/xray/outbound-form-adapter.ts @@ -266,6 +266,7 @@ function freedomFromWire(raw: Raw): FreedomOutboundFormSettings { return (allowed.includes(s) ? s : '') as FreedomOutboundFormSettings['domainStrategy']; })(), redirect: asString(raw.redirect), + userLevel: asNumber(raw.userLevel, 0), proxyProtocol: ((): FreedomOutboundFormSettings['proxyProtocol'] => { const n = asNumber(raw.proxyProtocol, 0); return (n === 1 || n === 2) ? n : 0; @@ -506,11 +507,15 @@ function freedomToWire(s: FreedomOutboundFormSettings) { // Legacy semantics: emit fragment only when the user actually populated // at least one of the four sub-fields. Defaults like packets='1-3' alone // are not enough — the modal's Fragment Switch sets all four together. - const fragmentEntries = Object.entries(s.fragment).filter(([, v]) => v !== '' && v != null); - const fragmentEnabled = !!s.fragment.length || !!s.fragment.interval || !!s.fragment.maxSplit; + // getFieldsValue(true) may omit `fragment` when the switch is off, so the + // fallback keeps Object.entries from throwing on undefined (issue #4686). + const fragment: Partial = s.fragment ?? {}; + const fragmentEntries = Object.entries(fragment).filter(([, v]) => v !== '' && v != null); + const fragmentEnabled = !!fragment.length || !!fragment.interval || !!fragment.maxSplit; return { domainStrategy: s.domainStrategy || undefined, redirect: s.redirect || undefined, + userLevel: s.userLevel || undefined, proxyProtocol: s.proxyProtocol || undefined, fragment: fragmentEnabled ? Object.fromEntries(fragmentEntries) : undefined, noises: s.noises.length > 0 ? s.noises : undefined, diff --git a/frontend/src/pages/xray/outbounds/protocols/freedom.tsx b/frontend/src/pages/xray/outbounds/protocols/freedom.tsx index 818cbffe..30816202 100644 --- a/frontend/src/pages/xray/outbounds/protocols/freedom.tsx +++ b/frontend/src/pages/xray/outbounds/protocols/freedom.tsx @@ -1,5 +1,5 @@ import { useTranslation } from 'react-i18next'; -import { Button, Form, Input, Select, Switch, type FormInstance } from 'antd'; +import { Button, Form, Input, InputNumber, Select, Switch, type FormInstance } from 'antd'; import { DeleteOutlined, PlusOutlined } from '@ant-design/icons'; import { OutboundDomainStrategies } from '@/schemas/primitives'; @@ -20,6 +20,9 @@ export default function FreedomFields({ form }: { form: FormInstance + + +