fix: fix db restore not applying and auto-create USB backup folder
- Reset public schema before psql restore so existing tables don't block CREATE TABLE statements (pg_dump without --clean has no DROPs) - Disconnect Prisma connection pool after restore so next request gets fresh connections against the restored data - Auto-create the USB backup subfolder if it doesn't exist instead of failing; only fail if the destination drive itself is missing Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -111,19 +111,23 @@ export const startBackupCron = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const usbBackupPath = path.join(destination.path, USB_BACKUP_FOLDER_NAME);
|
if (!fs.existsSync(destination.path)) {
|
||||||
|
const errorMessage = "Backup destination drive not found or disconnected.";
|
||||||
if (!fs.existsSync(usbBackupPath)) {
|
|
||||||
const errorMessage = `Folder "${USB_BACKUP_FOLDER_NAME}" not found on the drive.`;
|
|
||||||
await cronJobLogStorage.completeJobLog(log.id, "failed", new Date(), errorMessage);
|
await cronJobLogStorage.completeJobLog(log.id, "failed", new Date(), errorMessage);
|
||||||
await storage.createNotification(
|
await storage.createNotification(
|
||||||
admin.id,
|
admin.id,
|
||||||
"BACKUP",
|
"BACKUP",
|
||||||
`❌ USB backup failed: folder "${USB_BACKUP_FOLDER_NAME}" not found on the drive. Make sure the USB drive is connected and the folder exists.`
|
"❌ USB backup failed: backup drive not found. Make sure the USB drive is connected."
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const usbBackupPath = path.join(destination.path, USB_BACKUP_FOLDER_NAME);
|
||||||
|
|
||||||
|
if (!fs.existsSync(usbBackupPath)) {
|
||||||
|
fs.mkdirSync(usbBackupPath, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const filename = `dental_backup_usb_${Date.now()}.sql`;
|
const filename = `dental_backup_usb_${Date.now()}.sql`;
|
||||||
await backupDatabaseToPath({ destinationPath: usbBackupPath, filename });
|
await backupDatabaseToPath({ destinationPath: usbBackupPath, filename });
|
||||||
|
|||||||
@@ -339,6 +339,17 @@ router.post("/restore", restoreUpload.single("file"), async (req: Request, res:
|
|||||||
|
|
||||||
if (!req.file) return res.status(400).json({ error: "No file provided" });
|
if (!req.file) return res.status(400).json({ error: "No file provided" });
|
||||||
|
|
||||||
|
// Drop and recreate the public schema so existing tables don't block the restore.
|
||||||
|
// pg_dump without --clean produces no DROP statements, so restoring into an
|
||||||
|
// existing schema would silently skip CREATE TABLE / fail on duplicate inserts.
|
||||||
|
try {
|
||||||
|
await prisma.$executeRawUnsafe(`DROP SCHEMA public CASCADE`);
|
||||||
|
await prisma.$executeRawUnsafe(`CREATE SCHEMA public`);
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error("Failed to reset schema before restore:", err);
|
||||||
|
return res.status(500).json({ error: "Failed to reset database schema", details: err.message });
|
||||||
|
}
|
||||||
|
|
||||||
const psql = spawn(
|
const psql = spawn(
|
||||||
"psql",
|
"psql",
|
||||||
[
|
[
|
||||||
@@ -360,11 +371,20 @@ router.post("/restore", restoreUpload.single("file"), async (req: Request, res:
|
|||||||
res.status(500).json({ error: "Failed to run psql", details: err.message });
|
res.status(500).json({ error: "Failed to run psql", details: err.message });
|
||||||
});
|
});
|
||||||
|
|
||||||
psql.on("close", (code) => {
|
psql.on("close", async (code) => {
|
||||||
if (code !== 0) {
|
if (code !== 0) {
|
||||||
console.error("psql restore failed:", stderr);
|
console.error("psql restore failed:", stderr);
|
||||||
return res.status(500).json({ error: "Restore failed", details: stderr });
|
return res.status(500).json({ error: "Restore failed", details: stderr });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Disconnect so Prisma drops its pooled connections and reconnects fresh
|
||||||
|
// on the next request, avoiding stale prepared statements against the old schema.
|
||||||
|
try {
|
||||||
|
await prisma.$disconnect();
|
||||||
|
} catch (_) {
|
||||||
|
// non-fatal — the restore succeeded
|
||||||
|
}
|
||||||
|
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user