diff --git a/default/config.yaml b/default/config.yaml index 694a58be6..8a55f6afb 100644 --- a/default/config.yaml +++ b/default/config.yaml @@ -83,6 +83,8 @@ skipContentCheck: false disableChatBackup: false # Number of backups to keep for each chat and settings file numberOfBackups: 50 +# Interval in milliseconds to throttle chat backups per user +chatBackupThrottleInterval: 10000 # Allowed hosts for card downloads whitelistImportDomains: - localhost diff --git a/src/endpoints/chats.js b/src/endpoints/chats.js index 855ddff3a..455224a81 100644 --- a/src/endpoints/chats.js +++ b/src/endpoints/chats.js @@ -4,6 +4,7 @@ const readline = require('readline'); const express = require('express'); const sanitize = require('sanitize-filename'); const writeFileAtomicSync = require('write-file-atomic').sync; +const _ = require('lodash'); const { jsonParser, urlencodedParser } = require('../express-common'); const { getConfigValue, humanizedISO8601DateTime, tryParse, generateTimestamp, removeOldBackups } = require('../util'); @@ -34,6 +35,27 @@ function backupChat(directory, name, chat) { } } +const backupFunctions = new Map(); + +/** + * Gets a backup function for a user. + * @param {string} handle User handle + * @returns {function(string, string, string): void} Backup function + */ +function getBackupFunction(handle) { + const throttleInterval = getConfigValue('chatBackupThrottleInterval', 10_000); + if (!backupFunctions.has(handle)) { + backupFunctions.set(handle, _.throttle(backupChat, throttleInterval, { leading: true, trailing: true })); + } + return backupFunctions.get(handle); +} + +process.on('exit', () => { + for (const func of backupFunctions.values()) { + func.flush(); + } +}); + /** * Imports a chat from Ooba's format. * @param {string} userName User name @@ -147,7 +169,7 @@ router.post('/save', jsonParser, function (request, response) { const fileName = `${String(request.body.file_name)}.jsonl`; const filePath = path.join(request.user.directories.chats, directoryName, sanitize(fileName)); writeFileAtomicSync(filePath, jsonlData, 'utf8'); - backupChat(request.user.directories.backups, directoryName, jsonlData); + getBackupFunction(request.user.profile.handle)(request.user.directories.backups, directoryName, jsonlData); return response.send({ result: 'ok' }); } catch (error) { response.send(error); @@ -446,7 +468,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(request.user.directories.backups, String(id), jsonlData); + getBackupFunction(request.user.profile.handle)(request.user.directories.backups, String(id), jsonlData); return response.send({ ok: true }); });