// Eine Sicherung der Datenbank, die auch stimmt, waehrend geschrieben wird. // // WARUM NICHT EINFACH KOPIEREN. media.db laeuft im WAL-Modus. Was zuletzt // hochgeladen wurde, steht dann noch in media.db-wal und nicht in media.db — // eine Kopie nur der einen Datei ist deshalb still unvollstaendig. Beim ersten // Nachsehen im Container war das WAL nach wenigen Uploads 152 KB gross. // // Und selbst alle drei Dateien zu kopieren reicht nicht sicher: waehrend des // Kopierens kann geschrieben werden, und dann passen sie nicht mehr zueinander. // // Deshalb SQLites eigene Sicherungsschnittstelle. Sie laeuft auf der offenen // Datenbank, nimmt eine in sich stimmige Aufnahme und faengt von vorn an, wenn // dazwischen geschrieben wurde. Das Ergebnis ist EINE Datei ohne WAL daneben. // // Aufruf im Container: // // node dist/backup.js /data/.sicherung/media.db // // Der Rest — Bilder dazupacken, herausholen, pruefen — steht in // tools/sichern.ps1; hier bleibt nur der Teil, der die Datenbank kennt. import { mkdirSync } from 'node:fs' import { dirname } from 'node:path' import { db } from './db.js' const ziel = process.argv[2] if (!ziel) { console.error('[sicherung] Kein Ziel angegeben. Aufruf: node dist/backup.js ') process.exit(2) } mkdirSync(dirname(ziel), { recursive: true }) const zeilen = db.prepare('SELECT COUNT(*) AS n FROM media').get() as { n: number } db.backup(ziel) .then(() => { // Die Zeilenzahl mit ausgeben: sie ist der Wert, gegen den die // Sicherung hinterher geprueft wird. Eine Sicherung, die niemand // nachzaehlt, ist eine Vermutung. console.log(`[sicherung] ${ziel} geschrieben, ${zeilen.n} Medieneintraege`) process.exit(0) }) .catch((err: unknown) => { console.error(`[sicherung] fehlgeschlagen: ${err instanceof Error ? err.message : err}`) process.exit(1) })