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.

126 lines
4.3 KiB

4 years ago
  1. /*
  2. This is a repair tool. It rebuilds an old pad at a new pad location up to a
  3. known "good" revision.
  4. */
  5. if (process.argv.length != 4 && process.argv.length != 5) {
  6. console.error('Use: node bin/repairPad.js $PADID $REV [$NEWPADID]');
  7. process.exit(1);
  8. }
  9. const npm = require('../src/node_modules/npm');
  10. const async = require('../src/node_modules/async');
  11. const ueberDB = require('../src/node_modules/ueberdb2');
  12. const padId = process.argv[2];
  13. const newRevHead = process.argv[3];
  14. const newPadId = process.argv[4] || `${padId}-rebuilt`;
  15. let db, oldPad, newPad, settings;
  16. let AuthorManager, ChangeSet, Pad, PadManager;
  17. async.series([
  18. function (callback) {
  19. npm.load({}, (err) => {
  20. if (err) {
  21. console.error(`Could not load NPM: ${err}`);
  22. process.exit(1);
  23. } else {
  24. callback();
  25. }
  26. });
  27. },
  28. function (callback) {
  29. // Get a handle into the database
  30. db = require('../src/node/db/DB');
  31. db.init(callback);
  32. },
  33. function (callback) {
  34. PadManager = require('../src/node/db/PadManager');
  35. Pad = require('../src/node/db/Pad').Pad;
  36. // Get references to the original pad and to a newly created pad
  37. // HACK: This is a standalone script, so we want to write everything
  38. // out to the database immediately. The only problem with this is
  39. // that a driver (like the mysql driver) can hardcode these values.
  40. db.db.db.settings = {cache: 0, writeInterval: 0, json: true};
  41. // Validate the newPadId if specified and that a pad with that ID does
  42. // not already exist to avoid overwriting it.
  43. if (!PadManager.isValidPadId(newPadId)) {
  44. console.error('Cannot create a pad with that id as it is invalid');
  45. process.exit(1);
  46. }
  47. PadManager.doesPadExists(newPadId, (err, exists) => {
  48. if (exists) {
  49. console.error('Cannot create a pad with that id as it already exists');
  50. process.exit(1);
  51. }
  52. });
  53. PadManager.getPad(padId, (err, pad) => {
  54. oldPad = pad;
  55. newPad = new Pad(newPadId);
  56. callback();
  57. });
  58. },
  59. function (callback) {
  60. // Clone all Chat revisions
  61. const chatHead = oldPad.chatHead;
  62. for (var i = 0, curHeadNum = 0; i <= chatHead; i++) {
  63. db.db.get(`pad:${padId}:chat:${i}`, (err, chat) => {
  64. db.db.set(`pad:${newPadId}:chat:${curHeadNum++}`, chat);
  65. console.log(`Created: Chat Revision: pad:${newPadId}:chat:${curHeadNum}`);
  66. });
  67. }
  68. callback();
  69. },
  70. function (callback) {
  71. // Rebuild Pad from revisions up to and including the new revision head
  72. AuthorManager = require('../src/node/db/AuthorManager');
  73. Changeset = require('ep_etherpad-lite/static/js/Changeset');
  74. // Author attributes are derived from changesets, but there can also be
  75. // non-author attributes with specific mappings that changesets depend on
  76. // and, AFAICT, cannot be recreated any other way
  77. newPad.pool.numToAttrib = oldPad.pool.numToAttrib;
  78. for (let curRevNum = 0; curRevNum <= newRevHead; curRevNum++) {
  79. db.db.get(`pad:${padId}:revs:${curRevNum}`, (err, rev) => {
  80. if (rev.meta) {
  81. throw 'The specified revision number could not be found.';
  82. }
  83. const newRevNum = ++newPad.head;
  84. const newRevId = `pad:${newPad.id}:revs:${newRevNum}`;
  85. db.db.set(newRevId, rev);
  86. AuthorManager.addPad(rev.meta.author, newPad.id);
  87. newPad.atext = Changeset.applyToAText(rev.changeset, newPad.atext, newPad.pool);
  88. console.log(`Created: Revision: pad:${newPad.id}:revs:${newRevNum}`);
  89. if (newRevNum == newRevHead) {
  90. callback();
  91. }
  92. });
  93. }
  94. },
  95. function (callback) {
  96. // Add saved revisions up to the new revision head
  97. console.log(newPad.head);
  98. const newSavedRevisions = [];
  99. for (const i in oldPad.savedRevisions) {
  100. savedRev = oldPad.savedRevisions[i];
  101. if (savedRev.revNum <= newRevHead) {
  102. newSavedRevisions.push(savedRev);
  103. console.log(`Added: Saved Revision: ${savedRev.revNum}`);
  104. }
  105. }
  106. newPad.savedRevisions = newSavedRevisions;
  107. callback();
  108. },
  109. function (callback) {
  110. // Save the source pad
  111. db.db.set(`pad:${newPadId}`, newPad, (err) => {
  112. console.log(`Created: Source Pad: pad:${newPadId}`);
  113. newPad.saveToDatabase().then(() => callback(), callback);
  114. });
  115. },
  116. ], (err) => {
  117. if (err) { throw err; } else {
  118. console.info('finished');
  119. process.exit(0);
  120. }
  121. });