Change backups to be user data scoped

This commit is contained in:
Cohee 2024-05-28 17:49:34 +03:00
parent 0024f96a99
commit e66b270811
6 changed files with 30 additions and 26 deletions

9
backups/!README.md Normal file
View File

@ -0,0 +1,9 @@
# Looking for setting snapshots or chat backups?
Individual user backups are now located in the data directory.
Example for the default user under default data root:
/data/default-user/backups
This folder remains for historical purposes only.

View File

@ -41,6 +41,7 @@ const USER_DIRECTORY_TEMPLATE = Object.freeze({
comfyWorkflows: 'user/workflows', comfyWorkflows: 'user/workflows',
files: 'user/files', files: 'user/files',
vectors: 'vectors', vectors: 'vectors',
backups: 'backups',
}); });
/** /**

View File

@ -6,15 +6,16 @@ const sanitize = require('sanitize-filename');
const writeFileAtomicSync = require('write-file-atomic').sync; const writeFileAtomicSync = require('write-file-atomic').sync;
const { jsonParser, urlencodedParser } = require('../express-common'); const { jsonParser, urlencodedParser } = require('../express-common');
const { PUBLIC_DIRECTORIES, UPLOADS_PATH } = require('../constants'); const { UPLOADS_PATH } = require('../constants');
const { getConfigValue, humanizedISO8601DateTime, tryParse, generateTimestamp, removeOldBackups } = require('../util'); const { getConfigValue, humanizedISO8601DateTime, tryParse, generateTimestamp, removeOldBackups } = require('../util');
/** /**
* Saves a chat to the backups directory. * Saves a chat to the backups directory.
* @param {string} directory The user's backups directory.
* @param {string} name The name of the chat. * @param {string} name The name of the chat.
* @param {string} chat The serialized chat to save. * @param {string} chat The serialized chat to save.
*/ */
function backupChat(name, chat) { function backupChat(directory, name, chat) {
try { try {
const isBackupDisabled = getConfigValue('disableChatBackup', false); const isBackupDisabled = getConfigValue('disableChatBackup', false);
@ -22,17 +23,13 @@ function backupChat(name, chat) {
return; return;
} }
if (!fs.existsSync(PUBLIC_DIRECTORIES.backups)) {
fs.mkdirSync(PUBLIC_DIRECTORIES.backups);
}
// replace non-alphanumeric characters with underscores // replace non-alphanumeric characters with underscores
name = sanitize(name).replace(/[^a-z0-9]/gi, '_').toLowerCase(); name = sanitize(name).replace(/[^a-z0-9]/gi, '_').toLowerCase();
const backupFile = path.join(PUBLIC_DIRECTORIES.backups, `chat_${name}_${generateTimestamp()}.jsonl`); const backupFile = path.join(directory, `chat_${name}_${generateTimestamp()}.jsonl`);
writeFileAtomicSync(backupFile, chat, 'utf-8'); writeFileAtomicSync(backupFile, chat, 'utf-8');
removeOldBackups(`chat_${name}_`); removeOldBackups(directory, `chat_${name}_`);
} catch (err) { } catch (err) {
console.log(`Could not backup chat for ${name}`, err); console.log(`Could not backup chat for ${name}`, err);
} }
@ -151,7 +148,7 @@ router.post('/save', jsonParser, function (request, response) {
const fileName = `${sanitize(String(request.body.file_name))}.jsonl`; const fileName = `${sanitize(String(request.body.file_name))}.jsonl`;
const filePath = path.join(request.user.directories.chats, directoryName, fileName); const filePath = path.join(request.user.directories.chats, directoryName, fileName);
writeFileAtomicSync(filePath, jsonlData, 'utf8'); writeFileAtomicSync(filePath, jsonlData, 'utf8');
backupChat(directoryName, jsonlData); backupChat(request.user.directories.backups, directoryName, jsonlData);
return response.send({ result: 'ok' }); return response.send({ result: 'ok' });
} catch (error) { } catch (error) {
response.send(error); response.send(error);
@ -455,7 +452,7 @@ router.post('/group/save', jsonParser, (request, response) => {
let chat_data = request.body.chat; let chat_data = request.body.chat;
let jsonlData = chat_data.map(JSON.stringify).join('\n'); let jsonlData = chat_data.map(JSON.stringify).join('\n');
writeFileAtomicSync(pathToFile, jsonlData, 'utf8'); writeFileAtomicSync(pathToFile, jsonlData, 'utf8');
backupChat(String(id), jsonlData); backupChat(request.user.directories.backups, String(id), jsonlData);
return response.send({ ok: true }); return response.send({ ok: true });
}); });

View File

@ -110,10 +110,6 @@ function readPresetsFromDirectory(directoryPath, options = {}) {
async function backupSettings() { async function backupSettings() {
try { try {
if (!fs.existsSync(PUBLIC_DIRECTORIES.backups)) {
fs.mkdirSync(PUBLIC_DIRECTORIES.backups);
}
const userHandles = await getAllUserHandles(); const userHandles = await getAllUserHandles();
for (const handle of userHandles) { for (const handle of userHandles) {
@ -131,7 +127,7 @@ async function backupSettings() {
*/ */
function backupUserSettings(handle) { function backupUserSettings(handle) {
const userDirectories = getUserDirectories(handle); const userDirectories = getUserDirectories(handle);
const backupFile = path.join(PUBLIC_DIRECTORIES.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 (!fs.existsSync(sourceFile)) { if (!fs.existsSync(sourceFile)) {
@ -139,7 +135,7 @@ function backupUserSettings(handle) {
} }
fs.copyFileSync(sourceFile, backupFile); fs.copyFileSync(sourceFile, backupFile);
removeOldBackups(`settings_${handle}`); removeOldBackups(userDirectories.backups, `settings_${handle}`);
} }
const router = express.Router(); const router = express.Router();
@ -227,12 +223,12 @@ router.post('/get', jsonParser, (request, response) => {
router.post('/get-snapshots', jsonParser, async (request, response) => { router.post('/get-snapshots', jsonParser, async (request, response) => {
try { try {
const snapshots = fs.readdirSync(PUBLIC_DIRECTORIES.backups); const snapshots = fs.readdirSync(request.user.directories.backups);
const userFilesPattern = getFilePrefix(request.user.profile.handle); const userFilesPattern = getFilePrefix(request.user.profile.handle);
const userSnapshots = snapshots.filter(x => x.startsWith(userFilesPattern)); const userSnapshots = snapshots.filter(x => x.startsWith(userFilesPattern));
const result = userSnapshots.map(x => { const result = userSnapshots.map(x => {
const stat = fs.statSync(path.join(PUBLIC_DIRECTORIES.backups, x)); const stat = fs.statSync(path.join(request.user.directories.backups, x));
return { date: stat.ctimeMs, name: x, size: stat.size }; return { date: stat.ctimeMs, name: x, size: stat.size };
}); });
@ -252,7 +248,7 @@ router.post('/load-snapshot', jsonParser, async (request, response) => {
} }
const snapshotName = request.body.name; const snapshotName = request.body.name;
const snapshotPath = path.join(PUBLIC_DIRECTORIES.backups, snapshotName); const snapshotPath = path.join(request.user.directories.backups, snapshotName);
if (!fs.existsSync(snapshotPath)) { if (!fs.existsSync(snapshotPath)) {
return response.sendStatus(404); return response.sendStatus(404);
@ -286,7 +282,7 @@ router.post('/restore-snapshot', jsonParser, async (request, response) => {
} }
const snapshotName = request.body.name; const snapshotName = request.body.name;
const snapshotPath = path.join(PUBLIC_DIRECTORIES.backups, snapshotName); const snapshotPath = path.join(request.user.directories.backups, snapshotName);
if (!fs.existsSync(snapshotPath)) { if (!fs.existsSync(snapshotPath)) {
return response.sendStatus(404); return response.sendStatus(404);

View File

@ -87,6 +87,7 @@ const STORAGE_KEYS = {
* @property {string} comfyWorkflows - The directory where the ComfyUI workflows are stored * @property {string} comfyWorkflows - The directory where the ComfyUI workflows are stored
* @property {string} files - The directory where the uploaded files are stored * @property {string} files - The directory where the uploaded files are stored
* @property {string} vectors - The directory where the vectors are stored * @property {string} vectors - The directory where the vectors are stored
* @property {string} backups - The directory where the backups are stored
*/ */
/** /**

View File

@ -9,8 +9,6 @@ const yaml = require('yaml');
const { default: simpleGit } = require('simple-git'); const { default: simpleGit } = require('simple-git');
const { Readable } = require('stream'); const { Readable } = require('stream');
const { PUBLIC_DIRECTORIES } = require('./constants');
/** /**
* Parsed config object. * Parsed config object.
*/ */
@ -360,14 +358,16 @@ function generateTimestamp() {
} }
/** /**
* @param {string} prefix * Remove old backups with the given prefix from a specified directory.
* @param {string} directory The root directory to remove backups from.
* @param {string} prefix File prefix to filter backups by.
*/ */
function removeOldBackups(prefix) { function removeOldBackups(directory, prefix) {
const MAX_BACKUPS = 50; const MAX_BACKUPS = 50;
let files = fs.readdirSync(PUBLIC_DIRECTORIES.backups).filter(f => f.startsWith(prefix)); let files = fs.readdirSync(directory).filter(f => f.startsWith(prefix));
if (files.length > MAX_BACKUPS) { if (files.length > MAX_BACKUPS) {
files = files.map(f => path.join(PUBLIC_DIRECTORIES.backups, f)); files = files.map(f => path.join(directory, f));
files.sort((a, b) => fs.statSync(a).mtimeMs - fs.statSync(b).mtimeMs); files.sort((a, b) => fs.statSync(a).mtimeMs - fs.statSync(b).mtimeMs);
fs.rmSync(files[0]); fs.rmSync(files[0]);