mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
fix(onboarding): address final review findings — atomicity, scheduling guard, apply-all recovery, client validation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
fc42afe89e
commit
68cd89fbe1
@@ -93,6 +93,7 @@ export const applyOnboardingDbSettingsAction = userAction
|
|||||||
};
|
};
|
||||||
|
|
||||||
const applyScheduling = async () => {
|
const applyScheduling = async () => {
|
||||||
|
if (backupMethod === undefined) return;
|
||||||
// Direct DB update — intentionally skips the side effect in
|
// Direct DB update — intentionally skips the side effect in
|
||||||
// updateDatabaseBackupPolicyAction that deletes retention policy on null.
|
// updateDatabaseBackupPolicyAction that deletes retention policy on null.
|
||||||
const cronValue =
|
const cronValue =
|
||||||
@@ -105,12 +106,13 @@ export const applyOnboardingDbSettingsAction = userAction
|
|||||||
|
|
||||||
const applyNotifications = async () => {
|
const applyNotifications = async () => {
|
||||||
if (!notificationPolicies) return;
|
if (!notificationPolicies) return;
|
||||||
await db
|
await db.transaction(async (tx) => {
|
||||||
|
await tx
|
||||||
.delete(drizzleDb.schemas.alertPolicy)
|
.delete(drizzleDb.schemas.alertPolicy)
|
||||||
.where(eq(drizzleDb.schemas.alertPolicy.databaseId, databaseId));
|
.where(eq(drizzleDb.schemas.alertPolicy.databaseId, databaseId));
|
||||||
|
|
||||||
if (notificationPolicies.length > 0) {
|
if (notificationPolicies.length > 0) {
|
||||||
await db.insert(drizzleDb.schemas.alertPolicy).values(
|
await tx.insert(drizzleDb.schemas.alertPolicy).values(
|
||||||
notificationPolicies.map((p) => ({
|
notificationPolicies.map((p) => ({
|
||||||
databaseId,
|
databaseId,
|
||||||
notificationChannelId: p.channelId,
|
notificationChannelId: p.channelId,
|
||||||
@@ -119,16 +121,18 @@ export const applyOnboardingDbSettingsAction = userAction
|
|||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const applyStorage = async () => {
|
const applyStorage = async () => {
|
||||||
if (!storagePolicies) return;
|
if (!storagePolicies) return;
|
||||||
await db
|
await db.transaction(async (tx) => {
|
||||||
|
await tx
|
||||||
.delete(drizzleDb.schemas.storagePolicy)
|
.delete(drizzleDb.schemas.storagePolicy)
|
||||||
.where(eq(drizzleDb.schemas.storagePolicy.databaseId, databaseId));
|
.where(eq(drizzleDb.schemas.storagePolicy.databaseId, databaseId));
|
||||||
|
|
||||||
if (storagePolicies.length > 0) {
|
if (storagePolicies.length > 0) {
|
||||||
await db.insert(drizzleDb.schemas.storagePolicy).values(
|
await tx.insert(drizzleDb.schemas.storagePolicy).values(
|
||||||
storagePolicies.map((p) => ({
|
storagePolicies.map((p) => ({
|
||||||
databaseId,
|
databaseId,
|
||||||
storageChannelId: p.channelId,
|
storageChannelId: p.channelId,
|
||||||
@@ -136,6 +140,7 @@ export const applyOnboardingDbSettingsAction = userAction
|
|||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if (section === "retention" || section === "all") await applyRetention();
|
if (section === "retention" || section === "all") await applyRetention();
|
||||||
|
|||||||
@@ -497,7 +497,7 @@ const NotificationsSection = ({
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
disabled={isPending}
|
disabled={isPending || policies.some((p) => !p.channelId || p.eventKinds.length === 0)}
|
||||||
onClick={() => onSave(policies)}
|
onClick={() => onSave(policies)}
|
||||||
className="ml-auto"
|
className="ml-auto"
|
||||||
>
|
>
|
||||||
@@ -666,7 +666,7 @@ const StorageSection = ({
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
disabled={isPending}
|
disabled={isPending || policies.some((p) => !p.channelId)}
|
||||||
onClick={() => onSave(policies)}
|
onClick={() => onSave(policies)}
|
||||||
className="ml-auto"
|
className="ml-auto"
|
||||||
>
|
>
|
||||||
@@ -803,26 +803,42 @@ export const StepDbSettings = () => {
|
|||||||
|
|
||||||
const handleApplyToAll = async () => {
|
const handleApplyToAll = async () => {
|
||||||
const otherDbIds = databaseIds.filter((id) => id !== dbId);
|
const otherDbIds = databaseIds.filter((id) => id !== dbId);
|
||||||
|
const succeededIds: string[] = [];
|
||||||
|
|
||||||
for (const targetId of otherDbIds) {
|
for (const targetId of otherDbIds) {
|
||||||
|
try {
|
||||||
await applyMutation.mutateAsync({
|
await applyMutation.mutateAsync({
|
||||||
databaseId: targetId,
|
databaseId: targetId,
|
||||||
section: "all",
|
section: "all",
|
||||||
retention: settings.retention,
|
retention: settings.retention,
|
||||||
backupMethod: settings.backupMethod,
|
backupMethod: settings.backupMethod,
|
||||||
backupCron: settings.backupCron,
|
backupCron: settings.backupCron,
|
||||||
notificationPolicies: settings.notificationPolicies as any,
|
notificationPolicies: settings.notificationPolicies,
|
||||||
storagePolicies: settings.storagePolicies,
|
storagePolicies: settings.storagePolicies,
|
||||||
});
|
});
|
||||||
|
succeededIds.push(targetId);
|
||||||
|
} catch {
|
||||||
|
// mutation onError already shows a toast
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (succeededIds.length > 0) {
|
||||||
const updatedSettings = { ...dbSettings };
|
const updatedSettings = { ...dbSettings };
|
||||||
otherDbIds.forEach((id) => {
|
succeededIds.forEach((id) => {
|
||||||
updatedSettings[id] = { ...(dbSettings[id] ?? {}), ...settings };
|
updatedSettings[id] = { ...(dbSettings[id] ?? {}), ...settings };
|
||||||
});
|
});
|
||||||
await updateContext({
|
await updateContext({
|
||||||
flowData: { ...state?.context.flowData, dbSettings: updatedSettings },
|
flowData: { ...state?.context.flowData, dbSettings: updatedSettings },
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (succeededIds.length === otherDbIds.length) {
|
||||||
toast.success("Settings applied to all databases.");
|
toast.success("Settings applied to all databases.");
|
||||||
setPhase({ kind: "grid" });
|
setPhase({ kind: "grid" });
|
||||||
|
} else if (succeededIds.length > 0) {
|
||||||
|
toast.warning(`Settings applied to ${succeededIds.length} of ${otherDbIds.length} databases.`);
|
||||||
|
setPhase({ kind: "grid" });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const SECTIONS: {
|
const SECTIONS: {
|
||||||
|
|||||||
Reference in New Issue
Block a user