// Generated, do not edit. sha256:e1360b8b92e9fdeccd0b88044df16a08eafd2bf7c084bf7549eeac1712ff09fb import { DatabaseSync } from 'node:sqlite'; import { writeFile, rename, rm, access } from 'node:fs/promises'; import { CONFIG } from '../shared/config.js'; import { BASELINE_VERSION } from './db/migrations.js'; import { closeDatabase } from './db/conn.mjs'; import { initDatabase, dbFile } from './db.js'; import { fail } from '../shared/app-error.js'; const BUREN = ['-wal', '-shm']; const VERGRENDELD = new Set(['EBUSY', 'EPERM', 'EACCES']); const WACHT_MS = [50, 150, 400]; async function hernoemVolhardend(van, naar) { for (let poging = 0; ; poging++) { try { return await rename(van, naar); } catch (e) { if (!VERGRENDELD.has(e.code) || poging >= WACHT_MS.length) throw vertaalVergrendeling(e, van); await new Promise(r => setTimeout(r, WACHT_MS[poging])); } } } function vertaalVergrendeling(e, pad) { if (!VERGRENDELD.has(e.code)) return e; return new Error( `Het databasebestand is door een ander programma in gebruik (${e.code}), dus het kon niet worden ` + `verplaatst: ${pad}. Er is niets gewijzigd. Sluit wat er nog aan deze datamap hangt en probeer het ` + `opnieuw: een tweede exemplaar van de app, de MCP-server (npm run mcp), of een database-viewer.`); } const bestaat = async pad => { try { await access(pad); return true; } catch { return false; } }; export function keurBestand(pad) { let db; try { db = new DatabaseSync(pad, { readOnly: true }); } catch (e) { fail('dbfile.notReadableSqlite', { reden: e.message }, `This is not a readable SQLite file (${e.message}).`); } try { const heeftSchema = db.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='Schema'`).get(); if (!heeftSchema) { fail('dbfile.noSchemaTable', null, 'This file has no Schema table; a database from before the baseline can no longer be converted ' + '(git log --all -- scripts/baseline-convert.mjs).'); } const rij = db.prepare(`SELECT version FROM Schema`).get(); const versie = rij && Number(rij.version); if (!Number.isInteger(versie)) fail('dbfile.noReadableVersion', null, 'The Schema table holds no readable version.'); if (versie < BASELINE_VERSION) { fail('dbfile.versionBeforeBaseline', { versie, baseline: BASELINE_VERSION }, `This database is version ${versie}, from before the new layout (version ${BASELINE_VERSION}); ` + 'see git log --all -- scripts/baseline-convert.mjs.'); } if (versie > CONFIG.SCHEMA_VERSION) { fail('dbfile.versionNewerThanApp', { versie, app: CONFIG.SCHEMA_VERSION }, `This database is version ${versie} and thus newer than the app (${CONFIG.SCHEMA_VERSION}).`); } const wezen = db.prepare(`PRAGMA foreign_key_check`).all(); return { versie, wezen: wezen.length }; } finally { db.close(); } } export async function restoreDatabase(bytes) { if (!bytes || bytes.length === 0) fail('dbfile.emptyFile', null, 'Received an empty file.'); if (bytes.subarray(0, 15).toString('latin1') !== 'SQLite format 3') { fail('dbfile.notSqlite', null, 'This is not a SQLite file (the file header does not match).'); } const doel = dbFile(); const incoming = `${doel}.new`; const previous = `${doel}.previous`; await writeFile(incoming, bytes, { mode: 0o600 }); let keuring; try { keuring = keurBestand(incoming); } catch (e) { await rm(incoming, { force: true }); throw e; } await wisselDatabaseBestand(incoming, previous); const regel = initDatabase(); return { versie: keuring.versie, wezen: keuring.wezen, previous, regel, geheimenWaarschuwing: 'De geheimen (NWC-URI\'s, LND-macaroons, provider-sleutels) staan in ' + 'secrets.json in de map van deze portefeuille en zijn NIET meeverhuisd. Komt deze database uit een andere administratie, ' + 'controleer dan je tracking-bronnen: een geheim hangt aan een bron-id en dat id kan nu bij een ' + 'andere bron horen.', }; } export async function wisselDatabaseBestand(incoming, previous, naWissel = async () => {}) { let opzijGezet = false; const doel = dbFile(); closeDatabase(); try { await rm(previous, { force: true }); for (const b of BUREN) await rm(previous + b, { force: true }); if (await bestaat(doel)) { await hernoemVolhardend(doel, previous); opzijGezet = true; for (const b of BUREN) if (await bestaat(doel + b)) await hernoemVolhardend(doel + b, previous + b); } await hernoemVolhardend(incoming, doel); await naWissel(); } catch (e) { if (opzijGezet && !(await bestaat(doel))) { try { await rename(previous, doel); for (const b of BUREN) if (await bestaat(previous + b)) await rename(previous + b, doel + b); } catch { } } await rm(incoming, { force: true }); try { initDatabase(); } catch { } throw e; } }