Compare commits

..
Author SHA1 Message Date
ajnart f7b594c8ca fix(backup): prompt re-login after restore 2026-09-06 10:21:28 +02:00
6 changed files with 34 additions and 15 deletions

No files matched your search

+3 -2
View File
@@ -30,8 +30,9 @@ needed to restore encrypted integration credentials.
Select a Homarr backup ZIP. The browser previews entity counts, board names, and required database migrations before
the archive is uploaded.
Restoring replaces the current SQLite database and cannot be undone. After a successful restore, the Next.js process
restarts and the page reloads when Homarr is ready.
Restoring replaces the current SQLite database and cannot be undone. It also invalidates the current database session.
After a successful management restore, Homarr waits for the restarted server and sends the administrator to sign in
again. The first-run onboarding restore keeps its existing board destination.
A backup can be restored on another Homarr instance. When its `SECRET_ENCRYPTION_KEY` differs, Homarr re-encrypts stored
integration secrets for the target instance. Version metadata is used to apply compatible migrations.
@@ -204,8 +204,12 @@ export const DatabaseRestoreFlow = ({ variant = "card", onRestoreComplete }: Dat
}
onRestoreComplete?.();
const destination =
variant === "standalone" && homeBoardName ? `/boards/${encodeURIComponent(homeBoardName)}` : null;
let destination: string | null = null;
if (variant === "standalone" && homeBoardName) {
destination = `/boards/${encodeURIComponent(homeBoardName)}`;
} else if (variant === "card") {
destination = "/auth/login";
}
startReadinessCheck(restartAfterMs, destination);
}, [file, onRestoreComplete, startReadinessCheck, t, variant]);
@@ -317,6 +321,7 @@ export const DatabaseRestoreFlow = ({ variant = "card", onRestoreComplete }: Dat
<RestoreProgressPanel
active
status={restoreStatus}
reloginRequired={variant === "card"}
onRetry={() => startReadinessCheck(0)}
onReload={() => window.location.reload()}
/>
@@ -11,11 +11,18 @@ export type RestoreProgressStatus = "restoring" | "restarting" | "timedOut";
interface RestoreProgressPanelProps {
active: boolean;
status: RestoreProgressStatus;
reloginRequired?: boolean;
onRetry: () => void;
onReload: () => void;
}
export const RestoreProgressPanel = ({ active, status, onRetry, onReload }: RestoreProgressPanelProps) => {
export const RestoreProgressPanel = ({
active,
status,
reloginRequired = false,
onRetry,
onReload,
}: RestoreProgressPanelProps) => {
const t = useI18n("management.page.tool.backup.restore");
const tCommon = useI18n("common");
const reduceMotion = useReducedMotion();
@@ -63,6 +70,12 @@ export const RestoreProgressPanel = ({ active, status, onRetry, onReload }: Rest
</div>
</Group>
{reloginRequired && !isRestoring ? (
<Text size="sm" c="dimmed">
{t("progress.relogin")}
</Text>
) : null}
{isTimedOut ? (
<Group>
<Button variant="light" onClick={onRetry} leftSection={<IconRefresh size={16} />}>
+4 -5
View File
@@ -481,14 +481,13 @@ export function calculateCpuUsage(stats: ContainerStats): number {
return 0;
}
const cpuDelta = stats.cpu_stats.cpu_usage.total_usage - (stats.precpu_stats?.cpu_usage?.total_usage ?? 0);
const systemDelta = (stats.cpu_stats.system_cpu_usage ?? 0) - (stats.precpu_stats?.system_cpu_usage ?? 0);
if (systemDelta <= 0 || cpuDelta < 0) {
const numberOfCpus = stats.cpu_stats.online_cpus;
const usage = stats.cpu_stats.system_cpu_usage;
if (!usage || usage === 0) {
return 0;
}
return (cpuDelta / systemDelta) * 100;
return (stats.cpu_stats.cpu_usage.total_usage / usage) * numberOfCpus * 100;
}
export function calculateMemoryUsage(stats: ContainerStats): number {
@@ -72,16 +72,16 @@ describe("calculateCpuUsage", () => {
const stats = createStats({
cpu_stats: { online_cpus: 4, cpu_usage: { total_usage: 2000 }, system_cpu_usage: 10000 },
});
// (2000 / 10000) * 100 = 20
expect(calculateCpuUsage(stats)).toBe(20);
// (2000 / 10000) * 4 * 100 = 80
expect(calculateCpuUsage(stats)).toBe(80);
});
test("should handle fractional CPU usage", () => {
const stats = createStats({
cpu_stats: { online_cpus: 2, cpu_usage: { total_usage: 500 }, system_cpu_usage: 100000 },
});
// (500 / 100000) * 100 = 0.5
expect(calculateCpuUsage(stats)).toBe(0.5);
// (500 / 100000) * 2 * 100 = 1
expect(calculateCpuUsage(stats)).toBe(1);
});
});
+2 -1
View File
@@ -6456,7 +6456,8 @@
},
"progress": {
"title": "Restoring database...",
"restarting": "Restarting server"
"restarting": "Restarting server",
"relogin": "Restoring the database invalidated your current session. Homarr will send you to sign in when the server is ready."
},
"timeout": {
"title": "Server did not come back online",