Use Node SQLite libraries for backup verification, if available
Some checks failed
CI/CD Pipeline / Test on Node.js 16.x and ubuntu-latest (release) Successful in 1m37s
CI/CD Pipeline / Test on Node.js 18.x and ubuntu-latest (release) Successful in 43s
CI/CD Pipeline / Test on Node.js 20.x and ubuntu-latest (release) Successful in 45s
CI/CD Pipeline / Test on Node.js 22.x and ubuntu-latest (release) Successful in 50s
CI/CD Pipeline / Lint and Code Quality (release) Failing after 11s
CI/CD Pipeline / Security Scan (release) Failing after 11s
CI/CD Pipeline / Test on Node.js 16.x and windows-latest (release) Has been cancelled
CI/CD Pipeline / Test on Node.js 18.x and macos-latest (release) Has been cancelled
CI/CD Pipeline / Test on Node.js 18.x and windows-latest (release) Has been cancelled
CI/CD Pipeline / Test on Node.js 20.x and macos-latest (release) Has been cancelled
CI/CD Pipeline / Test on Node.js 20.x and windows-latest (release) Has been cancelled
CI/CD Pipeline / Test on Node.js 22.x and macos-latest (release) Has been cancelled
CI/CD Pipeline / Test on Node.js 22.x and windows-latest (release) Has been cancelled
CI/CD Pipeline / Publish to npm (release) Has been cancelled
CI/CD Pipeline / Auto-increment version on main (release) Has been cancelled
Some checks failed
CI/CD Pipeline / Test on Node.js 16.x and ubuntu-latest (release) Successful in 1m37s
CI/CD Pipeline / Test on Node.js 18.x and ubuntu-latest (release) Successful in 43s
CI/CD Pipeline / Test on Node.js 20.x and ubuntu-latest (release) Successful in 45s
CI/CD Pipeline / Test on Node.js 22.x and ubuntu-latest (release) Successful in 50s
CI/CD Pipeline / Lint and Code Quality (release) Failing after 11s
CI/CD Pipeline / Security Scan (release) Failing after 11s
CI/CD Pipeline / Test on Node.js 16.x and windows-latest (release) Has been cancelled
CI/CD Pipeline / Test on Node.js 18.x and macos-latest (release) Has been cancelled
CI/CD Pipeline / Test on Node.js 18.x and windows-latest (release) Has been cancelled
CI/CD Pipeline / Test on Node.js 20.x and macos-latest (release) Has been cancelled
CI/CD Pipeline / Test on Node.js 20.x and windows-latest (release) Has been cancelled
CI/CD Pipeline / Test on Node.js 22.x and macos-latest (release) Has been cancelled
CI/CD Pipeline / Test on Node.js 22.x and windows-latest (release) Has been cancelled
CI/CD Pipeline / Publish to npm (release) Has been cancelled
CI/CD Pipeline / Auto-increment version on main (release) Has been cancelled
This commit is contained in:
parent
b34e4b5d83
commit
c2b4a8eca3
82
lib/index.js
82
lib/index.js
@ -1,7 +1,7 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const { spawn, exec } = require('child_process');
|
const {spawn, exec} = require('child_process');
|
||||||
const { promisify } = require('util');
|
const {promisify} = require('util');
|
||||||
const execAsync = promisify(exec);
|
const execAsync = promisify(exec);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -24,8 +24,8 @@ class SQLiteBackup {
|
|||||||
|
|
||||||
this.databasePath = path.resolve(options.databasePath);
|
this.databasePath = path.resolve(options.databasePath);
|
||||||
this.backupDirectory = options.backupDirectory ?
|
this.backupDirectory = options.backupDirectory ?
|
||||||
path.resolve(options.backupDirectory) :
|
path.resolve(options.backupDirectory) :
|
||||||
path.join(path.dirname(this.databasePath), 'backups');
|
path.join(path.dirname(this.databasePath), 'backups');
|
||||||
this.createBackupDir = options.createBackupDir !== false;
|
this.createBackupDir = options.createBackupDir !== false;
|
||||||
|
|
||||||
// Validate database file exists
|
// Validate database file exists
|
||||||
@ -35,8 +35,8 @@ class SQLiteBackup {
|
|||||||
|
|
||||||
// Create backup directory if needed
|
// Create backup directory if needed
|
||||||
if (this.createBackupDir && !fs.existsSync(this.backupDirectory)) {
|
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,
|
error: error.message,
|
||||||
timestamp: new Date().toISOString()
|
timestamp: new Date().toISOString()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -114,9 +114,27 @@ class SQLiteBackup {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const command = `sqlite3 "${backupPath}" "PRAGMA integrity_check;"`;
|
const dbDriver = this._getDatabaseDriver();
|
||||||
const { stdout } = await execAsync(command);
|
|
||||||
return stdout.trim() === 'ok';
|
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) {
|
} catch (error) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -182,7 +200,7 @@ class SQLiteBackup {
|
|||||||
removed: 0,
|
removed: 0,
|
||||||
errors: [error.message]
|
errors: [error.message]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -212,7 +230,6 @@ class SQLiteBackup {
|
|||||||
isValid: null,
|
isValid: null,
|
||||||
checksum: null
|
checksum: null
|
||||||
};
|
};
|
||||||
|
|
||||||
if (includeChecksums) {
|
if (includeChecksums) {
|
||||||
backup.checksum = await this._calculateChecksum(file.path);
|
backup.checksum = await this._calculateChecksum(file.path);
|
||||||
backup.isValid = await this.verifyBackup(file.path);
|
backup.isValid = await this.verifyBackup(file.path);
|
||||||
@ -228,7 +245,7 @@ class SQLiteBackup {
|
|||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(`Failed to list backups: ${error.message}`);
|
throw new Error(`Failed to list backups: ${error.message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -295,7 +312,7 @@ class SQLiteBackup {
|
|||||||
error: error.message,
|
error: error.message,
|
||||||
timestamp: new Date().toISOString()
|
timestamp: new Date().toISOString()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Private methods
|
// Private methods
|
||||||
@ -307,7 +324,7 @@ class SQLiteBackup {
|
|||||||
|
|
||||||
const baseName = path.basename(this.databasePath, '.db');
|
const baseName = path.basename(this.databasePath, '.db');
|
||||||
const timestamp = includeTimestamp ?
|
const timestamp = includeTimestamp ?
|
||||||
`-${new Date().toISOString().replace(/[:.]/g, '-')}` : '';
|
`-${new Date().toISOString().replace(/[:.]/g, '-')}` : '';
|
||||||
|
|
||||||
return `${baseName}-backup${timestamp}.db`;
|
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) {
|
async _backupUsingCopy(backupPath) {
|
||||||
fs.copyFileSync(this.databasePath, backupPath);
|
fs.copyFileSync(this.databasePath, backupPath);
|
||||||
}
|
}
|
||||||
@ -401,7 +437,7 @@ class SQLiteBackup {
|
|||||||
|
|
||||||
async _calculateChecksum(filePath) {
|
async _calculateChecksum(filePath) {
|
||||||
try {
|
try {
|
||||||
const { stdout } = await execAsync(`shasum -a 256 "${filePath}"`);
|
const {stdout} = await execAsync(`shasum -a 256 "${filePath}"`);
|
||||||
return stdout.split(' ')[0];
|
return stdout.split(' ')[0];
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return null;
|
return null;
|
||||||
@ -434,7 +470,8 @@ class SQLiteBackup {
|
|||||||
|
|
||||||
_matchesPattern(filename, pattern) {
|
_matchesPattern(filename, pattern) {
|
||||||
// Simple pattern matching - could be enhanced with a proper glob library
|
// Simple pattern matching - could be enhanced with a proper glob library
|
||||||
if (pattern === '*' || pattern === '*.*') return true;
|
if (pattern === '*' || pattern === '*.*')
|
||||||
|
return true;
|
||||||
if (pattern.startsWith('*.')) {
|
if (pattern.startsWith('*.')) {
|
||||||
const extension = pattern.slice(2);
|
const extension = pattern.slice(2);
|
||||||
return filename.endsWith('.' + extension);
|
return filename.endsWith('.' + extension);
|
||||||
@ -454,7 +491,8 @@ class BackupUtils {
|
|||||||
*/
|
*/
|
||||||
static formatSize(bytes) {
|
static formatSize(bytes) {
|
||||||
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
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));
|
const i = Math.floor(Math.log(bytes) / Math.log(1024));
|
||||||
return `${(bytes / Math.pow(1024, i)).toFixed(2)} ${sizes[i]}`;
|
return `${(bytes / Math.pow(1024, i)).toFixed(2)} ${sizes[i]}`;
|
||||||
}
|
}
|
||||||
@ -465,8 +503,10 @@ class BackupUtils {
|
|||||||
* @returns {string} Formatted duration string
|
* @returns {string} Formatted duration string
|
||||||
*/
|
*/
|
||||||
static formatDuration(milliseconds) {
|
static formatDuration(milliseconds) {
|
||||||
if (milliseconds < 1000) return `${milliseconds}ms`;
|
if (milliseconds < 1000)
|
||||||
if (milliseconds < 60000) return `${(milliseconds / 1000).toFixed(2)}s`;
|
return `${milliseconds}ms`;
|
||||||
|
if (milliseconds < 60000)
|
||||||
|
return `${(milliseconds / 1000).toFixed(2)}s`;
|
||||||
return `${(milliseconds / 60000).toFixed(2)}m`;
|
return `${(milliseconds / 60000).toFixed(2)}m`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -483,7 +523,7 @@ class BackupUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const command = `sqlite3 "${databasePath}" "PRAGMA integrity_check;"`;
|
const command = `sqlite3 "${databasePath}" "PRAGMA integrity_check;"`;
|
||||||
const { stdout } = await execAsync(command);
|
const {stdout} = await execAsync(command);
|
||||||
return stdout.trim() === 'ok';
|
return stdout.trim() === 'ok';
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user