From 4ecbb0e55f4352587e323c1e69721e861afe7515 Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Mon, 25 May 2026 17:55:21 +0200 Subject: [PATCH] feat(frontend): block invalid settings saves with Zod pre-save check Tighten AllSettingSchema with the actual valid ranges and patterns: - webPort / subPort / ldapPort: integer 1-65535 - pageSize: integer 1-1000 - sessionMaxAge: integer >= 1 - tgCpu: integer 0-100 (percentage) - subUpdates: integer 1-168 (hours) - expireDiff / trafficDiff / ldapDefault*: non-negative integers - webBasePath / subPath / subJsonPath / subClashPath: must start with / The existing useAllSettings save path runs AllSettingSchema.partial() through safeParse and logs drift without blocking. SettingsPage now adds a stronger gate before the mutation: run the full schema against the draft and, on failure, surface the first issue (field path + message) via the existing messageApi.error so the user actually sees what's wrong instead of silently sending bad data to the backend. Use cases caught: port out of range, negative quota, sub path missing leading slash, page size set to 0, tgCpu > 100. --- frontend/src/pages/settings/SettingsPage.tsx | 15 +++++++- frontend/src/schemas/setting.ts | 36 +++++++++++--------- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/frontend/src/pages/settings/SettingsPage.tsx b/frontend/src/pages/settings/SettingsPage.tsx index af77f6b1..d0c74f2e 100644 --- a/frontend/src/pages/settings/SettingsPage.tsx +++ b/frontend/src/pages/settings/SettingsPage.tsx @@ -29,6 +29,7 @@ import { setMessageInstance } from '@/utils/messageBus'; import { useTheme } from '@/hooks/useTheme'; import { useMediaQuery } from '@/hooks/useMediaQuery'; import { useAllSettings } from '@/api/queries/useAllSettings'; +import { AllSettingSchema } from '@/schemas/setting'; import AppSidebar from '@/components/AppSidebar'; import GeneralTab from './GeneralTab'; import SecurityTab from './SecurityTab'; @@ -148,6 +149,18 @@ export default function SettingsPage() { return url.toString(); } + async function onSave() { + const result = AllSettingSchema.safeParse(allSetting); + if (!result.success) { + const issue = result.error.issues[0]; + const fieldPath = issue?.path.join('.') ?? 'value'; + const msgKey = issue?.message ?? 'somethingWentWrong'; + messageApi.error(`${fieldPath}: ${t(msgKey, { defaultValue: msgKey })}`); + return; + } + await saveAll(); + } + function restartPanel() { modal.confirm({ title: t('pages.settings.restartPanel'), @@ -301,7 +314,7 @@ export default function SettingsPage() { -