Merge pull request #3158 from SillyTavern/max-total-chat-backups Add maxTotalChatBackups config.yaml value

c05265d1a1c4a7b777d4a2ccd7b3e6f23426eced

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
3 files changed, +23 -5Ignore whitespace
default/config.yaml+2 -0
@@ -98,6 +98,8 @@ skipContentCheck: false
9898disableChatBackup: false
9999# Number of backups to keep for each chat and settings file
100100numberOfBackups: 50
101+# Maximum number of chat backups to keep per user (starting from the most recent). Set to -1 to keep all backups.
102+maxTotalChatBackups: -1
101103# Interval in milliseconds to throttle chat backups per user
102104chatBackupThrottleInterval: 10000
103105# Allowed hosts for card downloads
src/endpoints/chats.js+10 -2
@@ -11,6 +11,10 @@ import _ from 'lodash';
1111import { jsonParser, urlencodedParser } from '../express-common.js';
1212import { getConfigValue, humanizedISO8601DateTime, tryParse, generateTimestamp, removeOldBackups } from '../util.js';
1313
14+const isBackupDisabled = getConfigValue('disableChatBackup', false);
15+const maxTotalChatBackups = Number(getConfigValue('maxTotalChatBackups', -1));
16+const throttleInterval = getConfigValue('chatBackupThrottleInterval', 10_000);
17+
1418/**
1519 * Saves a chat to the backups directory.
1620 * @param {string} directory The user's backups directory.
@@ -19,7 +23,6 @@ import { getConfigValue, humanizedISO8601DateTime, tryParse, generateTimestamp,
1923 */
2024function backupChat(directory, name, chat) {
2125 try {
22- const isBackupDisabled = getConfigValue('disableChatBackup', false);
2326
2427 if (isBackupDisabled) {
2528 return;
@@ -32,6 +35,12 @@ function backupChat(directory, name, chat) {
3235 writeFileAtomicSync(backupFile, chat, 'utf-8');
3336
3437 removeOldBackups(directory, `chat_${name}_`);
38+
39+ if (isNaN(maxTotalChatBackups) || maxTotalChatBackups < 0) {
40+ return;
41+ }
42+
43+ removeOldBackups(directory, 'chat_', maxTotalChatBackups);
3544 } catch (err) {
3645 console.log(`Could not backup chat for ${name}`, err);
3746 }
@@ -45,7 +54,6 @@ const backupFunctions = new Map();
4554 * @returns {function(string, string, string): void} Backup function
4655 */
4756function getBackupFunction(handle) {
48- const throttleInterval = getConfigValue('chatBackupThrottleInterval', 10_000);
4957 if (!backupFunctions.has(handle)) {
5058 backupFunctions.set(handle, _.throttle(backupChat, throttleInterval, { leading: true, trailing: true }));
5159 }
src/util.js+11 -3
@@ -376,16 +376,24 @@ export function generateTimestamp() {
376376 * Remove old backups with the given prefix from a specified directory.
377377 * @param {string} directory The root directory to remove backups from.
378378 * @param {string} prefix File prefix to filter backups by.
379+ * @param {number?} limit Maximum number of backups to keep. If null, the limit is determined by the `numberOfBackups` config value.
379380 */
380381export function removeOldBackups(directory, prefix, limit = null) {
381382 const MAX_BACKUPS = limit ?? Number(getConfigValue('numberOfBackups', 50));
382383
383384 let files = fs.readdirSync(directory).filter(f => f.startsWith(prefix));
384385 if (files.length > MAX_BACKUPS) {
385386 files = files.map(f => path.join(directory, f));
386387 files.sort((a, b) => fs.statSync(a).mtimeMs - fs.statSync(b).mtimeMs);
387388
388- fs.rmSync(files[0]);
389+ while (files.length > MAX_BACKUPS) {
390+ const oldest = files.shift();
391+ if (!oldest) {
392+ break;
393+ }
394+
395+ fs.rmSync(oldest);
396+ }
389397 }
390398}
391399