From 68cd89fbe1e46a723144ebfdf8c1a0fa247573e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20LAGACHE?= Date: Fri, 19 Jun 2026 15:49:46 +0200 Subject: [PATCH] =?UTF-8?q?fix(onboarding):=20address=20final=20review=20f?= =?UTF-8?q?indings=20=E2=80=94=20atomicity,=20scheduling=20guard,=20apply-?= =?UTF-8?q?all=20recovery,=20client=20validation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- .../actions/apply-db-settings.action.ts | 55 ++++++++++--------- .../onboarding/steps/step-db-settings.tsx | 54 +++++++++++------- 2 files changed, 65 insertions(+), 44 deletions(-) diff --git a/src/features/onboarding/actions/apply-db-settings.action.ts b/src/features/onboarding/actions/apply-db-settings.action.ts index 17a34184..7213d0bc 100644 --- a/src/features/onboarding/actions/apply-db-settings.action.ts +++ b/src/features/onboarding/actions/apply-db-settings.action.ts @@ -93,6 +93,7 @@ export const applyOnboardingDbSettingsAction = userAction }; const applyScheduling = async () => { + if (backupMethod === undefined) return; // Direct DB update — intentionally skips the side effect in // updateDatabaseBackupPolicyAction that deletes retention policy on null. const cronValue = @@ -105,37 +106,41 @@ export const applyOnboardingDbSettingsAction = userAction const applyNotifications = async () => { if (!notificationPolicies) return; - await db - .delete(drizzleDb.schemas.alertPolicy) - .where(eq(drizzleDb.schemas.alertPolicy.databaseId, databaseId)); + await db.transaction(async (tx) => { + await tx + .delete(drizzleDb.schemas.alertPolicy) + .where(eq(drizzleDb.schemas.alertPolicy.databaseId, databaseId)); - if (notificationPolicies.length > 0) { - await db.insert(drizzleDb.schemas.alertPolicy).values( - notificationPolicies.map((p) => ({ - databaseId, - notificationChannelId: p.channelId, - eventKinds: p.eventKinds as any, - enabled: p.enabled, - })) - ); - } + if (notificationPolicies.length > 0) { + await tx.insert(drizzleDb.schemas.alertPolicy).values( + notificationPolicies.map((p) => ({ + databaseId, + notificationChannelId: p.channelId, + eventKinds: p.eventKinds as any, + enabled: p.enabled, + })) + ); + } + }); }; const applyStorage = async () => { if (!storagePolicies) return; - await db - .delete(drizzleDb.schemas.storagePolicy) - .where(eq(drizzleDb.schemas.storagePolicy.databaseId, databaseId)); + await db.transaction(async (tx) => { + await tx + .delete(drizzleDb.schemas.storagePolicy) + .where(eq(drizzleDb.schemas.storagePolicy.databaseId, databaseId)); - if (storagePolicies.length > 0) { - await db.insert(drizzleDb.schemas.storagePolicy).values( - storagePolicies.map((p) => ({ - databaseId, - storageChannelId: p.channelId, - enabled: p.enabled, - })) - ); - } + if (storagePolicies.length > 0) { + await tx.insert(drizzleDb.schemas.storagePolicy).values( + storagePolicies.map((p) => ({ + databaseId, + storageChannelId: p.channelId, + enabled: p.enabled, + })) + ); + } + }); }; if (section === "retention" || section === "all") await applyRetention(); diff --git a/src/features/onboarding/steps/step-db-settings.tsx b/src/features/onboarding/steps/step-db-settings.tsx index 60474711..5a72905c 100644 --- a/src/features/onboarding/steps/step-db-settings.tsx +++ b/src/features/onboarding/steps/step-db-settings.tsx @@ -497,7 +497,7 @@ const NotificationsSection = ({