Files
EvoFirewall/deploy/docker/evofw/sqlite-backup.mjs
T
Denozordec 57b7652d4d
quality / changes (push) Successful in 6s
quality / commitlint (push) Skipped
quality / openapi (push) Successful in 28s
quality / web (push) Successful in 57s
quality / api (push) Successful in 1m35s
quality / linux-agent-smoke (push) Successful in 24s
quality / docker-check (push) Failing after 3m20s
CD / quality (push) Failing after 6m56s
CD / publish (push) Skipped
fix(ci): support ipset sockets and deployed image smoke checks
2026-10-26 14:14:51 +07:00

42 lines
2.1 KiB
JavaScript

import { createRequire } from 'node:module'
import { resolve, dirname } from 'node:path'
import { existsSync, mkdirSync, renameSync, rmSync, realpathSync } from 'node:fs'
// Resolve through the production workspace package; pnpm isolates transitive deps.
const require = createRequire(realpathSync(resolve('node_modules/@evofw/db/package.json')))
const Database = require('better-sqlite3')
const [mode, input, output, offline] = process.argv.slice(2)
if (!['backup', 'restore', 'verify'].includes(mode) || !input || (mode !== 'verify' && !output)) {
throw new Error('Usage: sqlite-backup.mjs backup|verify|restore INPUT [OUTPUT] [--offline]')
}
if (mode === 'restore' && offline !== '--offline') throw new Error('Restore requires a stopped volume and --offline')
if (output && resolve(input) === resolve(output)) throw new Error('Input and output must differ')
if (mode === 'backup' && existsSync(output)) throw new Error('Backup destination already exists')
const source = new Database(input, { readonly: true, fileMustExist: true })
let temporary
try {
if (source.pragma('integrity_check', { simple: true }) !== 'ok') throw new Error('Source integrity_check failed')
if (mode !== 'verify') {
mkdirSync(dirname(output), { recursive: true })
temporary = `${output}.partial-${process.pid}`
// SQLite online backup includes committed WAL pages while the API runs.
await source.backup(temporary)
const check = new Database(temporary, { readonly: true, fileMustExist: true })
try {
if (check.pragma('integrity_check', { simple: true }) !== 'ok') throw new Error('Backup integrity_check failed')
if (check.pragma('foreign_key_check').length) throw new Error('Backup foreign_key_check failed')
} finally { check.close() }
if (mode === 'restore') {
// Only safe after the caller has excluded every process using this volume.
rmSync(`${output}-wal`, { force: true })
rmSync(`${output}-shm`, { force: true })
}
renameSync(temporary, output)
temporary = undefined
}
console.log(JSON.stringify({ mode, input, output, integrity: 'ok' }))
} finally {
source.close()
if (temporary) rmSync(temporary, { force: true })
}