fix(backup): log restore errors via the central logger and harden cleanup

Replace raw console.error calls in the backup import/migrations routes with
createLogger + ErrorWithMetadata so restore failures flow through the standard
log transports (console + Redis). Wrap the temporary DB close in finally so a
close failure cannot 500 a successful restore, and log client-side restore and
analysis failures to the browser console.
This commit is contained in:
Thomas Camlong committed 2026-08-18 11:14:04 +02:00
1 parent 2a76bb185b
commit 6befc5ad41
4 files changed
+21 -6

No files matched your search

+12 -4
View File
@@ -12,10 +12,14 @@ import { auth } from "@homarr/auth/next";
import { env } from "@homarr/common/env";
import { DB_CASING } from "@homarr/core/infrastructure/db/constants";
import { dbEnv } from "@homarr/core/infrastructure/db/env";
import { createLogger } from "@homarr/core/infrastructure/logs";
import { ErrorWithMetadata } from "@homarr/core/infrastructure/logs/error";
import { db } from "@homarr/db";
import { findMigrationsFolder } from "../shared";
const logger = createLogger({ module: "backupImportRoute" });
const REQUIRED_ZIP_ENTRIES = ["db.sqlite", "metadata.json"] as const;
const ALGORITHM = "aes-256-cbc";
const HEX_KEY_REGEX = /^[0-9a-fA-F]{64}$/;
@@ -174,22 +178,26 @@ export async function POST(req: Request) {
const sidecar = `${dbPath}${suffix}`;
if (fs.existsSync(sidecar)) fs.unlinkSync(sidecar);
} catch (cleanupErr) {
console.error(`Failed to remove ${suffix} file:`, cleanupErr);
logger.warn("Failed to remove SQLite sidecar file after restore", { suffix, dbPath, cause: cleanupErr });
}
}
setTimeout(() => {
console.log("Database restored, restarting server...");
logger.info("Database restored, restarting server...");
process.exit(0);
}, 500);
return NextResponse.json({ success: true, message: "Database restored. Server is restarting..." });
} catch (error) {
console.error("[backup/import] Restore failed:", error);
logger.error(new ErrorWithMetadata("Backup restore failed", { dbPath }, { cause: error }));
const message = error instanceof Error ? error.message : "Unknown error";
return NextResponse.json({ error: `Restore failed: ${message}` }, { status: 500 });
} finally {
tempDb?.close();
try {
tempDb?.close();
} catch (closeErr) {
logger.warn("Failed to close temporary database after restore", { cause: closeErr });
}
if (fs.existsSync(tempPath)) {
try {
fs.unlinkSync(tempPath);
@@ -3,8 +3,13 @@ import path from "path";
import { NextResponse } from "next/server";
import { createLogger } from "@homarr/core/infrastructure/logs";
import { ErrorWithMetadata } from "@homarr/core/infrastructure/logs/error";
import { findMigrationsFolder } from "../shared";
const logger = createLogger({ module: "backupMigrationsRoute" });
interface JournalEntry {
idx: number;
version: string;
@@ -56,7 +61,7 @@ export async function GET() {
},
);
} catch (error) {
console.error("[backup/migrations] Failed to read migrations:", error);
logger.error(new ErrorWithMetadata("Failed to read migration files", {}, { cause: error }));
const message = error instanceof Error ? error.message : "Unknown error";
return NextResponse.json({ error: `Failed to read migrations: ${message}` }, { status: 500 });
}
@@ -113,7 +113,8 @@ export const DatabaseRestoreFlow = ({ variant = "card", onRestoreComplete }: Dat
apiErrorRef.current = `Server returned ${response.status}`;
}
}
} catch {
} catch (error) {
console.error("Restore request failed", error);
apiErrorRef.current = t("failed.title");
} finally {
apiDoneRef.current = true;
@@ -168,6 +168,7 @@ export const useBackupAnalysis = () => {
dbRef.current = null;
}
} catch (err) {
console.error("Backup analysis failed", err);
setError(err instanceof Error ? err.message : t("analyzeError"));
} finally {
setLoading(false);