fix(backup): show explicit sign-in prompt after database restore
Restoring a backup replaces the database, invalidating the database-backed session (Auth.js session strategy: database). The old code silently reloaded the page, leaving users on an empty board or a login screen with no context. Replace the opaque reload with an explicit completion state that informs the user their session was invalidated and auto-redirects to the sign-in page (bypassing bfcache so the stale board cannot be restored).
This commit is contained in:
3 files changed
+48
-6
No files matched your search
@@ -1,10 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useRef, useState } from "react";
|
||||
import { Alert, Button, Group, Loader, rem, Stack, Text } from "@mantine/core";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { Alert, Button, Group, Loader, rem, Stack, Text, ThemeIcon } from "@mantine/core";
|
||||
import type { FileWithPath } from "@mantine/dropzone";
|
||||
import { Dropzone, MIME_TYPES } from "@mantine/dropzone";
|
||||
import { IconAlertTriangle, IconArrowRight, IconFileZip, IconUpload, IconX } from "@tabler/icons-react";
|
||||
import { IconAlertTriangle, IconArrowRight, IconCheck, IconFileZip, IconUpload, IconX } from "@tabler/icons-react";
|
||||
|
||||
import "@mantine/dropzone/styles.css";
|
||||
|
||||
@@ -30,8 +30,17 @@ export const DatabaseRestoreFlow = ({ variant = "card", onRestoreComplete }: Dat
|
||||
const apiDoneRef = useRef(false);
|
||||
const animDoneRef = useRef(false);
|
||||
const apiErrorRef = useRef<string | null>(null);
|
||||
const redirectTimerRef = useRef<number | null>(null);
|
||||
const { analysis, loading, error: analysisError, migrationProgress, analyzeFile, reset } = useBackupAnalysis();
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (redirectTimerRef.current !== null) {
|
||||
window.clearTimeout(redirectTimerRef.current);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleFileDrop = useCallback(
|
||||
(files: FileWithPath[]) => {
|
||||
const droppedFile = files[0];
|
||||
@@ -45,6 +54,10 @@ export const DatabaseRestoreFlow = ({ variant = "card", onRestoreComplete }: Dat
|
||||
);
|
||||
|
||||
const handleClear = useCallback(() => {
|
||||
if (redirectTimerRef.current !== null) {
|
||||
window.clearTimeout(redirectTimerRef.current);
|
||||
redirectTimerRef.current = null;
|
||||
}
|
||||
setFile(null);
|
||||
setStep("upload");
|
||||
setImportError(null);
|
||||
@@ -65,9 +78,20 @@ export const DatabaseRestoreFlow = ({ variant = "card", onRestoreComplete }: Dat
|
||||
|
||||
if (!animDoneRef.current) return;
|
||||
onRestoreComplete?.();
|
||||
window.location.reload();
|
||||
setStep("complete");
|
||||
redirectTimerRef.current = window.setTimeout(() => {
|
||||
window.location.assign("/auth/login");
|
||||
}, 2000);
|
||||
}, [onRestoreComplete]);
|
||||
|
||||
const handleSignInAgain = useCallback(() => {
|
||||
if (redirectTimerRef.current !== null) {
|
||||
window.clearTimeout(redirectTimerRef.current);
|
||||
redirectTimerRef.current = null;
|
||||
}
|
||||
window.location.assign("/auth/login");
|
||||
}, []);
|
||||
|
||||
const handleConfirm = useCallback(async () => {
|
||||
if (!file) return;
|
||||
setStep("restoring");
|
||||
@@ -212,6 +236,23 @@ export const DatabaseRestoreFlow = ({ variant = "card", onRestoreComplete }: Dat
|
||||
);
|
||||
}
|
||||
|
||||
if (step === "complete") {
|
||||
return (
|
||||
<Stack gap="md" align="center" py="lg">
|
||||
<ThemeIcon size={64} radius="xl" color="green" variant="light">
|
||||
<IconCheck size={32} style={{ width: rem(40), height: rem(40) }} stroke={1.5} />
|
||||
</ThemeIcon>
|
||||
<Text size="lg" fw={600} ta="center">
|
||||
{t("complete.title")}
|
||||
</Text>
|
||||
<Text size="sm" c="dimmed" ta="center" maw={480}>
|
||||
{t("complete.message")}
|
||||
</Text>
|
||||
<Button onClick={handleSignInAgain}>{t("complete.button")}</Button>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
if (step === "error") {
|
||||
return (
|
||||
<Stack gap="md">
|
||||
|
||||
@@ -25,6 +25,6 @@ export interface BackupAnalysis {
|
||||
migrations: MigrationStatus;
|
||||
}
|
||||
|
||||
export type RestoreStep = "upload" | "preview" | "confirm" | "restoring" | "error";
|
||||
export type RestoreStep = "upload" | "preview" | "confirm" | "restoring" | "complete" | "error";
|
||||
|
||||
export const PREVIEW_TABLE_KEYS = ["board", "user", "app", "integration", "item", "media", "search_engine"] as const;
|
||||
@@ -5276,7 +5276,8 @@
|
||||
},
|
||||
"complete": {
|
||||
"title": "Restore Complete",
|
||||
"message": "Database has been restored successfully. The server is restarting — please refresh the page in a few seconds."
|
||||
"message": "Database has been restored successfully and the server has restarted. Your previous session was invalidated, so you need to sign in again to see the restored board.",
|
||||
"button": "Sign in now"
|
||||
},
|
||||
"failed": {
|
||||
"title": "Restore Failed"
|
||||
|
||||
Reference in new issue
Block a user