Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f7b594c8ca |
No files matched your search
@@ -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} />}>
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in new issue
Block a user