bugfix, lint and refactor all bin scripts (#4617)

* bugfix, lint and refactor all bin scripts

* for squash: throw Error(message) rather than log(message); throw Error()

* for squash: Exit non-0 on unhandled Promise rejection

Many of the recent lint changes have converted normal functions to
async functions, and an error thrown in an async function does not
cause Node.js to exit by default.

* for squash: fix `require()` paths

* for squash: remove erroneous `Object.keys()` call

* for squash: fix missing `continue` statements

* for squash: Fix HTTP method for deleteSession

* for squash: delete erroneous throw

Throw is only for errors, not successful completion.

* for squash: redo migrateDirtyDBtoRealDB.js to fix async bugs

* for squash: fix erroneous use of `for..of`

* for squash: Add line break between statements

* for squash: put closing paren on same line as last arg

* for squash: Move `log()` back up where it was

to minimize the diff to develop

* for squash: indentation fixes

* for squash: typo fix

* for squash: wrap long lines

* for squash: use `util.callbackify` to silence promise/no-callback-in-promise warning

* for squash: use double quotes to improve readability

Co-authored-by: Richard Hansen <rhansen@rhansen.org>
This commit is contained in:
John McLear 2021-01-18 08:53:15 +00:00 committed by GitHub
parent c0d9881a62
commit 2fdc737355
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 458 additions and 466 deletions

View file

@ -1,111 +1,107 @@
'use strict';
/*
* This is a debug tool. It checks all revisions for data corruption
*/
if (process.argv.length != 3) {
console.error('Use: node bin/checkPadDeltas.js $PADID');
process.exit(1);
}
// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an
// unhandled rejection into an uncaught exception, which does cause Node.js to exit.
process.on('unhandledRejection', (err) => { throw err; });
if (process.argv.length !== 3) throw new Error('Use: node bin/checkPadDeltas.js $PADID');
// get the padID
const padId = process.argv[2];
// load and initialize NPM;
const expect = require('expect.js');
const diff = require('diff');
var async = require('async');
const npm = require('../src/node_modules/npm');
var async = require('ep_etherpad-lite/node_modules/async');
const Changeset = require('ep_etherpad-lite/static/js/Changeset');
const expect = require('../tests/frontend/lib/expect');
const diff = require('ep_etherpad-lite/node_modules/diff');
const npm = require('ep_etherpad-lite/node_modules/npm');
npm.load({}, async () => {
try {
// initialize database
const settings = require('../src/node/utils/Settings');
const db = require('../src/node/db/DB');
await db.init();
// initialize database
require('ep_etherpad-lite/node/utils/Settings');
const db = require('ep_etherpad-lite/node/db/DB');
await db.init();
// load modules
const Changeset = require('ep_etherpad-lite/static/js/Changeset');
const padManager = require('../src/node/db/PadManager');
// load modules
const Changeset = require('ep_etherpad-lite/static/js/Changeset');
const padManager = require('ep_etherpad-lite/node/db/PadManager');
const exists = await padManager.doesPadExists(padId);
if (!exists) {
console.error('Pad does not exist');
process.exit(1);
const exists = await padManager.doesPadExists(padId);
if (!exists) throw new Error('Pad does not exist');
// get the pad
const pad = await padManager.getPad(padId);
// create an array with key revisions
// key revisions always save the full pad atext
const head = pad.getHeadRevisionNumber();
const keyRevisions = [];
for (let i = 0; i < head; i += 100) {
keyRevisions.push(i);
}
// create an array with all revisions
const revisions = [];
for (let i = 0; i <= head; i++) {
revisions.push(i);
}
let atext = Changeset.makeAText('\n');
// run trough all revisions
for (const revNum of revisions) {
// console.log('Fetching', revNum)
const revision = await db.get(`pad:${padId}:revs:${revNum}`);
// check if there is a atext in the keyRevisions
if (~keyRevisions.indexOf(revNum) &&
(revision === undefined ||
revision.meta === undefined ||
revision.meta.atext === undefined)) {
console.error(`No atext in key revision ${revNum}`);
continue;
}
// get the pad
const pad = await padManager.getPad(padId);
// create an array with key revisions
// key revisions always save the full pad atext
const head = pad.getHeadRevisionNumber();
const keyRevisions = [];
for (var i = 0; i < head; i += 100) {
keyRevisions.push(i);
// try glue everything together
try {
// console.log("check revision ", revNum);
const cs = revision.changeset;
atext = Changeset.applyToAText(cs, atext, pad.pool);
} catch (e) {
console.error(`Bad changeset at revision ${revNum} - ${e.message}`);
continue;
}
// create an array with all revisions
const revisions = [];
for (var i = 0; i <= head; i++) {
revisions.push(i);
}
let atext = Changeset.makeAText('\n');
// run trough all revisions
async.forEachSeries(revisions, (revNum, callback) => {
// console.log('Fetching', revNum)
db.db.get(`pad:${padId}:revs:${revNum}`, (err, revision) => {
if (err) return callback(err);
// check if there is a atext in the keyRevisions
if (~keyRevisions.indexOf(revNum) && (revision === undefined || revision.meta === undefined || revision.meta.atext === undefined)) {
console.error(`No atext in key revision ${revNum}`);
callback();
return;
}
try {
// console.log("check revision ", revNum);
const cs = revision.changeset;
atext = Changeset.applyToAText(cs, atext, pad.pool);
} catch (e) {
console.error(`Bad changeset at revision ${revNum} - ${e.message}`);
callback();
return;
}
if (~keyRevisions.indexOf(revNum)) {
try {
expect(revision.meta.atext.text).to.eql(atext.text);
expect(revision.meta.atext.attribs).to.eql(atext.attribs);
} catch (e) {
console.error(`Atext in key revision ${revNum} doesn't match computed one.`);
console.log(diff.diffChars(atext.text, revision.meta.atext.text).map((op) => { if (!op.added && !op.removed) op.value = op.value.length; return op; }));
// console.error(e)
// console.log('KeyRev. :', revision.meta.atext)
// console.log('Computed:', atext)
callback();
return;
}
}
setImmediate(callback);
});
}, (er) => {
if (pad.atext.text == atext.text) { console.log('ok'); } else {
console.error('Pad AText doesn\'t match computed one! (Computed ', atext.text.length, ', db', pad.atext.text.length, ')');
console.log(diff.diffChars(atext.text, pad.atext.text).map((op) => { if (!op.added && !op.removed) op.value = op.value.length; return op; }));
// check things are working properly
if (~keyRevisions.indexOf(revNum)) {
try {
expect(revision.meta.atext.text).to.eql(atext.text);
expect(revision.meta.atext.attribs).to.eql(atext.attribs);
} catch (e) {
console.error(`Atext in key revision ${revNum} doesn't match computed one.`);
console.log(diff.diffChars(atext.text, revision.meta.atext.text).map((op) => {
if (!op.added && !op.removed) op.value = op.value.length;
return op;
}));
// console.error(e)
// console.log('KeyRev. :', revision.meta.atext)
// console.log('Computed:', atext)
continue;
}
callback(er);
});
}
}
process.exit(0);
} catch (e) {
console.trace(e);
process.exit(1);
// check final text is right...
if (pad.atext.text === atext.text) {
console.log('ok');
} else {
console.error('Pad AText doesn\'t match computed one! (Computed ',
atext.text.length, ', db', pad.atext.text.length, ')');
console.log(diff.diffChars(atext.text, pad.atext.text).map((op) => {
if (!op.added && !op.removed) {
op.value = op.value.length;
return op;
}
}));
}
});