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',
files: 'user/files',
vectors: 'vectors',
backups: 'backups',
});
/**

View File

@ -6,15 +6,16 @@ const sanitize = require('sanitize-filename');
const writeFileAtomicSync = require('write-file-atomic').sync;
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');
/**
* 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} chat The serialized chat to save.
*/
function backupChat(name, chat) {
function backupChat(directory, name, chat) {
try {
const isBackupDisabled = getConfigValue('disableChatBackup', false);
@ -22,17 +23,13 @@ function backupChat(name, chat) {
return;
}
if (!fs.existsSync(PUBLIC_DIRECTORIES.backups)) {
fs.mkdirSync(PUBLIC_DIRECTORIES.backups);
}
// replace non-alphanumeric characters with underscores
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');
removeOldBackups(`chat_${name}_`);
removeOldBackups(directory, `chat_${name}_`);
} catch (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 filePath = path.join(request.user.directories.chats, directoryName, fileName);
writeFileAtomicSync(filePath, jsonlData, 'utf8');
backupChat(directoryName, jsonlData);
backupChat(request.user.directories.backups, directoryName, jsonlData);
return response.send({ result: 'ok' });
} catch (error) {
response.send(error);
@ -455,7 +452,7 @@ router.post('/group/save', jsonParser, (request, response) => {
let chat_data = request.body.chat;
let jsonlData = chat_data.map(JSON.stringify).join('\n');
writeFileAtomicSync(pathToFile, jsonlData, 'utf8');
backupChat(String(id), jsonlData);
backupChat(request.user.directories.backups, String(id), jsonlData);
return response.send({ ok: true });
});

View File

@ -110,10 +110,6 @@ function readPresetsFromDirectory(directoryPath, options = {}) {
async function backupSettings() {
try {
if (!fs.existsSync(PUBLIC_DIRECTORIES.backups)) {
fs.mkdirSync(PUBLIC_DIRECTORIES.backups);
}
const userHandles = await getAllUserHandles();
for (const handle of userHandles) {
@ -131,7 +127,7 @@ async function backupSettings() {
*/
function backupUserSettings(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);
if (!fs.existsSync(sourceFile)) {
@ -139,7 +135,7 @@ function backupUserSettings(handle) {
}
fs.copyFileSync(sourceFile, backupFile);
removeOldBackups(`settings_${handle}`);
removeOldBackups(userDirectories.backups, `settings_${handle}`);
}
const router = express.Router();
@ -227,12 +223,12 @@ router.post('/get', jsonParser, (request, response) => {
router.post('/get-snapshots', jsonParser, async (request, response) => {
try {
const snapshots = fs.readdirSync(PUBLIC_DIRECTORIES.backups);
const snapshots = fs.readdirSync(request.user.directories.backups);
const userFilesPattern = getFilePrefix(request.user.profile.handle);
const userSnapshots = snapshots.filter(x => x.startsWith(userFilesPattern));
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 };
});
@ -252,7 +248,7 @@ router.post('/load-snapshot', jsonParser, async (request, response) => {
}
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)) {
return response.sendStatus(404);
@ -286,7 +282,7 @@ router.post('/restore-snapshot', jsonParser, async (request, response) => {
}
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)) {
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} files - The directory where the uploaded files 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 { Readable } = require('stream');
const { PUBLIC_DIRECTORIES } = require('./constants');
/**
* 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;
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) {
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);
fs.rmSync(files[0]);