Change backups to be user data scoped
This commit is contained in:
parent
0024f96a99
commit
e66b270811
|
@ -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.
|
|
@ -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',
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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 });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
12
src/util.js
12
src/util.js
|
@ -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]);
|
||||||
|
|
Loading…
Reference in New Issue