From 579acbc66969e59eccc05ec81b383597fac70bbd Mon Sep 17 00:00:00 2001 From: PathGao <42336971+PathGao@users.noreply.github.com> Date: Wed, 29 Jul 2026 02:10:21 +0800 Subject: [PATCH] fix(settings): keep the stored port when a port field is cleared (#6121) * fix(settings): keep the stored port when a port field is cleared Clearing the panel-port, subscription-port or LDAP-port InputNumber fired onChange(null), which the handlers coerced to 0; on blur Ant Design clamped the empty field to min=1 and the next save silently persisted port 1. For subPort that breaks the generated subscription links; for webPort it moves the panel itself to port 1 and locks the admin out until the port is fixed via the x-ui CLI. Ignore null changes so clearing a port field snaps back to the last valid value instead of committing a bogus port. Co-Authored-By: Claude Fable 5 * test(settings): pin cleared port fields to the stored value Clearing the subscription-port field must not reach updateSetting at all, while typed ports still pass through unchanged. Pins the fix so a handler refactor cannot silently reintroduce the clamped port 1. Co-Authored-By: Claude Fable 5 --------- Co-authored-by: Claude Fable 5 --- frontend/src/pages/settings/GeneralTab.tsx | 4 +-- .../pages/settings/SubscriptionGeneralTab.tsx | 2 +- .../test/subscription-general-tab.test.tsx | 30 +++++++++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/frontend/src/pages/settings/GeneralTab.tsx b/frontend/src/pages/settings/GeneralTab.tsx index df7a3f395..2c1ae840d 100644 --- a/frontend/src/pages/settings/GeneralTab.tsx +++ b/frontend/src/pages/settings/GeneralTab.tsx @@ -170,7 +170,7 @@ export default function GeneralTab({ allSetting, updateSetting }: GeneralTabProp updateSetting({ webPort: Number(v) || 0 })} /> + onChange={(v) => { if (v != null) updateSetting({ webPort: v }); }} /> @@ -308,7 +308,7 @@ export default function GeneralTab({ allSetting, updateSetting }: GeneralTabProp updateSetting({ ldapPort: Number(v) || 0 })} /> + onChange={(v) => { if (v != null) updateSetting({ ldapPort: v }); }} /> updateSetting({ ldapUseTLS: v })} /> diff --git a/frontend/src/pages/settings/SubscriptionGeneralTab.tsx b/frontend/src/pages/settings/SubscriptionGeneralTab.tsx index e94c8a877..c2ba1f833 100644 --- a/frontend/src/pages/settings/SubscriptionGeneralTab.tsx +++ b/frontend/src/pages/settings/SubscriptionGeneralTab.tsx @@ -57,7 +57,7 @@ export default function SubscriptionGeneralTab({ allSetting, updateSetting }: Su updateSetting({ subPort: Number(v) || 0 })} /> + onChange={(v) => { if (v != null) updateSetting({ subPort: v }); }} /> { + it('keeps the stored subscription port when the field is cleared', () => { + const updateSetting = vi.fn(); + + renderWithProviders( + + + , + ); + + const portInput = screen.getByDisplayValue('2096'); + fireEvent.change(portInput, { target: { value: '' } }); + fireEvent.blur(portInput); + + expect(updateSetting).not.toHaveBeenCalled(); + }); + + it('forwards typed subscription ports unchanged', () => { + const updateSetting = vi.fn(); + + renderWithProviders( + + + , + ); + + fireEvent.change(screen.getByDisplayValue('2096'), { target: { value: '8443' } }); + + expect(updateSetting).toHaveBeenCalledWith({ subPort: 8443 }); + }); + it('uses router navigation to open subscription format settings', () => { const allSetting = new AllSetting({ subClashEnable: true });