summaryrefslogtreecommitdiff
path: root/lib/workers
diff options
context:
space:
mode:
Diffstat (limited to 'lib/workers')
-rw-r--r--lib/workers/dmpWorker.js142
-rw-r--r--lib/workers/historyUpdater.js66
-rw-r--r--lib/workers/noteRevisionSaver.js19
-rw-r--r--lib/workers/noteUpdater.js101
4 files changed, 142 insertions, 186 deletions
diff --git a/lib/workers/dmpWorker.js b/lib/workers/dmpWorker.js
new file mode 100644
index 00000000..fae36191
--- /dev/null
+++ b/lib/workers/dmpWorker.js
@@ -0,0 +1,142 @@
+// external modules
+var LZString = require('lz-string');
+var DiffMatchPatch = require('diff-match-patch');
+var dmp = new DiffMatchPatch();
+
+// core
+var config = require("../config.js");
+var logger = require("../logger.js");
+
+process.on('message', function(data) {
+ if (!data || !data.msg || !data.cacheKey) {
+ return logger.error('dmp worker error: not enough data');
+ }
+ switch (data.msg) {
+ case 'create patch':
+ if (!data.hasOwnProperty('lastDoc') || !data.hasOwnProperty('currDoc')) {
+ return logger.error('dmp worker error: not enough data on create patch');
+ }
+ try {
+ var patch = createPatch(data.lastDoc, data.currDoc);
+ process.send({
+ msg: 'check',
+ result: patch,
+ cacheKey: data.cacheKey
+ });
+ } catch (err) {
+ logger.error('dmp worker error', err);
+ process.send({
+ msg: 'error',
+ error: err,
+ cacheKey: data.cacheKey
+ });
+ }
+ break;
+ case 'get revision':
+ if (!data.hasOwnProperty('revisions') || !data.hasOwnProperty('count')) {
+ return logger.error('dmp worker error: not enough data on get revision');
+ }
+ try {
+ var result = getRevision(data.revisions, data.count);
+ process.send({
+ msg: 'check',
+ result: result,
+ cacheKey: data.cacheKey
+ });
+ } catch (err) {
+ logger.error('dmp worker error', err);
+ process.send({
+ msg: 'error',
+ error: err,
+ cacheKey: data.cacheKey
+ });
+ }
+ break;
+ }
+});
+
+function createPatch(lastDoc, currDoc) {
+ var ms_start = (new Date()).getTime();
+ var diff = dmp.diff_main(lastDoc, currDoc);
+ dmp.diff_cleanupSemantic(diff);
+ var patch = dmp.patch_make(lastDoc, diff);
+ patch = dmp.patch_toText(patch);
+ var ms_end = (new Date()).getTime();
+ if (config.debug) {
+ logger.info(patch);
+ logger.info((ms_end - ms_start) + 'ms');
+ }
+ return patch;
+}
+
+function getRevision(revisions, count) {
+ var ms_start = (new Date()).getTime();
+ var startContent = null;
+ var lastPatch = [];
+ var applyPatches = [];
+ var authorship = [];
+ if (count <= Math.round(revisions.length / 2)) {
+ // start from top to target
+ for (var i = 0; i < count; i++) {
+ var revision = revisions[i];
+ if (i == 0) {
+ startContent = LZString.decompressFromBase64(revision.content || revision.lastContent);
+ }
+ if (i != count - 1) {
+ var patch = dmp.patch_fromText(LZString.decompressFromBase64(revision.patch));
+ applyPatches = applyPatches.concat(patch);
+ }
+ lastPatch = revision.patch;
+ authorship = revision.authorship;
+ }
+ // swap DIFF_INSERT and DIFF_DELETE to achieve unpatching
+ for (var i = 0, l = applyPatches.length; i < l; i++) {
+ for (var j = 0, m = applyPatches[i].diffs.length; j < m; j++) {
+ var diff = applyPatches[i].diffs[j];
+ if (diff[0] == DiffMatchPatch.DIFF_INSERT)
+ diff[0] = DiffMatchPatch.DIFF_DELETE;
+ else if (diff[0] == DiffMatchPatch.DIFF_DELETE)
+ diff[0] = DiffMatchPatch.DIFF_INSERT;
+ }
+ }
+ } else {
+ // start from bottom to target
+ var l = revisions.length - 1;
+ for (var i = l; i >= count - 1; i--) {
+ var revision = revisions[i];
+ if (i == l) {
+ startContent = LZString.decompressFromBase64(revision.lastContent);
+ authorship = revision.authorship;
+ }
+ if (revision.patch) {
+ var patch = dmp.patch_fromText(LZString.decompressFromBase64(revision.patch));
+ applyPatches = applyPatches.concat(patch);
+ }
+ lastPatch = revision.patch;
+ authorship = revision.authorship;
+ }
+ }
+ try {
+ var finalContent = dmp.patch_apply(applyPatches, startContent)[0];
+ } catch (err) {
+ throw new Error(err);
+ }
+ var data = {
+ content: finalContent,
+ patch: dmp.patch_fromText(LZString.decompressFromBase64(lastPatch)),
+ authorship: authorship ? JSON.parse(LZString.decompressFromBase64(authorship)) : null
+ };
+ var ms_end = (new Date()).getTime();
+ if (config.debug) {
+ logger.info((ms_end - ms_start) + 'ms');
+ }
+ return data;
+}
+
+// log uncaught exception
+process.on('uncaughtException', function (err) {
+ logger.error('An uncaught exception has occured.');
+ logger.error(err);
+ logger.error('Process will exit now.');
+ process.exit(1);
+}); \ No newline at end of file
diff --git a/lib/workers/historyUpdater.js b/lib/workers/historyUpdater.js
deleted file mode 100644
index df80e92d..00000000
--- a/lib/workers/historyUpdater.js
+++ /dev/null
@@ -1,66 +0,0 @@
-// external modules
-var async = require('async');
-
-// core
-var config = require("../config.js");
-var logger = require("../logger.js");
-var models = require("../models");
-
-process.on('message', function (data) {
- if (!data || !data.msg || data.msg !== 'update history' || !data.caches) return process.exit();
- var caches = data.caches;
- async.each(Object.keys(caches), function (key, callback) {
- var cache = caches[key];
- if (config.debug) logger.info("history updater found dirty history: " + key);
- var history = parseHistoryToArray(cache);
- finishUpdateHistory(key, history, function (err, count) {
- if (err) return callback(err, null);
- if (!count) return callback(null, null);
- process.send({
- msg: 'check',
- userid: key
- });
- return callback(null, null);
- });
- }, function (err) {
- if (err) logger.error('history updater error', err);
- process.exit();
- });
-});
-
-function finishUpdateHistory(userid, history, callback) {
- models.User.update({
- history: JSON.stringify(history)
- }, {
- where: {
- id: userid
- }
- }).then(function (count) {
- return callback(null, count);
- }).catch(function (err) {
- return callback(err, null);
- });
-}
-
-function parseHistoryToArray(history) {
- var _history = [];
- Object.keys(history).forEach(function (key) {
- var item = history[key];
- _history.push(item);
- });
- return _history;
-}
-
-function parseHistoryToObject(history) {
- var _history = {};
- for (var i = 0, l = history.length; i < l; i++) {
- var item = history[i];
- _history[item.id] = item;
- }
- return _history;
-}
-
-module.exports = {
- parseHistoryToArray: parseHistoryToArray,
- parseHistoryToObject: parseHistoryToObject
-}; \ No newline at end of file
diff --git a/lib/workers/noteRevisionSaver.js b/lib/workers/noteRevisionSaver.js
deleted file mode 100644
index b6b117a3..00000000
--- a/lib/workers/noteRevisionSaver.js
+++ /dev/null
@@ -1,19 +0,0 @@
-// core
-var logger = require("../logger.js");
-var models = require("../models");
-
-process.on('message', function (data) {
- if (!data || !data.msg || data.msg !== 'save note revision') return process.exit();
- models.Revision.saveAllNotesRevision(function (err, notes) {
- if (err) {
- logger.error('note revision saver failed: ' + err);
- return process.exit();
- }
- if (notes && notes.length <= 0) {
- process.send({
- msg: 'empty'
- });
- }
- process.exit();
- });
-}); \ No newline at end of file
diff --git a/lib/workers/noteUpdater.js b/lib/workers/noteUpdater.js
deleted file mode 100644
index 3fc4b1eb..00000000
--- a/lib/workers/noteUpdater.js
+++ /dev/null
@@ -1,101 +0,0 @@
-// external modules
-var async = require('async');
-var moment = require('moment');
-var LZString = require('lz-string');
-
-// core
-var config = require("../config.js");
-var logger = require("../logger.js");
-var models = require("../models");
-
-process.on('message', function (data) {
- if (!data || !data.msg || data.msg !== 'update note' || !data.notes) return process.exit();
- var notes = data.notes;
- async.each(Object.keys(notes), function (key, callback) {
- var note = notes[key];
- if (config.debug) logger.info("note updater found dirty note: " + key);
- updateNote(note, function(err, _note) {
- if (!_note) {
- process.send({
- msg: 'note not found',
- note: note
- });
- logger.error('note not found: ', note.id);
- }
- if (err || !_note) {
- process.send({
- msg: 'error',
- note: note
- });
- return callback(err, null);
- }
- note.updatetime = moment(_note.lastchangeAt).valueOf();
- process.send({
- msg: 'check',
- note: note
- });
- return callback(null, null);
- });
- }, function (err) {
- if (err) logger.error('note updater error', err);
- process.exit();
- });
-});
-
-function updateNote(note, callback) {
- models.Note.findOne({
- where: {
- id: note.id
- }
- }).then(function (_note) {
- if (!_note) return callback(null, null);
- if (note.lastchangeuser) {
- if (_note.lastchangeuserId != note.lastchangeuser) {
- models.User.findOne({
- where: {
- id: note.lastchangeuser
- }
- }).then(function (user) {
- if (!user) return callback(null, null);
- note.lastchangeuserprofile = models.User.parseProfile(user.profile);
- return finishUpdateNote(note, _note, callback);
- }).catch(function (err) {
- logger.error(err);
- return callback(err, null);
- });
- } else {
- return finishUpdateNote(note, _note, callback);
- }
- } else {
- note.lastchangeuserprofile = null;
- return finishUpdateNote(note, _note, callback);
- }
- }).catch(function (err) {
- logger.error(err);
- return callback(err, null);
- });
-}
-
-function finishUpdateNote(note, _note, callback) {
- var body = note.document;
- var title = note.title = models.Note.parseNoteTitle(body);
- title = LZString.compressToBase64(title);
- body = LZString.compressToBase64(body);
- var values = {
- title: title,
- content: body,
- authorship: LZString.compressToBase64(JSON.stringify(note.authorship)),
- lastchangeuserId: note.lastchangeuser,
- lastchangeAt: Date.now()
- };
- _note.update(values).then(function (_note) {
- return callback(null, _note);
- }).catch(function (err) {
- logger.error(err);
- return callback(err, null);
- });
-}
-
-module.exports = {
- updateNote: updateNote
-}; \ No newline at end of file