You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

111 lines
3.5 KiB

4 years ago
  1. /*
  2. * This is a debug tool. It checks all revisions for data corruption
  3. */
  4. if (process.argv.length != 3) {
  5. console.error('Use: node bin/checkPadDeltas.js $PADID');
  6. process.exit(1);
  7. }
  8. // get the padID
  9. const padId = process.argv[2];
  10. // load and initialize NPM;
  11. const expect = require('expect.js');
  12. const diff = require('diff');
  13. var async = require('async');
  14. const npm = require('../src/node_modules/npm');
  15. var async = require('ep_etherpad-lite/node_modules/async');
  16. const Changeset = require('ep_etherpad-lite/static/js/Changeset');
  17. npm.load({}, async () => {
  18. try {
  19. // initialize database
  20. const settings = require('../src/node/utils/Settings');
  21. const db = require('../src/node/db/DB');
  22. await db.init();
  23. // load modules
  24. const Changeset = require('ep_etherpad-lite/static/js/Changeset');
  25. const padManager = require('../src/node/db/PadManager');
  26. const exists = await padManager.doesPadExists(padId);
  27. if (!exists) {
  28. console.error('Pad does not exist');
  29. process.exit(1);
  30. }
  31. // get the pad
  32. const pad = await padManager.getPad(padId);
  33. // create an array with key revisions
  34. // key revisions always save the full pad atext
  35. const head = pad.getHeadRevisionNumber();
  36. const keyRevisions = [];
  37. for (var i = 0; i < head; i += 100) {
  38. keyRevisions.push(i);
  39. }
  40. // create an array with all revisions
  41. const revisions = [];
  42. for (var i = 0; i <= head; i++) {
  43. revisions.push(i);
  44. }
  45. let atext = Changeset.makeAText('\n');
  46. // run trough all revisions
  47. async.forEachSeries(revisions, (revNum, callback) => {
  48. // console.log('Fetching', revNum)
  49. db.db.get(`pad:${padId}:revs:${revNum}`, (err, revision) => {
  50. if (err) return callback(err);
  51. // check if there is a atext in the keyRevisions
  52. if (~keyRevisions.indexOf(revNum) && (revision === undefined || revision.meta === undefined || revision.meta.atext === undefined)) {
  53. console.error(`No atext in key revision ${revNum}`);
  54. callback();
  55. return;
  56. }
  57. try {
  58. // console.log("check revision ", revNum);
  59. const cs = revision.changeset;
  60. atext = Changeset.applyToAText(cs, atext, pad.pool);
  61. } catch (e) {
  62. console.error(`Bad changeset at revision ${revNum} - ${e.message}`);
  63. callback();
  64. return;
  65. }
  66. if (~keyRevisions.indexOf(revNum)) {
  67. try {
  68. expect(revision.meta.atext.text).to.eql(atext.text);
  69. expect(revision.meta.atext.attribs).to.eql(atext.attribs);
  70. } catch (e) {
  71. console.error(`Atext in key revision ${revNum} doesn't match computed one.`);
  72. console.log(diff.diffChars(atext.text, revision.meta.atext.text).map((op) => { if (!op.added && !op.removed) op.value = op.value.length; return op; }));
  73. // console.error(e)
  74. // console.log('KeyRev. :', revision.meta.atext)
  75. // console.log('Computed:', atext)
  76. callback();
  77. return;
  78. }
  79. }
  80. setImmediate(callback);
  81. });
  82. }, (er) => {
  83. if (pad.atext.text == atext.text) { console.log('ok'); } else {
  84. console.error('Pad AText doesn\'t match computed one! (Computed ', atext.text.length, ', db', pad.atext.text.length, ')');
  85. console.log(diff.diffChars(atext.text, pad.atext.text).map((op) => { if (!op.added && !op.removed) op.value = op.value.length; return op; }));
  86. }
  87. callback(er);
  88. });
  89. process.exit(0);
  90. } catch (e) {
  91. console.trace(e);
  92. process.exit(1);
  93. }
  94. });