diff --git a/bin/rebuildPad.js b/bin/rebuildPad.js index 0f929ef75..3e6e6e5fa 100644 --- a/bin/rebuildPad.js +++ b/bin/rebuildPad.js @@ -13,7 +13,6 @@ if (process.argv.length !== 4 && process.argv.length !== 5) { throw new Error('Use: node bin/repairPad.js $PADID $REV [$NEWPADID]'); } -const async = require('ep_etherpad-lite/node_modules/async'); const npm = require('ep_etherpad-lite/node_modules/npm'); const util = require('util'); @@ -21,99 +20,76 @@ const padId = process.argv[2]; const newRevHead = process.argv[3]; const newPadId = process.argv[4] || `${padId}-rebuilt`; -let db, oldPad, newPad; -let Pad, PadManager; +(async () => { + await util.promisify(npm.load)({}); -async.series([ - (callback) => npm.load({}, callback), - (callback) => { - // Get a handle into the database - db = require('ep_etherpad-lite/node/db/DB'); - util.callbackify(db.init)(callback); - }, - (callback) => { - Pad = require('ep_etherpad-lite/node/db/Pad').Pad; - PadManager = require('ep_etherpad-lite/node/db/PadManager'); - // Get references to the original pad and to a newly created pad - // HACK: This is a standalone script, so we want to write everything - // out to the database immediately. The only problem with this is - // that a driver (like the mysql driver) can hardcode these values. - db.db.db.settings = {cache: 0, writeInterval: 0, json: true}; - // Validate the newPadId if specified and that a pad with that ID does - // not already exist to avoid overwriting it. - if (!PadManager.isValidPadId(newPadId)) { - throw new Error('Cannot create a pad with that id as it is invalid'); + const db = require('ep_etherpad-lite/node/db/DB'); + await db.init(); + + const Pad = require('ep_etherpad-lite/node/db/Pad').Pad; + const PadManager = require('ep_etherpad-lite/node/db/PadManager'); + // Get references to the original pad and to a newly created pad + // HACK: This is a standalone script, so we want to write everything + // out to the database immediately. The only problem with this is + // that a driver (like the mysql driver) can hardcode these values. + db.db.db.settings = {cache: 0, writeInterval: 0, json: true}; + // Validate the newPadId if specified and that a pad with that ID does + // not already exist to avoid overwriting it. + if (!PadManager.isValidPadId(newPadId)) { + throw new Error('Cannot create a pad with that id as it is invalid'); + } + const exists = await PadManager.doesPadExist(newPadId); + if (exists) throw new Error('Cannot create a pad with that id as it already exists'); + + const oldPad = await PadManager.getPad(padId); + const newPad = new Pad(newPadId); + + // Clone all Chat revisions + const chatHead = oldPad.chatHead; + await Promise.all([...Array(chatHead + 1).keys()].map(async (i) => { + const chat = await db.get(`pad:${padId}:chat:${i}`); + await db.set(`pad:${newPadId}:chat:${i}`, chat); + console.log(`Created: Chat Revision: pad:${newPadId}:chat:${i}`); + })); + + // Rebuild Pad from revisions up to and including the new revision head + const AuthorManager = require('ep_etherpad-lite/node/db/AuthorManager'); + const Changeset = require('ep_etherpad-lite/static/js/Changeset'); + // Author attributes are derived from changesets, but there can also be + // non-author attributes with specific mappings that changesets depend on + // and, AFAICT, cannot be recreated any other way + newPad.pool.numToAttrib = oldPad.pool.numToAttrib; + for (let curRevNum = 0; curRevNum <= newRevHead; curRevNum++) { + const rev = await db.get(`pad:${padId}:revs:${curRevNum}`); + if (rev.meta) { + throw new Error('The specified revision number could not be found.'); } - util.callbackify(PadManager.doesPadExist)(newPadId, (err, exists) => { - if (err != null) return callback(err); - if (exists) throw new Error('Cannot create a pad with that id as it already exists'); - callback(); - }); - }, - (callback) => { - util.callbackify(PadManager.getPad)(padId, (err, pad) => { - if (err) return callback(err); - oldPad = pad; - newPad = new Pad(newPadId); - callback(); - }); - }, - (callback) => { - // Clone all Chat revisions - const chatHead = oldPad.chatHead; - for (let i = 0, curHeadNum = 0; i <= chatHead; i++) { - db.db.get(`pad:${padId}:chat:${i}`, (err, chat) => { - db.db.set(`pad:${newPadId}:chat:${curHeadNum++}`, chat); - console.log(`Created: Chat Revision: pad:${newPadId}:chat:${curHeadNum}`); - }); + const newRevNum = ++newPad.head; + const newRevId = `pad:${newPad.id}:revs:${newRevNum}`; + await Promise.all([ + db.set(newRevId, rev), + AuthorManager.addPad(rev.meta.author, newPad.id), + ]); + newPad.atext = Changeset.applyToAText(rev.changeset, newPad.atext, newPad.pool); + console.log(`Created: Revision: pad:${newPad.id}:revs:${newRevNum}`); + } + + // Add saved revisions up to the new revision head + console.log(newPad.head); + const newSavedRevisions = []; + for (const savedRev of oldPad.savedRevisions) { + if (savedRev.revNum <= newRevHead) { + newSavedRevisions.push(savedRev); + console.log(`Added: Saved Revision: ${savedRev.revNum}`); } - callback(); - }, - (callback) => { - // Rebuild Pad from revisions up to and including the new revision head - const AuthorManager = require('ep_etherpad-lite/node/db/AuthorManager'); - const Changeset = require('ep_etherpad-lite/static/js/Changeset'); - // Author attributes are derived from changesets, but there can also be - // non-author attributes with specific mappings that changesets depend on - // and, AFAICT, cannot be recreated any other way - newPad.pool.numToAttrib = oldPad.pool.numToAttrib; - for (let curRevNum = 0; curRevNum <= newRevHead; curRevNum++) { - db.db.get(`pad:${padId}:revs:${curRevNum}`, (err, rev) => { - if (rev.meta) { - throw new Error('The specified revision number could not be found.'); - } - const newRevNum = ++newPad.head; - const newRevId = `pad:${newPad.id}:revs:${newRevNum}`; - db.db.set(newRevId, rev); - AuthorManager.addPad(rev.meta.author, newPad.id); - newPad.atext = Changeset.applyToAText(rev.changeset, newPad.atext, newPad.pool); - console.log(`Created: Revision: pad:${newPad.id}:revs:${newRevNum}`); - if (newRevNum === newRevHead) { - callback(); - } - }); - } - }, - (callback) => { - // Add saved revisions up to the new revision head - console.log(newPad.head); - const newSavedRevisions = []; - for (const savedRev of oldPad.savedRevisions) { - if (savedRev.revNum <= newRevHead) { - newSavedRevisions.push(savedRev); - console.log(`Added: Saved Revision: ${savedRev.revNum}`); - } - } - newPad.savedRevisions = newSavedRevisions; - callback(); - }, + } + newPad.savedRevisions = newSavedRevisions; + // Save the source pad - (callback) => db.db.set(`pad:${newPadId}`, newPad, callback), - (callback) => { - console.log(`Created: Source Pad: pad:${newPadId}`); - util.callbackify(newPad.saveToDatabase.bind(newPad))(callback); - }, -], (err) => { - if (err) throw err; + await db.set(`pad:${newPadId}`, newPad); + + console.log(`Created: Source Pad: pad:${newPadId}`); + await newPad.saveToDatabase(); + console.info('finished'); -}); +})();