Files
gold_land_server/scripts/sqlite-backup.mjs
T
safavimohadeseh78andCursor dfb20ca92c
Backend CI / type-check, build, tests (push) Successful in 1m0s
feat: prepare six-lesson trial backend with reliable rewards and operations
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-12 14:35:42 +03:30

54 lines
1.6 KiB
JavaScript

/**
* WAL-safe SQLite backup via better-sqlite3 backup API (not a raw file copy).
* Usage: DB_PATH=./data/app.db BACKUP_DIR=./backups node scripts/sqlite-backup.mjs
*/
import Database from 'better-sqlite3'
import { mkdirSync, existsSync, readdirSync, statSync, unlinkSync } from 'node:fs'
import { join } from 'node:path'
const srcPath = process.env.DB_PATH ?? join(process.cwd(), 'data', 'app.db')
const backupDir = process.env.BACKUP_DIR ?? join(process.cwd(), 'backups')
const retentionDays = Number(process.env.BACKUP_RETENTION_DAYS ?? '0')
if (!existsSync(srcPath)) {
console.error(`Source database not found: ${srcPath}`)
process.exit(1)
}
mkdirSync(backupDir, { recursive: true })
const stamp = new Date().toISOString().replace(/[:.]/g, '-')
const destPath = join(backupDir, `app-${stamp}.db`)
const src = new Database(srcPath, { readonly: true })
try {
await src.backup(destPath)
} finally {
src.close()
}
const verify = new Database(destPath, { readonly: true })
try {
const integrity = verify.pragma('integrity_check', { simple: true })
if (integrity !== 'ok') {
console.error(`Backup integrity check failed: ${integrity}`)
process.exit(1)
}
} finally {
verify.close()
}
console.log(`Backup written: ${destPath}`)
if (retentionDays > 0) {
const cutoff = Date.now() - retentionDays * 86_400_000
for (const name of readdirSync(backupDir)) {
if (!name.startsWith('app-') || !name.endsWith('.db')) continue
const full = join(backupDir, name)
if (statSync(full).mtimeMs < cutoff) {
unlinkSync(full)
console.log(`Removed old backup: ${name}`)
}
}
}