/** * Restore configurateur.db from the newest non-empty backup. * Safe to run while the server is up: the guarded saver refuses empty overwrites, * then restart the server (or let --watch reload) to pick up the restored file. */ import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; import initSqlJs from 'sql.js'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const dataDir = path.join(__dirname, '..', 'data'); const dbPath = process.env.DATABASE_PATH || path.join(dataDir, 'configurateur.db'); const backupDir = path.join(dataDir, 'backups'); const SQL = await initSqlJs({ locateFile: (f) => path.join(__dirname, '..', 'node_modules', 'sql.js', 'dist', f) }); function countMaterials(filePath) { if (!fs.existsSync(filePath)) return 0; let db = null; try { db = new SQL.Database(fs.readFileSync(filePath)); const r = db.exec('SELECT COUNT(*) FROM formex_materials'); return Number(r[0]?.values?.[0]?.[0] ?? 0); } catch { return 0; } finally { try { db?.close(); } catch { /* ignore */ } } } const backups = fs.existsSync(backupDir) ? fs.readdirSync(backupDir) .filter((f) => /^configurateur-.*\.db$/.test(f)) .map((f) => { const full = path.join(backupDir, f); return { file: f, full, count: countMaterials(full), mtime: fs.statSync(full).mtimeMs }; }) .filter((b) => b.count > 0) .sort((a, b) => b.count - a.count || b.mtime - a.mtime) : []; if (backups.length === 0) { console.error('No non-empty backup found under server/data/backups/'); process.exit(1); } const best = backups[0]; const currentCount = countMaterials(dbPath); console.log(`Current DB: ${currentCount} materials`); console.log(`Restoring from ${best.file} (${best.count} materials)`); if (fs.existsSync(dbPath) && currentCount > 0) { const stamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19); const safety = path.join(backupDir, `configurateur-pre-restore-${stamp}.db`); fs.copyFileSync(dbPath, safety); console.log(`Safety copy of current DB → ${safety}`); } fs.copyFileSync(best.full, dbPath); console.log(`Restored. Disk now has ${countMaterials(dbPath)} materials.`); console.log('Restart the API server so it reloads this file into memory.');