diff --git a/lib/index.js b/lib/index.js index 64315b0..5492475 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,12 +1,12 @@ const path = require('path'); const fs = require('fs'); -const { spawn, exec } = require('child_process'); -const { promisify } = require('util'); +const {spawn, exec} = require('child_process'); +const {promisify} = require('util'); const execAsync = promisify(exec); /** * SQLite Backup Library - * + * * A standalone library for creating, managing, and verifying SQLite database backups. */ class SQLiteBackup { @@ -24,8 +24,8 @@ class SQLiteBackup { this.databasePath = path.resolve(options.databasePath); this.backupDirectory = options.backupDirectory ? - path.resolve(options.backupDirectory) : - path.join(path.dirname(this.databasePath), 'backups'); + path.resolve(options.backupDirectory) : + path.join(path.dirname(this.databasePath), 'backups'); this.createBackupDir = options.createBackupDir !== false; // Validate database file exists @@ -35,8 +35,8 @@ class SQLiteBackup { // Create backup directory if needed if (this.createBackupDir && !fs.existsSync(this.backupDirectory)) { - fs.mkdirSync(this.backupDirectory, { recursive: true }); - } + fs.mkdirSync(this.backupDirectory, {recursive: true}); + } } /** @@ -99,7 +99,7 @@ class SQLiteBackup { error: error.message, timestamp: new Date().toISOString() }; - } + } } /** @@ -114,9 +114,27 @@ class SQLiteBackup { return false; } - const command = `sqlite3 "${backupPath}" "PRAGMA integrity_check;"`; - const { stdout } = await execAsync(command); - return stdout.trim() === 'ok'; + const dbDriver = this._getDatabaseDriver(); + + var result = ""; + + switch (dbDriver.type) { + case "cli": + const command = `sqlite3 "${backupPath}" "PRAGMA integrity_check;"`; + const {stdout} = await execAsync(command); + result = stdout.trim(); + break; + case "better-sqlite3": + const db = dbDriver.driver(backupPath); + result = await db.pragma('integrity_check', {simple: true}); + break; + case "sqlite3": + result = await this._validateUsingSQLite3Package(dbDriver.driver, backupPath); + console.log(result); + break; + } + + return result === 'ok'; } catch (error) { return false; } @@ -182,7 +200,7 @@ class SQLiteBackup { removed: 0, errors: [error.message] }; - } + } } /** @@ -212,7 +230,6 @@ class SQLiteBackup { isValid: null, checksum: null }; - if (includeChecksums) { backup.checksum = await this._calculateChecksum(file.path); backup.isValid = await this.verifyBackup(file.path); @@ -228,7 +245,7 @@ class SQLiteBackup { } catch (error) { throw new Error(`Failed to list backups: ${error.message}`); - } + } } /** @@ -295,7 +312,7 @@ class SQLiteBackup { error: error.message, timestamp: new Date().toISOString() }; - } + } } // Private methods @@ -307,7 +324,7 @@ class SQLiteBackup { const baseName = path.basename(this.databasePath, '.db'); const timestamp = includeTimestamp ? - `-${new Date().toISOString().replace(/[:.]/g, '-')}` : ''; + `-${new Date().toISOString().replace(/[:.]/g, '-')}` : ''; return `${baseName}-backup${timestamp}.db`; } @@ -390,6 +407,25 @@ class SQLiteBackup { }); } + async _validateUsingSQLite3Package(driver, databasePath) { + return new Promise((resolve, reject) => { + const db = new driver.Database(databasePath, driver.OPEN_READONLY, err => { + if (err) { + return resolve("fail"); + } + }); + + db.get("PRAGMA integrity_check", function (err, res) { + console.log(res); + if (typeof res == 'undefined') { + resolve("fail"); + } else { + resolve(res.integrity_check ?? "fail"); + } + }); + }); + } + async _backupUsingCopy(backupPath) { fs.copyFileSync(this.databasePath, backupPath); } @@ -401,7 +437,7 @@ class SQLiteBackup { async _calculateChecksum(filePath) { try { - const { stdout } = await execAsync(`shasum -a 256 "${filePath}"`); + const {stdout} = await execAsync(`shasum -a 256 "${filePath}"`); return stdout.split(' ')[0]; } catch (error) { return null; @@ -434,7 +470,8 @@ class SQLiteBackup { _matchesPattern(filename, pattern) { // Simple pattern matching - could be enhanced with a proper glob library - if (pattern === '*' || pattern === '*.*') return true; + if (pattern === '*' || pattern === '*.*') + return true; if (pattern.startsWith('*.')) { const extension = pattern.slice(2); return filename.endsWith('.' + extension); @@ -454,7 +491,8 @@ class BackupUtils { */ static formatSize(bytes) { const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; - if (bytes === 0) return '0 B'; + if (bytes === 0) + return '0 B'; const i = Math.floor(Math.log(bytes) / Math.log(1024)); return `${(bytes / Math.pow(1024, i)).toFixed(2)} ${sizes[i]}`; } @@ -465,8 +503,10 @@ class BackupUtils { * @returns {string} Formatted duration string */ static formatDuration(milliseconds) { - if (milliseconds < 1000) return `${milliseconds}ms`; - if (milliseconds < 60000) return `${(milliseconds / 1000).toFixed(2)}s`; + if (milliseconds < 1000) + return `${milliseconds}ms`; + if (milliseconds < 60000) + return `${(milliseconds / 1000).toFixed(2)}s`; return `${(milliseconds / 60000).toFixed(2)}m`; } @@ -483,7 +523,7 @@ class BackupUtils { } const command = `sqlite3 "${databasePath}" "PRAGMA integrity_check;"`; - const { stdout } = await execAsync(command); + const {stdout} = await execAsync(command); return stdout.trim() === 'ok'; } catch (error) { return false;