diff --git a/bin/rebuildPad.js b/bin/rebuildPad.js index 0013718a9..313510ccd 100644 --- a/bin/rebuildPad.js +++ b/bin/rebuildPad.js @@ -110,8 +110,7 @@ async.series([ // Save the source pad db.db.set("pad:"+newPadId, newPad, function(err) { console.log("Created: Source Pad: pad:" + newPadId); - newPad.saveToDatabase(); - callback(); + newPad.saveToDatabase().then(() => callback(), callback); }); } ], function (err) { diff --git a/src/node/db/API.js b/src/node/db/API.js index 209461114..9f5b786f9 100644 --- a/src/node/db/API.js +++ b/src/node/db/API.js @@ -203,11 +203,10 @@ exports.setText = async function(padID, text) // get the pad let pad = await getPadSafe(padID, true); - // set the text - pad.setText(text); - - // update the clients on the pad - padMessageHandler.updatePadClients(pad); + await Promise.all([ + pad.setText(text), + padMessageHandler.updatePadClients(pad), + ]); } /** @@ -226,12 +225,11 @@ exports.appendText = async function(padID, text) throw new customError("text is not a string", "apierror"); } - // get and update the pad let pad = await getPadSafe(padID, true); - pad.appendText(text); - - // update the clients on the pad - padMessageHandler.updatePadClients(pad); + await Promise.all([ + pad.appendText(text), + padMessageHandler.updatePadClients(pad), + ]); } /** @@ -287,7 +285,7 @@ exports.setHTML = async function(padID, html) // add a new changeset with the new html to the pad try { - importHtml.setPadHTML(pad, cleanText(html)); + await importHtml.setPadHTML(pad, cleanText(html)); } catch (e) { throw new customError("HTML is malformed", "apierror"); } @@ -373,7 +371,7 @@ exports.appendChatMessage = async function(padID, text, authorID, time) // @TODO - missing getPadSafe() call ? // save chat message to database and send message to all connected clients - padMessageHandler.sendChatMessageToPadClients(time, authorID, text, padID); + await padMessageHandler.sendChatMessageToPadClients(time, authorID, text, padID); } /*****************/ @@ -454,7 +452,7 @@ exports.saveRevision = async function(padID, rev) } let author = await authorManager.createAuthor('API'); - pad.addSavedRevision(rev, author.authorID, 'Saved through API call'); + await pad.addSavedRevision(rev, author.authorID, 'Saved through API call'); } /** @@ -575,11 +573,10 @@ exports.restoreRevision = async function(padID, rev) var changeset = builder.toString(); - // append the changeset - pad.appendRevision(changeset); - - // update the clients on the pad - padMessageHandler.updatePadClients(pad); + await Promise.all([ + pad.appendRevision(changeset), + padMessageHandler.updatePadClients(pad), + ]); } /** @@ -688,7 +685,7 @@ exports.setPublicStatus = async function(padID, publicStatus) } // set the password - pad.setPublicStatus(publicStatus); + await pad.setPublicStatus(publicStatus); } /** @@ -726,7 +723,7 @@ exports.setPassword = async function(padID, password) let pad = await getPadSafe(padID, true); // set the password - pad.setPassword(password == "" ? null : password); + await pad.setPassword(password === '' ? null : password); } /** diff --git a/src/node/db/AuthorManager.js b/src/node/db/AuthorManager.js index a17952248..2723717db 100644 --- a/src/node/db/AuthorManager.js +++ b/src/node/db/AuthorManager.js @@ -245,6 +245,6 @@ exports.removePad = async function(authorID, padID) if (author.padIDs !== null) { // remove pad from author delete author.padIDs[padID]; - db.set("globalAuthor:" + authorID, author); + await db.set('globalAuthor:' + authorID, author); } } diff --git a/src/node/db/Pad.js b/src/node/db/Pad.js index 33cc38bcb..85188d2b6 100644 --- a/src/node/db/Pad.js +++ b/src/node/db/Pad.js @@ -71,7 +71,7 @@ Pad.prototype.getPublicStatus = function getPublicStatus() { return this.publicStatus; }; -Pad.prototype.appendRevision = function appendRevision(aChangeset, author) { +Pad.prototype.appendRevision = async function appendRevision(aChangeset, author) { if (!author) { author = ''; } @@ -97,12 +97,14 @@ Pad.prototype.appendRevision = function appendRevision(aChangeset, author) { newRevData.meta.atext = this.atext; } - db.set("pad:" + this.id + ":revs:" + newRev, newRevData); - this.saveToDatabase(); + const p = [ + db.set('pad:' + this.id + ':revs:' + newRev, newRevData), + this.saveToDatabase(), + ]; // set the author to pad if (author) { - authorManager.addPad(author, this.id); + p.push(authorManager.addPad(author, this.id)); } if (this.head == 0) { @@ -110,10 +112,12 @@ Pad.prototype.appendRevision = function appendRevision(aChangeset, author) { } else { hooks.callAll("padUpdate", {'pad':this, 'author': author}); } + + await Promise.all(p); }; // save all attributes to the database -Pad.prototype.saveToDatabase = function saveToDatabase() { +Pad.prototype.saveToDatabase = async function saveToDatabase() { var dbObject = {}; for (var attr in this) { @@ -127,7 +131,7 @@ Pad.prototype.saveToDatabase = function saveToDatabase() { } } - db.set("pad:" + this.id, dbObject); + await db.set('pad:' + this.id, dbObject); } // get time of last edit (changeset application) @@ -244,7 +248,7 @@ Pad.prototype.text = function text() { return this.atext.text; }; -Pad.prototype.setText = function setText(newText) { +Pad.prototype.setText = async function setText(newText) { // clean the new text newText = exports.cleanText(newText); @@ -261,10 +265,10 @@ Pad.prototype.setText = function setText(newText) { } // append the changeset - this.appendRevision(changeset); + await this.appendRevision(changeset); }; -Pad.prototype.appendText = function appendText(newText) { +Pad.prototype.appendText = async function appendText(newText) { // clean the new text newText = exports.cleanText(newText); @@ -274,14 +278,16 @@ Pad.prototype.appendText = function appendText(newText) { var changeset = Changeset.makeSplice(oldText, oldText.length, 0, newText); // append the changeset - this.appendRevision(changeset); + await this.appendRevision(changeset); }; -Pad.prototype.appendChatMessage = function appendChatMessage(text, userId, time) { +Pad.prototype.appendChatMessage = async function appendChatMessage(text, userId, time) { this.chatHead++; // save the chat entry in the database - db.set("pad:" + this.id + ":chat:" + this.chatHead, { "text": text, "userId": userId, "time": time }); - this.saveToDatabase(); + await Promise.all([ + db.set('pad:' + this.id + ':chat:' + this.chatHead, {text, userId, time}), + this.saveToDatabase(), + ]); }; Pad.prototype.getChatMessage = async function getChatMessage(entryNum) { @@ -350,7 +356,7 @@ Pad.prototype.init = async function init(text) { // this pad doesn't exist, so create it let firstChangeset = Changeset.makeSplice("\n", 0, 0, exports.cleanText(text)); - this.appendRevision(firstChangeset, ''); + await this.appendRevision(firstChangeset, ''); } hooks.callAll("padLoad", { 'pad': this }); @@ -366,7 +372,7 @@ Pad.prototype.copy = async function copy(destinationID, force) { // padMessageHandler.kickSessionsFromPad(sourceID); // flush the source pad: - this.saveToDatabase(); + await this.saveToDatabase(); try { @@ -532,6 +538,7 @@ Pad.prototype.copyPadWithoutHistory = async function copyPadWithoutHistory(desti Pad.prototype.remove = async function remove() { var padID = this.id; + const p = []; // kick everyone from this pad padMessageHandler.kickSessionsFromPad(padID); @@ -552,44 +559,45 @@ Pad.prototype.remove = async function remove() { delete group.pads[padID]; // set the new value - db.set("group:" + groupID, group); + p.push(db.set('group:' + groupID, group)); } // remove the readonly entries - let readonlyID = readOnlyManager.getReadOnlyId(padID); - - db.remove("pad2readonly:" + padID); - db.remove("readonly2pad:" + readonlyID); + p.push(readOnlyManager.getReadOnlyId(padID).then(async (readonlyID) => { + await db.remove('readonly2pad:' + readonlyID); + })); + p.push(db.remove('pad2readonly:' + padID)); // delete all chat messages - promises.timesLimit(this.chatHead + 1, 500, function (i) { - return db.remove("pad:" + padID + ":chat:" + i, null); - }) + p.push(promises.timesLimit(this.chatHead + 1, 500, async (i) => { + await db.remove('pad:' + padID + ':chat:' + i, null); + })); // delete all revisions - promises.timesLimit(this.head + 1, 500, function (i) { - return db.remove("pad:" + padID + ":revs:" + i, null); - }) + p.push(promises.timesLimit(this.head + 1, 500, async (i) => { + await db.remove('pad:' + padID + ':revs:' + i, null); + })); // remove pad from all authors who contributed this.getAllAuthors().forEach(authorID => { - authorManager.removePad(authorID, padID); + p.push(authorManager.removePad(authorID, padID)); }); // delete the pad entry and delete pad from padManager - padManager.removePad(padID); + p.push(padManager.removePad(padID)); hooks.callAll("padRemove", { padID }); + await Promise.all(p); } // set in db -Pad.prototype.setPublicStatus = function setPublicStatus(publicStatus) { +Pad.prototype.setPublicStatus = async function setPublicStatus(publicStatus) { this.publicStatus = publicStatus; - this.saveToDatabase(); + await this.saveToDatabase(); }; -Pad.prototype.setPassword = function setPassword(password) { +Pad.prototype.setPassword = async function setPassword(password) { this.passwordHash = password == null ? null : hash(password, generateSalt()); - this.saveToDatabase(); + await this.saveToDatabase(); }; Pad.prototype.isCorrectPassword = function isCorrectPassword(password) { @@ -600,7 +608,7 @@ Pad.prototype.isPasswordProtected = function isPasswordProtected() { return this.passwordHash != null; }; -Pad.prototype.addSavedRevision = function addSavedRevision(revNum, savedById, label) { +Pad.prototype.addSavedRevision = async function addSavedRevision(revNum, savedById, label) { // if this revision is already saved, return silently for (var i in this.savedRevisions) { if (this.savedRevisions[i] && this.savedRevisions[i].revNum === revNum) { @@ -618,7 +626,7 @@ Pad.prototype.addSavedRevision = function addSavedRevision(revNum, savedById, la // save this new saved revision this.savedRevisions.push(savedRevision); - this.saveToDatabase(); + await this.saveToDatabase(); }; Pad.prototype.getSavedRevisions = function getSavedRevisions() { diff --git a/src/node/db/PadManager.js b/src/node/db/PadManager.js index 89e6a84ab..adbbb79e2 100644 --- a/src/node/db/PadManager.js +++ b/src/node/db/PadManager.js @@ -200,10 +200,11 @@ exports.isValidPadId = function(padId) /** * Removes the pad from database and unloads it. */ -exports.removePad = function(padId) { - db.remove("pad:" + padId); +exports.removePad = async (padId) => { + const p = db.remove('pad:' + padId); exports.unloadPad(padId); padList.removePad(padId); + await p; } // removes a pad from the cache diff --git a/src/node/handler/ImportHandler.js b/src/node/handler/ImportHandler.js index 6ea76cac3..0f076f68f 100644 --- a/src/node/handler/ImportHandler.js +++ b/src/node/handler/ImportHandler.js @@ -226,12 +226,12 @@ async function doImport(req, res, padId) if (!req.directDatabaseAccess) { if (importHandledByPlugin || useConvertor || fileIsHTML) { try { - importHtml.setPadHTML(pad, text); + await importHtml.setPadHTML(pad, text); } catch (e) { apiLogger.warn("Error importing, possibly caused by malformed HTML"); } } else { - pad.setText(text); + await pad.setText(text); } } diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index 6f3cab8a5..879d40587 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -294,7 +294,7 @@ async function handleSaveRevisionMessage(client, message) var userId = sessioninfos[client.id].author; let pad = await padManager.getPad(padId); - pad.addSavedRevision(pad.head, userId); + await pad.addSavedRevision(pad.head, userId); } /** @@ -365,7 +365,7 @@ exports.sendChatMessageToPadClients = async function(time, userId, text, padId) let userName = await authorManager.getAuthorName(userId); // save the chat message - pad.appendChatMessage(text, userId, time); + const promise = pad.appendChatMessage(text, userId, time); let msg = { type: "COLLABROOM", @@ -374,6 +374,8 @@ exports.sendChatMessageToPadClients = async function(time, userId, text, padId) // broadcast the chat message to everyone on the pad socketio.sockets.in(padId).json.send(msg); + + await promise; } /** @@ -664,7 +666,7 @@ async function handleUserChanges(data) } try { - pad.appendRevision(changeset, thisSession.author); + await pad.appendRevision(changeset, thisSession.author); } catch(e) { client.json.send({ disconnect: "badChangeset" }); stats.meter('failedChangesets').mark(); @@ -673,13 +675,13 @@ async function handleUserChanges(data) let correctionChangeset = _correctMarkersInPad(pad.atext, pad.pool); if (correctionChangeset) { - pad.appendRevision(correctionChangeset); + await pad.appendRevision(correctionChangeset); } // Make sure the pad always ends with an empty line. if (pad.text().lastIndexOf("\n") !== pad.text().length-1) { var nlChangeset = Changeset.makeSplice(pad.text(), pad.text().length - 1, 0, "\n"); - pad.appendRevision(nlChangeset); + await pad.appendRevision(nlChangeset); } await exports.updatePadClients(pad); diff --git a/src/node/utils/ImportHtml.js b/src/node/utils/ImportHtml.js index 28ce4f689..8cbfdbab9 100644 --- a/src/node/utils/ImportHtml.js +++ b/src/node/utils/ImportHtml.js @@ -22,8 +22,7 @@ const rehype = require("rehype") const format = require("rehype-format") -exports.setPadHTML = function(pad, html) -{ +exports.setPadHTML = async (pad, html) => { var apiLogger = log4js.getLogger("ImportHtml"); var opts = { @@ -103,6 +102,8 @@ exports.setPadHTML = function(pad, html) var theChangeset = builder.toString(); apiLogger.debug('The changeset: ' + theChangeset); - pad.setText("\n"); - pad.appendRevision(theChangeset); + await Promise.all([ + pad.setText('\n'), + pad.appendRevision(theChangeset), + ]); }