Prevent duplicate settings auto-backups
This commit is contained in:
parent
1b6e816525
commit
f53c77b9d8
|
@ -3,7 +3,7 @@ const path = require('path');
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const writeFileAtomicSync = require('write-file-atomic').sync;
|
const writeFileAtomicSync = require('write-file-atomic').sync;
|
||||||
const { PUBLIC_DIRECTORIES, SETTINGS_FILE } = require('../constants');
|
const { SETTINGS_FILE } = require('../constants');
|
||||||
const { getConfigValue, generateTimestamp, removeOldBackups } = require('../util');
|
const { getConfigValue, generateTimestamp, removeOldBackups } = require('../util');
|
||||||
const { jsonParser } = require('../express-common');
|
const { jsonParser } = require('../express-common');
|
||||||
const { getAllUserHandles, getUserDirectories } = require('../users');
|
const { getAllUserHandles, getUserDirectories } = require('../users');
|
||||||
|
@ -27,12 +27,12 @@ const AUTOSAVE_FUNCTIONS = new Map();
|
||||||
*/
|
*/
|
||||||
function triggerAutoSave(handle) {
|
function triggerAutoSave(handle) {
|
||||||
if (!AUTOSAVE_FUNCTIONS.has(handle)) {
|
if (!AUTOSAVE_FUNCTIONS.has(handle)) {
|
||||||
const throttledAutoSave = _.throttle(() => backupUserSettings(handle), AUTOSAVE_INTERVAL);
|
const throttledAutoSave = _.throttle(() => backupUserSettings(handle, true), AUTOSAVE_INTERVAL);
|
||||||
AUTOSAVE_FUNCTIONS.set(handle, throttledAutoSave);
|
AUTOSAVE_FUNCTIONS.set(handle, throttledAutoSave);
|
||||||
}
|
}
|
||||||
|
|
||||||
const functionToCall = AUTOSAVE_FUNCTIONS.get(handle);
|
const functionToCall = AUTOSAVE_FUNCTIONS.get(handle);
|
||||||
if (functionToCall) {
|
if (functionToCall && typeof functionToCall === 'function') {
|
||||||
functionToCall();
|
functionToCall();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,7 +113,7 @@ async function backupSettings() {
|
||||||
const userHandles = await getAllUserHandles();
|
const userHandles = await getAllUserHandles();
|
||||||
|
|
||||||
for (const handle of userHandles) {
|
for (const handle of userHandles) {
|
||||||
backupUserSettings(handle);
|
backupUserSettings(handle, true);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('Could not backup settings file', err);
|
console.log('Could not backup settings file', err);
|
||||||
|
@ -123,13 +123,18 @@ async function backupSettings() {
|
||||||
/**
|
/**
|
||||||
* Makes a backup of the user's settings file.
|
* Makes a backup of the user's settings file.
|
||||||
* @param {string} handle User handle
|
* @param {string} handle User handle
|
||||||
|
* @param {boolean} preventDuplicates Prevent duplicate backups
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
function backupUserSettings(handle) {
|
function backupUserSettings(handle, preventDuplicates) {
|
||||||
const userDirectories = getUserDirectories(handle);
|
const userDirectories = getUserDirectories(handle);
|
||||||
const backupFile = path.join(userDirectories.backups, `${getFilePrefix(handle)}${generateTimestamp()}.json`);
|
const backupFile = path.join(userDirectories.backups, `${getFilePrefix(handle)}${generateTimestamp()}.json`);
|
||||||
const sourceFile = path.join(userDirectories.root, SETTINGS_FILE);
|
const sourceFile = path.join(userDirectories.root, SETTINGS_FILE);
|
||||||
|
|
||||||
|
if (preventDuplicates && isDuplicateBackup(handle, sourceFile)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!fs.existsSync(sourceFile)) {
|
if (!fs.existsSync(sourceFile)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -138,6 +143,52 @@ function backupUserSettings(handle) {
|
||||||
removeOldBackups(userDirectories.backups, `settings_${handle}`);
|
removeOldBackups(userDirectories.backups, `settings_${handle}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the backup would be a duplicate.
|
||||||
|
* @param {string} handle User handle
|
||||||
|
* @param {string} sourceFile Source file path
|
||||||
|
* @returns {boolean} True if the backup is a duplicate
|
||||||
|
*/
|
||||||
|
function isDuplicateBackup(handle, sourceFile) {
|
||||||
|
const latestBackup = getLatestBackup(handle);
|
||||||
|
if (!latestBackup) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return areFilesEqual(latestBackup, sourceFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the two files are equal.
|
||||||
|
* @param {string} file1 File path
|
||||||
|
* @param {string} file2 File path
|
||||||
|
*/
|
||||||
|
function areFilesEqual(file1, file2) {
|
||||||
|
if (!fs.existsSync(file1) || !fs.existsSync(file2)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const content1 = fs.readFileSync(file1);
|
||||||
|
const content2 = fs.readFileSync(file2);
|
||||||
|
return content1.toString() === content2.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the latest backup file for a user.
|
||||||
|
* @param {string} handle User handle
|
||||||
|
* @returns {string|null} Latest backup file. Null if no backup exists.
|
||||||
|
*/
|
||||||
|
function getLatestBackup(handle) {
|
||||||
|
const userDirectories = getUserDirectories(handle);
|
||||||
|
const backupFiles = fs.readdirSync(userDirectories.backups)
|
||||||
|
.filter(x => x.startsWith(getFilePrefix(handle)))
|
||||||
|
.map(x => ({ name: x, ctime: fs.statSync(path.join(userDirectories.backups, x)).ctimeMs }));
|
||||||
|
const latestBackup = backupFiles.sort((a, b) => b.ctime - a.ctime)[0]?.name;
|
||||||
|
if (!latestBackup) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return path.join(userDirectories.backups, latestBackup);
|
||||||
|
}
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
router.post('/save', jsonParser, function (request, response) {
|
router.post('/save', jsonParser, function (request, response) {
|
||||||
|
@ -265,7 +316,7 @@ router.post('/load-snapshot', jsonParser, async (request, response) => {
|
||||||
|
|
||||||
router.post('/make-snapshot', jsonParser, async (request, response) => {
|
router.post('/make-snapshot', jsonParser, async (request, response) => {
|
||||||
try {
|
try {
|
||||||
backupUserSettings(request.user.profile.handle);
|
backupUserSettings(request.user.profile.handle, false);
|
||||||
response.sendStatus(204);
|
response.sendStatus(204);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
|
Loading…
Reference in New Issue