const { migrate } = require('drizzle-orm/better-sqlite3/migrator'); const { drizzle } = require('drizzle-orm/better-sqlite3'); const Database = require('better-sqlite3'); const fs = require('node:fs'); const path = require('node:path'); const dataDir = path.resolve( process.env.LEISTUNGSBILANZ_DATA_DIR || 'data' ); if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir, { recursive: true }); const databasePath = path.join(dataDir, 'leistungsbilanz.db'); async function run() { const sqlite = new Database(databasePath); try { if (requiresLinearXlsxBackup(sqlite)) { const backupDir = path.join(dataDir, 'backups'); fs.mkdirSync(backupDir, { recursive: true }); const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const backupPath = path.join( backupDir, `leistungsbilanz-before-linear-xlsx-${timestamp}.db` ); await sqlite.backup(backupPath); verifyBackup(backupPath); console.log(`Verified pre-migration backup created: ${backupPath}`); } migrate(drizzle(sqlite), { migrationsFolder: path.resolve('src/db/migrations'), }); } finally { sqlite.close(); } console.log('Migrations applied'); } function requiresLinearXlsxBackup(sqlite) { const tables = new Set( sqlite.prepare("SELECT name FROM sqlite_master WHERE type = 'table'") .all() .map((row) => row.name) ); return tables.has('external_csv_configurations') && !tables.has('external_model_configurations'); } function verifyBackup(backupPath) { const backup = new Database(backupPath, { readonly: true, fileMustExist: true }); try { const integrity = backup.pragma('integrity_check', { simple: true }); const foreignKeyViolations = backup.pragma('foreign_key_check'); if (integrity !== 'ok' || foreignKeyViolations.length > 0) { throw new Error( `Pre-migration backup verification failed: integrity=${integrity}, ` + `foreign-key violations=${foreignKeyViolations.length}` ); } } finally { backup.close(); } } run().catch((error) => { console.error('Migration failed:', error instanceof Error ? error.message : error); process.exit(1); });