summaryrefslogtreecommitdiff
path: root/lib/models/revision.js
diff options
context:
space:
mode:
Diffstat (limited to 'lib/models/revision.js')
-rw-r--r--lib/models/revision.js580
1 files changed, 290 insertions, 290 deletions
diff --git a/lib/models/revision.js b/lib/models/revision.js
index c7360fed..d8dab30a 100644
--- a/lib/models/revision.js
+++ b/lib/models/revision.js
@@ -1,306 +1,306 @@
-"use strict";
-
// external modules
-var Sequelize = require("sequelize");
-var async = require('async');
-var moment = require('moment');
-var childProcess = require('child_process');
-var shortId = require('shortid');
+var Sequelize = require('sequelize')
+var async = require('async')
+var moment = require('moment')
+var childProcess = require('child_process')
+var shortId = require('shortid')
// core
-var config = require("../config.js");
-var logger = require("../logger.js");
+var config = require('../config.js')
+var logger = require('../logger.js')
-var dmpWorker = createDmpWorker();
-var dmpCallbackCache = {};
+var dmpWorker = createDmpWorker()
+var dmpCallbackCache = {}
-function createDmpWorker() {
- var worker = childProcess.fork("./lib/workers/dmpWorker.js", {
- stdio: 'ignore'
- });
- if (config.debug) logger.info('dmp worker process started');
- worker.on('message', function (data) {
- if (!data || !data.msg || !data.cacheKey) {
- return logger.error('dmp worker error: not enough data on message');
- }
- var cacheKey = data.cacheKey;
- switch(data.msg) {
- case 'error':
- dmpCallbackCache[cacheKey](data.error, null);
- break;
- case 'check':
- dmpCallbackCache[cacheKey](null, data.result);
- break;
- }
- delete dmpCallbackCache[cacheKey];
- });
- worker.on('close', function (code) {
- dmpWorker = null;
- if (config.debug) logger.info('dmp worker process exited with code ' + code);
- });
- return worker;
+function createDmpWorker () {
+ var worker = childProcess.fork('./lib/workers/dmpWorker.js', {
+ stdio: 'ignore'
+ })
+ if (config.debug) logger.info('dmp worker process started')
+ worker.on('message', function (data) {
+ if (!data || !data.msg || !data.cacheKey) {
+ return logger.error('dmp worker error: not enough data on message')
+ }
+ var cacheKey = data.cacheKey
+ switch (data.msg) {
+ case 'error':
+ dmpCallbackCache[cacheKey](data.error, null)
+ break
+ case 'check':
+ dmpCallbackCache[cacheKey](null, data.result)
+ break
+ }
+ delete dmpCallbackCache[cacheKey]
+ })
+ worker.on('close', function (code) {
+ dmpWorker = null
+ if (config.debug) logger.info('dmp worker process exited with code ' + code)
+ })
+ return worker
}
-function sendDmpWorker(data, callback) {
- if (!dmpWorker) dmpWorker = createDmpWorker();
- var cacheKey = Date.now() + '_' + shortId.generate();
- dmpCallbackCache[cacheKey] = callback;
- data = Object.assign(data, {
- cacheKey: cacheKey
- });
- dmpWorker.send(data);
+function sendDmpWorker (data, callback) {
+ if (!dmpWorker) dmpWorker = createDmpWorker()
+ var cacheKey = Date.now() + '_' + shortId.generate()
+ dmpCallbackCache[cacheKey] = callback
+ data = Object.assign(data, {
+ cacheKey: cacheKey
+ })
+ dmpWorker.send(data)
}
module.exports = function (sequelize, DataTypes) {
- var Revision = sequelize.define("Revision", {
- id: {
- type: DataTypes.UUID,
- primaryKey: true,
- defaultValue: Sequelize.UUIDV4
- },
- patch: {
- type: DataTypes.TEXT,
- get: function () {
- return sequelize.processData(this.getDataValue('patch'), "");
- },
- set: function (value) {
- this.setDataValue('patch', sequelize.stripNullByte(value));
- }
- },
- lastContent: {
- type: DataTypes.TEXT,
- get: function () {
- return sequelize.processData(this.getDataValue('lastContent'), "");
+ var Revision = sequelize.define('Revision', {
+ id: {
+ type: DataTypes.UUID,
+ primaryKey: true,
+ defaultValue: Sequelize.UUIDV4
+ },
+ patch: {
+ type: DataTypes.TEXT,
+ get: function () {
+ return sequelize.processData(this.getDataValue('patch'), '')
+ },
+ set: function (value) {
+ this.setDataValue('patch', sequelize.stripNullByte(value))
+ }
+ },
+ lastContent: {
+ type: DataTypes.TEXT,
+ get: function () {
+ return sequelize.processData(this.getDataValue('lastContent'), '')
+ },
+ set: function (value) {
+ this.setDataValue('lastContent', sequelize.stripNullByte(value))
+ }
+ },
+ content: {
+ type: DataTypes.TEXT,
+ get: function () {
+ return sequelize.processData(this.getDataValue('content'), '')
+ },
+ set: function (value) {
+ this.setDataValue('content', sequelize.stripNullByte(value))
+ }
+ },
+ length: {
+ type: DataTypes.INTEGER
+ },
+ authorship: {
+ type: DataTypes.TEXT,
+ get: function () {
+ return sequelize.processData(this.getDataValue('authorship'), [], JSON.parse)
+ },
+ set: function (value) {
+ this.setDataValue('authorship', value ? JSON.stringify(value) : value)
+ }
+ }
+ }, {
+ classMethods: {
+ associate: function (models) {
+ Revision.belongsTo(models.Note, {
+ foreignKey: 'noteId',
+ as: 'note',
+ constraints: false
+ })
+ },
+ getNoteRevisions: function (note, callback) {
+ Revision.findAll({
+ where: {
+ noteId: note.id
+ },
+ order: '"createdAt" DESC'
+ }).then(function (revisions) {
+ var data = []
+ for (var i = 0, l = revisions.length; i < l; i++) {
+ var revision = revisions[i]
+ data.push({
+ time: moment(revision.createdAt).valueOf(),
+ length: revision.length
+ })
+ }
+ callback(null, data)
+ }).catch(function (err) {
+ callback(err, null)
+ })
+ },
+ getPatchedNoteRevisionByTime: function (note, time, callback) {
+ // find all revisions to prepare for all possible calculation
+ Revision.findAll({
+ where: {
+ noteId: note.id
+ },
+ order: '"createdAt" DESC'
+ }).then(function (revisions) {
+ if (revisions.length <= 0) return callback(null, null)
+ // measure target revision position
+ Revision.count({
+ where: {
+ noteId: note.id,
+ createdAt: {
+ $gte: time
+ }
},
- set: function (value) {
- this.setDataValue('lastContent', sequelize.stripNullByte(value));
+ order: '"createdAt" DESC'
+ }).then(function (count) {
+ if (count <= 0) return callback(null, null)
+ sendDmpWorker({
+ msg: 'get revision',
+ revisions: revisions,
+ count: count
+ }, callback)
+ }).catch(function (err) {
+ return callback(err, null)
+ })
+ }).catch(function (err) {
+ return callback(err, null)
+ })
+ },
+ checkAllNotesRevision: function (callback) {
+ Revision.saveAllNotesRevision(function (err, notes) {
+ if (err) return callback(err, null)
+ if (!notes || notes.length <= 0) {
+ return callback(null, notes)
+ } else {
+ Revision.checkAllNotesRevision(callback)
+ }
+ })
+ },
+ saveAllNotesRevision: function (callback) {
+ sequelize.models.Note.findAll({
+ // query all notes that need to save for revision
+ where: {
+ $and: [
+ {
+ lastchangeAt: {
+ $or: {
+ $eq: null,
+ $and: {
+ $ne: null,
+ $gt: sequelize.col('createdAt')
+ }
+ }
+ }
+ },
+ {
+ savedAt: {
+ $or: {
+ $eq: null,
+ $lt: sequelize.col('lastchangeAt')
+ }
+ }
+ }
+ ]
+ }
+ }).then(function (notes) {
+ if (notes.length <= 0) return callback(null, notes)
+ var savedNotes = []
+ async.each(notes, function (note, _callback) {
+ // revision saving policy: note not been modified for 5 mins or not save for 10 mins
+ if (note.lastchangeAt && note.savedAt) {
+ var lastchangeAt = moment(note.lastchangeAt)
+ var savedAt = moment(note.savedAt)
+ if (moment().isAfter(lastchangeAt.add(5, 'minutes'))) {
+ savedNotes.push(note)
+ Revision.saveNoteRevision(note, _callback)
+ } else if (lastchangeAt.isAfter(savedAt.add(10, 'minutes'))) {
+ savedNotes.push(note)
+ Revision.saveNoteRevision(note, _callback)
+ } else {
+ return _callback(null, null)
+ }
+ } else {
+ savedNotes.push(note)
+ Revision.saveNoteRevision(note, _callback)
}
- },
- content: {
- type: DataTypes.TEXT,
- get: function () {
- return sequelize.processData(this.getDataValue('content'), "");
- },
- set: function (value) {
- this.setDataValue('content', sequelize.stripNullByte(value));
+ }, function (err) {
+ if (err) {
+ return callback(err, null)
}
- },
- length: {
- type: DataTypes.INTEGER
- },
- authorship: {
- type: DataTypes.TEXT,
- get: function () {
- return sequelize.processData(this.getDataValue('authorship'), [], JSON.parse);
- },
- set: function (value) {
- this.setDataValue('authorship', value ? JSON.stringify(value) : value);
- }
- }
- }, {
- classMethods: {
- associate: function (models) {
- Revision.belongsTo(models.Note, {
- foreignKey: "noteId",
- as: "note",
- constraints: false
- });
- },
- getNoteRevisions: function (note, callback) {
- Revision.findAll({
- where: {
- noteId: note.id
- },
- order: '"createdAt" DESC'
- }).then(function (revisions) {
- var data = [];
- for (var i = 0, l = revisions.length; i < l; i++) {
- var revision = revisions[i];
- data.push({
- time: moment(revision.createdAt).valueOf(),
- length: revision.length
- });
- }
- callback(null, data);
- }).catch(function (err) {
- callback(err, null);
- });
- },
- getPatchedNoteRevisionByTime: function (note, time, callback) {
- // find all revisions to prepare for all possible calculation
- Revision.findAll({
- where: {
- noteId: note.id
- },
- order: '"createdAt" DESC'
- }).then(function (revisions) {
- if (revisions.length <= 0) return callback(null, null);
- // measure target revision position
- Revision.count({
- where: {
- noteId: note.id,
- createdAt: {
- $gte: time
- }
- },
- order: '"createdAt" DESC'
- }).then(function (count) {
- if (count <= 0) return callback(null, null);
- sendDmpWorker({
- msg: 'get revision',
- revisions: revisions,
- count: count
- }, callback);
- }).catch(function (err) {
- return callback(err, null);
- });
+ // return null when no notes need saving at this moment but have delayed tasks to be done
+ var result = ((savedNotes.length === 0) && (notes.length > savedNotes.length)) ? null : savedNotes
+ return callback(null, result)
+ })
+ }).catch(function (err) {
+ return callback(err, null)
+ })
+ },
+ saveNoteRevision: function (note, callback) {
+ Revision.findAll({
+ where: {
+ noteId: note.id
+ },
+ order: '"createdAt" DESC'
+ }).then(function (revisions) {
+ if (revisions.length <= 0) {
+ // if no revision available
+ Revision.create({
+ noteId: note.id,
+ lastContent: note.content,
+ length: note.content.length,
+ authorship: note.authorship
+ }).then(function (revision) {
+ Revision.finishSaveNoteRevision(note, revision, callback)
+ }).catch(function (err) {
+ return callback(err, null)
+ })
+ } else {
+ var latestRevision = revisions[0]
+ var lastContent = latestRevision.content || latestRevision.lastContent
+ var content = note.content
+ sendDmpWorker({
+ msg: 'create patch',
+ lastDoc: lastContent,
+ currDoc: content
+ }, function (err, patch) {
+ if (err) logger.error('save note revision error', err)
+ if (!patch) {
+ // if patch is empty (means no difference) then just update the latest revision updated time
+ latestRevision.changed('updatedAt', true)
+ latestRevision.update({
+ updatedAt: Date.now()
+ }).then(function (revision) {
+ Revision.finishSaveNoteRevision(note, revision, callback)
}).catch(function (err) {
- return callback(err, null);
- });
- },
- checkAllNotesRevision: function (callback) {
- Revision.saveAllNotesRevision(function (err, notes) {
- if (err) return callback(err, null);
- if (!notes || notes.length <= 0) {
- return callback(null, notes);
- } else {
- Revision.checkAllNotesRevision(callback);
- }
- });
- },
- saveAllNotesRevision: function (callback) {
- sequelize.models.Note.findAll({
- // query all notes that need to save for revision
- where: {
- $and: [
- {
- lastchangeAt: {
- $or: {
- $eq: null,
- $and: {
- $ne: null,
- $gt: sequelize.col('createdAt')
- }
- }
- }
- },
- {
- savedAt: {
- $or: {
- $eq: null,
- $lt: sequelize.col('lastchangeAt')
- }
- }
- }
- ]
- }
- }).then(function (notes) {
- if (notes.length <= 0) return callback(null, notes);
- var savedNotes = [];
- async.each(notes, function (note, _callback) {
- // revision saving policy: note not been modified for 5 mins or not save for 10 mins
- if (note.lastchangeAt && note.savedAt) {
- var lastchangeAt = moment(note.lastchangeAt);
- var savedAt = moment(note.savedAt);
- if (moment().isAfter(lastchangeAt.add(5, 'minutes'))) {
- savedNotes.push(note);
- Revision.saveNoteRevision(note, _callback);
- } else if (lastchangeAt.isAfter(savedAt.add(10, 'minutes'))) {
- savedNotes.push(note);
- Revision.saveNoteRevision(note, _callback);
- } else {
- return _callback(null, null);
- }
- } else {
- savedNotes.push(note);
- Revision.saveNoteRevision(note, _callback);
- }
- }, function (err) {
- if (err) return callback(err, null);
- // return null when no notes need saving at this moment but have delayed tasks to be done
- var result = ((savedNotes.length == 0) && (notes.length > savedNotes.length)) ? null : savedNotes;
- return callback(null, result);
- });
+ return callback(err, null)
+ })
+ } else {
+ Revision.create({
+ noteId: note.id,
+ patch: patch,
+ content: note.content,
+ length: note.content.length,
+ authorship: note.authorship
+ }).then(function (revision) {
+ // clear last revision content to reduce db size
+ latestRevision.update({
+ content: null
+ }).then(function () {
+ Revision.finishSaveNoteRevision(note, revision, callback)
+ }).catch(function (err) {
+ return callback(err, null)
+ })
}).catch(function (err) {
- return callback(err, null);
- });
- },
- saveNoteRevision: function (note, callback) {
- Revision.findAll({
- where: {
- noteId: note.id
- },
- order: '"createdAt" DESC'
- }).then(function (revisions) {
- if (revisions.length <= 0) {
- // if no revision available
- Revision.create({
- noteId: note.id,
- lastContent: note.content,
- length: note.content.length,
- authorship: note.authorship
- }).then(function (revision) {
- Revision.finishSaveNoteRevision(note, revision, callback);
- }).catch(function (err) {
- return callback(err, null);
- });
- } else {
- var latestRevision = revisions[0];
- var lastContent = latestRevision.content || latestRevision.lastContent;
- var content = note.content;
- sendDmpWorker({
- msg: 'create patch',
- lastDoc: lastContent,
- currDoc: content,
- }, function (err, patch) {
- if (err) logger.error('save note revision error', err);
- if (!patch) {
- // if patch is empty (means no difference) then just update the latest revision updated time
- latestRevision.changed('updatedAt', true);
- latestRevision.update({
- updatedAt: Date.now()
- }).then(function (revision) {
- Revision.finishSaveNoteRevision(note, revision, callback);
- }).catch(function (err) {
- return callback(err, null);
- });
- } else {
- Revision.create({
- noteId: note.id,
- patch: patch,
- content: note.content,
- length: note.content.length,
- authorship: note.authorship
- }).then(function (revision) {
- // clear last revision content to reduce db size
- latestRevision.update({
- content: null
- }).then(function () {
- Revision.finishSaveNoteRevision(note, revision, callback);
- }).catch(function (err) {
- return callback(err, null);
- });
- }).catch(function (err) {
- return callback(err, null);
- });
- }
- });
- }
- }).catch(function (err) {
- return callback(err, null);
- });
- },
- finishSaveNoteRevision: function (note, revision, callback) {
- note.update({
- savedAt: revision.updatedAt
- }).then(function () {
- return callback(null, revision);
- }).catch(function (err) {
- return callback(err, null);
- });
- }
- }
- });
+ return callback(err, null)
+ })
+ }
+ })
+ }
+ }).catch(function (err) {
+ return callback(err, null)
+ })
+ },
+ finishSaveNoteRevision: function (note, revision, callback) {
+ note.update({
+ savedAt: revision.updatedAt
+ }).then(function () {
+ return callback(null, revision)
+ }).catch(function (err) {
+ return callback(err, null)
+ })
+ }
+ }
+ })
- return Revision;
-}; \ No newline at end of file
+ return Revision
+}