summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app.js56
-rw-r--r--lib/history.js228
2 files changed, 233 insertions, 51 deletions
diff --git a/app.js b/app.js
index b3473a13..86df1010 100644
--- a/app.js
+++ b/app.js
@@ -22,6 +22,7 @@ var i18n = require('i18n');
var config = require("./lib/config.js");
var logger = require("./lib/logger.js");
var auth = require("./lib/auth.js");
+var history = require("./lib/history.js");
var response = require("./lib/response.js");
var models = require("./lib/models");
@@ -365,56 +366,9 @@ app.get('/logout', function (req, res) {
res.redirect(config.serverurl + '/');
});
//get history
-app.get('/history', function (req, res) {
- if (req.isAuthenticated()) {
- models.User.findOne({
- where: {
- id: req.user.id
- }
- }).then(function (user) {
- if (!user)
- return response.errorNotFound(res);
- var history = [];
- if (user.history)
- history = JSON.parse(user.history);
- res.send({
- history: history
- });
- if (config.debug)
- logger.info('read history success: ' + user.id);
- }).catch(function (err) {
- logger.error('read history failed: ' + err);
- return response.errorInternalError(res);
- });
- } else {
- return response.errorForbidden(res);
- }
-});
+app.get('/history', history.historyGet);
//post history
-app.post('/history', urlencodedParser, function (req, res) {
- if (req.isAuthenticated()) {
- if (config.debug)
- logger.info('SERVER received history from [' + req.user.id + ']: ' + req.body.history);
- models.User.update({
- history: req.body.history
- }, {
- where: {
- id: req.user.id
- }
- }).then(function (count) {
- if (!count)
- return response.errorNotFound(res);
- if (config.debug)
- logger.info("write user history success: " + req.user.id);
- }).catch(function (err) {
- logger.error('write history failed: ' + err);
- return response.errorInternalError(res);
- });
- res.end();
- } else {
- return response.errorForbidden(res);
- }
-});
+app.post('/history', urlencodedParser, history.historyPost);
//get me info
app.get('/me', function (req, res) {
if (req.isAuthenticated()) {
@@ -522,7 +476,7 @@ function startListen() {
// sync db then start listen
models.sequelize.sync().then(function () {
// check if realtime is ready
- if (realtime.isReady()) {
+ if (history.isReady() && realtime.isReady()) {
models.Revision.checkAllNotesRevision(function (err, notes) {
if (err) return new Error(err);
if (!notes || notes.length <= 0) return startListen();
@@ -549,7 +503,7 @@ process.on('SIGINT', function () {
socket.disconnect(true);
});
var checkCleanTimer = setInterval(function () {
- if (realtime.isReady()) {
+ if (history.isReady() && realtime.isReady()) {
models.Revision.checkAllNotesRevision(function (err, notes) {
if (err) return new Error(err);
if (notes.length <= 0) {
diff --git a/lib/history.js b/lib/history.js
new file mode 100644
index 00000000..119b7510
--- /dev/null
+++ b/lib/history.js
@@ -0,0 +1,228 @@
+//history
+//external modules
+var async = require('async');
+var moment = require('moment');
+
+//core
+var config = require("./config.js");
+var logger = require("./logger.js");
+var response = require("./response.js");
+var models = require("./models");
+
+//public
+var History = {
+ historyGet: historyGet,
+ historyPost: historyPost,
+ historyDelete: historyDelete,
+ isReady: isReady,
+ updateHistory: updateHistory
+};
+
+var caches = {};
+//update when the history is dirty
+var updater = setInterval(function () {
+ var deleted = [];
+ async.each(Object.keys(caches), function (key, callback) {
+ var cache = caches[key];
+ if (cache.isDirty) {
+ if (config.debug) logger.info("history updater found dirty history: " + key);
+ var history = parseHistoryToArray(cache.history);
+ finishUpdateHistory(key, history, function (err, count) {
+ if (err) return callback(err, null);
+ if (!count) return callback(null, null);
+ cache.isDirty = false;
+ cache.updateAt = Date.now();
+ return callback(null, null);
+ });
+ } else {
+ if (moment().isAfter(moment(cache.updateAt).add(5, 'minutes'))) {
+ deleted.push(key);
+ }
+ return callback(null, null);
+ }
+ }, function (err) {
+ if (err) return logger.error('history updater error', err);
+ });
+ // delete specified caches
+ for (var i = 0, l = deleted.length; i < l; i++) {
+ caches[deleted[i]].history = {};
+ delete caches[deleted[i]];
+ }
+}, 1000);
+
+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 isReady() {
+ var dirtyCount = 0;
+ async.each(Object.keys(caches), function (key, callback) {
+ if (caches[key].isDirty) dirtyCount++;
+ return callback(null, null);
+ }, function (err) {
+ if (err) return logger.error('history ready check error', err);
+ });
+ return dirtyCount > 0 ? false : true;
+}
+
+function getHistory(userid, callback) {
+ if (caches[userid]) {
+ return callback(null, caches[userid].history);
+ } else {
+ models.User.findOne({
+ where: {
+ id: userid
+ }
+ }).then(function (user) {
+ if (!user)
+ return callback(null, null);
+ var history = [];
+ if (user.history)
+ history = JSON.parse(user.history);
+ if (config.debug)
+ logger.info('read history success: ' + user.id);
+ setHistory(userid, history);
+ return callback(null, history);
+ }).catch(function (err) {
+ logger.error('read history failed: ' + err);
+ return callback(err, null);
+ });
+ }
+}
+
+function setHistory(userid, history) {
+ if (Array.isArray(history)) history = parseHistoryToObject(history);
+ if (!caches[userid]) {
+ caches[userid] = {
+ history: {},
+ isDirty: false,
+ updateAt: Date.now()
+ };
+ }
+ caches[userid].history = history;
+}
+
+function updateHistory(userid, noteId, document) {
+ var t0 = new Date().getTime();
+ if (userid && noteId && document) {
+ getHistory(userid, function (err, history) {
+ if (err || !history) return;
+ if (!caches[userid].history[noteId]) {
+ caches[userid].history[noteId] = {};
+ }
+ var noteHistory = caches[userid].history[noteId];
+ var noteInfo = models.Note.parseNoteInfo(document);
+ noteHistory.id = noteId;
+ noteHistory.text = noteInfo.title;
+ noteHistory.time = moment().format('MMMM Do YYYY, h:mm:ss a');
+ noteHistory.tags = noteInfo.tags;
+ caches[userid].isDirty = true;
+ var t1 = new Date().getTime();
+ console.warn(t1 - t0);
+ });
+ }
+}
+
+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;
+}
+
+function historyGet(req, res) {
+ if (req.isAuthenticated()) {
+ getHistory(req.user.id, function (err, history) {
+ if (err) return response.errorInternalError(res);
+ if (!history) return response.errorNotFound(res);
+ res.send({
+ history: parseHistoryToArray(history)
+ });
+ });
+ } else {
+ return response.errorForbidden(res);
+ }
+}
+
+function historyPost(req, res) {
+ if (req.isAuthenticated()) {
+ var noteId = req.params.noteId;
+ if (!noteId) {
+ if (typeof req.body['history'] === 'undefined') return response.errorBadRequest(res);
+ if (config.debug)
+ logger.info('SERVER received history from [' + req.user.id + ']: ' + req.body.history);
+ try {
+ var history = JSON.parse(req.body.history);
+ } catch (err) {
+ return response.errorBadRequest(res);
+ }
+ if (Array.isArray(history)) {
+ setHistory(req.user.id, history);
+ caches[req.user.id].isDirty = true;
+ res.end();
+ } else {
+ return response.errorBadRequest(res);
+ }
+ } else {
+ if (typeof req.body['pinned'] === 'undefined') return response.errorBadRequest(res);
+ getHistory(req.user.id, function (err, history) {
+ if (err) return response.errorInternalError(res);
+ if (!history) return response.errorNotFound(res);
+ if (!caches[req.user.id].history[noteId]) return response.errorNotFound(res);
+ if (req.body.pinned === 'true' || req.body.pinned === 'false') {
+ caches[req.user.id].history[noteId].pinned = (req.body.pinned === 'true');
+ caches[req.user.id].isDirty = true;
+ res.end();
+ } else {
+ return response.errorBadRequest(res);
+ }
+ });
+ }
+ } else {
+ return response.errorForbidden(res);
+ }
+}
+
+function historyDelete(req, res) {
+ if (req.isAuthenticated()) {
+ var noteId = req.params.noteId;
+ if (!noteId) {
+ setHistory(req.user.id, []);
+ caches[req.user.id].isDirty = true;
+ res.end();
+ } else {
+ getHistory(req.user.id, function (err, history) {
+ if (err) return response.errorInternalError(res);
+ if (!history) return response.errorNotFound(res);
+ delete caches[req.user.id].history[noteId];
+ caches[req.user.id].isDirty = true;
+ res.end();
+ });
+ }
+ } else {
+ return response.errorForbidden(res);
+ }
+}
+
+module.exports = History; \ No newline at end of file