Skip to content

Commit af77bb8

Browse files
committed
Update to add cache to history
1 parent dc34397 commit af77bb8

2 files changed

Lines changed: 233 additions & 51 deletions

File tree

app.js

Lines changed: 5 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ var i18n = require('i18n');
2222
var config = require("./lib/config.js");
2323
var logger = require("./lib/logger.js");
2424
var auth = require("./lib/auth.js");
25+
var history = require("./lib/history.js");
2526
var response = require("./lib/response.js");
2627
var models = require("./lib/models");
2728

@@ -365,56 +366,9 @@ app.get('/logout', function (req, res) {
365366
res.redirect(config.serverurl + '/');
366367
});
367368
//get history
368-
app.get('/history', function (req, res) {
369-
if (req.isAuthenticated()) {
370-
models.User.findOne({
371-
where: {
372-
id: req.user.id
373-
}
374-
}).then(function (user) {
375-
if (!user)
376-
return response.errorNotFound(res);
377-
var history = [];
378-
if (user.history)
379-
history = JSON.parse(user.history);
380-
res.send({
381-
history: history
382-
});
383-
if (config.debug)
384-
logger.info('read history success: ' + user.id);
385-
}).catch(function (err) {
386-
logger.error('read history failed: ' + err);
387-
return response.errorInternalError(res);
388-
});
389-
} else {
390-
return response.errorForbidden(res);
391-
}
392-
});
369+
app.get('/history', history.historyGet);
393370
//post history
394-
app.post('/history', urlencodedParser, function (req, res) {
395-
if (req.isAuthenticated()) {
396-
if (config.debug)
397-
logger.info('SERVER received history from [' + req.user.id + ']: ' + req.body.history);
398-
models.User.update({
399-
history: req.body.history
400-
}, {
401-
where: {
402-
id: req.user.id
403-
}
404-
}).then(function (count) {
405-
if (!count)
406-
return response.errorNotFound(res);
407-
if (config.debug)
408-
logger.info("write user history success: " + req.user.id);
409-
}).catch(function (err) {
410-
logger.error('write history failed: ' + err);
411-
return response.errorInternalError(res);
412-
});
413-
res.end();
414-
} else {
415-
return response.errorForbidden(res);
416-
}
417-
});
371+
app.post('/history', urlencodedParser, history.historyPost);
418372
//get me info
419373
app.get('/me', function (req, res) {
420374
if (req.isAuthenticated()) {
@@ -522,7 +476,7 @@ function startListen() {
522476
// sync db then start listen
523477
models.sequelize.sync().then(function () {
524478
// check if realtime is ready
525-
if (realtime.isReady()) {
479+
if (history.isReady() && realtime.isReady()) {
526480
models.Revision.checkAllNotesRevision(function (err, notes) {
527481
if (err) return new Error(err);
528482
if (!notes || notes.length <= 0) return startListen();
@@ -549,7 +503,7 @@ process.on('SIGINT', function () {
549503
socket.disconnect(true);
550504
});
551505
var checkCleanTimer = setInterval(function () {
552-
if (realtime.isReady()) {
506+
if (history.isReady() && realtime.isReady()) {
553507
models.Revision.checkAllNotesRevision(function (err, notes) {
554508
if (err) return new Error(err);
555509
if (notes.length <= 0) {

lib/history.js

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
//history
2+
//external modules
3+
var async = require('async');
4+
var moment = require('moment');
5+
6+
//core
7+
var config = require("./config.js");
8+
var logger = require("./logger.js");
9+
var response = require("./response.js");
10+
var models = require("./models");
11+
12+
//public
13+
var History = {
14+
historyGet: historyGet,
15+
historyPost: historyPost,
16+
historyDelete: historyDelete,
17+
isReady: isReady,
18+
updateHistory: updateHistory
19+
};
20+
21+
var caches = {};
22+
//update when the history is dirty
23+
var updater = setInterval(function () {
24+
var deleted = [];
25+
async.each(Object.keys(caches), function (key, callback) {
26+
var cache = caches[key];
27+
if (cache.isDirty) {
28+
if (config.debug) logger.info("history updater found dirty history: " + key);
29+
var history = parseHistoryToArray(cache.history);
30+
finishUpdateHistory(key, history, function (err, count) {
31+
if (err) return callback(err, null);
32+
if (!count) return callback(null, null);
33+
cache.isDirty = false;
34+
cache.updateAt = Date.now();
35+
return callback(null, null);
36+
});
37+
} else {
38+
if (moment().isAfter(moment(cache.updateAt).add(5, 'minutes'))) {
39+
deleted.push(key);
40+
}
41+
return callback(null, null);
42+
}
43+
}, function (err) {
44+
if (err) return logger.error('history updater error', err);
45+
});
46+
// delete specified caches
47+
for (var i = 0, l = deleted.length; i < l; i++) {
48+
caches[deleted[i]].history = {};
49+
delete caches[deleted[i]];
50+
}
51+
}, 1000);
52+
53+
function finishUpdateHistory(userid, history, callback) {
54+
models.User.update({
55+
history: JSON.stringify(history)
56+
}, {
57+
where: {
58+
id: userid
59+
}
60+
}).then(function (count) {
61+
return callback(null, count);
62+
}).catch(function (err) {
63+
return callback(err, null);
64+
});
65+
}
66+
67+
function isReady() {
68+
var dirtyCount = 0;
69+
async.each(Object.keys(caches), function (key, callback) {
70+
if (caches[key].isDirty) dirtyCount++;
71+
return callback(null, null);
72+
}, function (err) {
73+
if (err) return logger.error('history ready check error', err);
74+
});
75+
return dirtyCount > 0 ? false : true;
76+
}
77+
78+
function getHistory(userid, callback) {
79+
if (caches[userid]) {
80+
return callback(null, caches[userid].history);
81+
} else {
82+
models.User.findOne({
83+
where: {
84+
id: userid
85+
}
86+
}).then(function (user) {
87+
if (!user)
88+
return callback(null, null);
89+
var history = [];
90+
if (user.history)
91+
history = JSON.parse(user.history);
92+
if (config.debug)
93+
logger.info('read history success: ' + user.id);
94+
setHistory(userid, history);
95+
return callback(null, history);
96+
}).catch(function (err) {
97+
logger.error('read history failed: ' + err);
98+
return callback(err, null);
99+
});
100+
}
101+
}
102+
103+
function setHistory(userid, history) {
104+
if (Array.isArray(history)) history = parseHistoryToObject(history);
105+
if (!caches[userid]) {
106+
caches[userid] = {
107+
history: {},
108+
isDirty: false,
109+
updateAt: Date.now()
110+
};
111+
}
112+
caches[userid].history = history;
113+
}
114+
115+
function updateHistory(userid, noteId, document) {
116+
var t0 = new Date().getTime();
117+
if (userid && noteId && document) {
118+
getHistory(userid, function (err, history) {
119+
if (err || !history) return;
120+
if (!caches[userid].history[noteId]) {
121+
caches[userid].history[noteId] = {};
122+
}
123+
var noteHistory = caches[userid].history[noteId];
124+
var noteInfo = models.Note.parseNoteInfo(document);
125+
noteHistory.id = noteId;
126+
noteHistory.text = noteInfo.title;
127+
noteHistory.time = moment().format('MMMM Do YYYY, h:mm:ss a');
128+
noteHistory.tags = noteInfo.tags;
129+
caches[userid].isDirty = true;
130+
var t1 = new Date().getTime();
131+
console.warn(t1 - t0);
132+
});
133+
}
134+
}
135+
136+
function parseHistoryToArray(history) {
137+
var _history = [];
138+
Object.keys(history).forEach(function (key) {
139+
var item = history[key];
140+
_history.push(item);
141+
});
142+
return _history;
143+
}
144+
145+
function parseHistoryToObject(history) {
146+
var _history = {};
147+
for (var i = 0, l = history.length; i < l; i++) {
148+
var item = history[i];
149+
_history[item.id] = item;
150+
}
151+
return _history;
152+
}
153+
154+
function historyGet(req, res) {
155+
if (req.isAuthenticated()) {
156+
getHistory(req.user.id, function (err, history) {
157+
if (err) return response.errorInternalError(res);
158+
if (!history) return response.errorNotFound(res);
159+
res.send({
160+
history: parseHistoryToArray(history)
161+
});
162+
});
163+
} else {
164+
return response.errorForbidden(res);
165+
}
166+
}
167+
168+
function historyPost(req, res) {
169+
if (req.isAuthenticated()) {
170+
var noteId = req.params.noteId;
171+
if (!noteId) {
172+
if (typeof req.body['history'] === 'undefined') return response.errorBadRequest(res);
173+
if (config.debug)
174+
logger.info('SERVER received history from [' + req.user.id + ']: ' + req.body.history);
175+
try {
176+
var history = JSON.parse(req.body.history);
177+
} catch (err) {
178+
return response.errorBadRequest(res);
179+
}
180+
if (Array.isArray(history)) {
181+
setHistory(req.user.id, history);
182+
caches[req.user.id].isDirty = true;
183+
res.end();
184+
} else {
185+
return response.errorBadRequest(res);
186+
}
187+
} else {
188+
if (typeof req.body['pinned'] === 'undefined') return response.errorBadRequest(res);
189+
getHistory(req.user.id, function (err, history) {
190+
if (err) return response.errorInternalError(res);
191+
if (!history) return response.errorNotFound(res);
192+
if (!caches[req.user.id].history[noteId]) return response.errorNotFound(res);
193+
if (req.body.pinned === 'true' || req.body.pinned === 'false') {
194+
caches[req.user.id].history[noteId].pinned = (req.body.pinned === 'true');
195+
caches[req.user.id].isDirty = true;
196+
res.end();
197+
} else {
198+
return response.errorBadRequest(res);
199+
}
200+
});
201+
}
202+
} else {
203+
return response.errorForbidden(res);
204+
}
205+
}
206+
207+
function historyDelete(req, res) {
208+
if (req.isAuthenticated()) {
209+
var noteId = req.params.noteId;
210+
if (!noteId) {
211+
setHistory(req.user.id, []);
212+
caches[req.user.id].isDirty = true;
213+
res.end();
214+
} else {
215+
getHistory(req.user.id, function (err, history) {
216+
if (err) return response.errorInternalError(res);
217+
if (!history) return response.errorNotFound(res);
218+
delete caches[req.user.id].history[noteId];
219+
caches[req.user.id].isDirty = true;
220+
res.end();
221+
});
222+
}
223+
} else {
224+
return response.errorForbidden(res);
225+
}
226+
}
227+
228+
module.exports = History;

0 commit comments

Comments
 (0)