Added ability to backup remote DB

Added ability to restore DB
This commit is contained in:
Isaac Abadi
2022-04-21 19:29:50 -04:00
parent 091f81bb38
commit a288163644
19 changed files with 420 additions and 29 deletions

View File

@@ -1924,6 +1924,45 @@ app.post('/api/updateTaskSchedule', optionalJwt, async (req, res) => {
res.send({success: true});
});
app.post('/api/updateTaskData', optionalJwt, async (req, res) => {
const task_key = req.body.task_key;
const new_data = req.body.new_data;
const success = await db_api.updateRecord('tasks', {key: task_key}, {data: new_data});
res.send({success: success});
});
app.post('/api/getDBBackups', optionalJwt, async (req, res) => {
const backup_dir = path.join('appdata', 'db_backup');
const db_backups = [];
const candidate_backups = await utils.recFindByExt(backup_dir, 'bak', null, [], false);
for (let i = 0; i < candidate_backups.length; i++) {
const candidate_backup = candidate_backups[i];
// must have specific format
if (candidate_backup.split('.').length - 1 !== 4) continue;
const candidate_backup_path = candidate_backup;
const stats = fs.statSync(candidate_backup_path);
db_backups.push({ name: path.basename(candidate_backup), timestamp: parseInt(candidate_backup.split('.')[2]), size: stats.size, source: candidate_backup.includes('local') ? 'local' : 'remote' });
}
db_backups.sort((a,b) => b.timestamp - a.timestamp);
res.send({db_backups: db_backups});
});
app.post('/api/restoreDBBackup', optionalJwt, async (req, res) => {
const file_name = req.body.file_name;
const success = await db_api.restoreDB(file_name);
res.send({success: success});
});
// logs management
app.post('/api/logs', optionalJwt, async function(req, res) {

View File

@@ -987,6 +987,52 @@ const createDownloadsRecords = (downloads) => {
return new_downloads;
}
exports.backupDB = async () => {
const backup_dir = path.join('appdata', 'db_backup');
fs.ensureDirSync(backup_dir);
const backup_file_name = `${using_local_db ? 'local' : 'remote'}_db.json.${Date.now()/1000}.bak`;
const path_to_backups = path.join(backup_dir, backup_file_name);
logger.verbose(`Backing up ${using_local_db ? 'local' : 'remote'} DB to ${path_to_backups}`);
const table_to_records = {};
for (let i = 0; i < tables_list.length; i++) {
const table = tables_list[i];
table_to_records[table] = await exports.getRecords(table);
}
fs.writeJsonSync(path_to_backups, table_to_records);
return backup_file_name;
}
exports.restoreDB = async (file_name) => {
const path_to_backup = path.join('appdata', 'db_backup', file_name);
logger.debug('Reading database backup file.');
const table_to_records = fs.readJSONSync(path_to_backup);
if (!table_to_records) {
logger.error(`Failed to restore DB! Backup file '${path_to_backup}' could not be read.`);
return false;
}
logger.debug('Clearing database.');
await exports.removeAllRecords();
logger.debug('Database cleared! Beginning restore.');
let success = true;
for (let i = 0; i < tables_list.length; i++) {
const table = tables_list[i];
if (!table_to_records[table] || table_to_records[table].length === 0) continue;
success &= await exports.bulkInsertRecordsIntoTable(table, table_to_records[table]);
}
logger.debug('Restore finished!');
return success;
}
exports.transferDB = async (local_to_remote) => {
const table_to_records = {};
for (let i = 0; i < tables_list.length; i++) {
@@ -996,9 +1042,8 @@ exports.transferDB = async (local_to_remote) => {
using_local_db = !local_to_remote;
if (local_to_remote) {
// backup local DB
logger.debug('Backup up Local DB...');
await fs.copyFile('appdata/local_db.json', `appdata/local_db.json.${Date.now()/1000}.bak`);
logger.debug('Backup up DB...');
await exports.backupDB();
const db_connected = await exports.connectToDB(5, true);
if (!db_connected) {
logger.error('Failed to transfer database - could not connect to MongoDB. Verify that your connection URL is valid.');

View File

@@ -7,8 +7,8 @@ const scheduler = require('node-schedule');
const TASKS = {
backup_local_db: {
run: utils.backupLocalDB,
title: 'Backup Local DB',
run: db_api.backupDB,
title: 'Backup DB',
job: null
},
missing_files_check: {
@@ -81,7 +81,8 @@ const setupTasks = async () => {
confirming: false,
data: null,
error: null,
schedule: null
schedule: null,
options: {}
});
} else {
// reset task if necessary

View File

@@ -70,6 +70,17 @@ describe('Database', async function() {
const success = await db_api.getRecord('test', {test: 'test'});
assert(success);
});
it('Restore db', async function() {
const db_stats = await db_api.getDBStats();
const file_name = await db_api.backupDB();
await db_api.restoreDB(file_name);
const new_db_stats = await db_api.getDBStats();
assert(JSON.stringify(db_stats), JSON.stringify(new_db_stats));
});
});
describe('Export', function() {
@@ -393,7 +404,7 @@ describe('Tasks', function() {
await tasks_api.initialize();
});
it('Backup local db', async function() {
it('Backup db', async function() {
const backups_original = await utils.recFindByExt('appdata', 'bak');
const original_length = backups_original.length;
await tasks_api.executeTask('backup_local_db');

View File

@@ -266,13 +266,7 @@ function getCurrentDownloader() {
return details_json['downloader'];
}
async function backupLocalDB() {
const path_to_backups = path.join('appdata', 'db_backup');
fs.ensureDir(path_to_backups);
await fs.copyFile('appdata/local_db.json', path.join(path_to_backups, `local_db.json.${Date.now()/1000}.bak`));
}
async function recFindByExt(base,ext,files,result)
async function recFindByExt(base, ext, files, result, recursive = true)
{
files = files || (await fs.readdir(base))
result = result || []
@@ -281,6 +275,7 @@ async function recFindByExt(base,ext,files,result)
var newbase = path.join(base,file)
if ( (await fs.stat(newbase)).isDirectory() )
{
if (!recursive) continue;
result = await recFindByExt(newbase,ext,await fs.readdir(newbase),result)
}
else
@@ -396,7 +391,6 @@ module.exports = {
getMatchingCategoryFiles: getMatchingCategoryFiles,
addUIDsToCategory: addUIDsToCategory,
getCurrentDownloader: getCurrentDownloader,
backupLocalDB: backupLocalDB,
recFindByExt: recFindByExt,
removeFileExtension: removeFileExtension,
formatDateString: formatDateString,