2017-03-14 05:02:43 +00:00
|
|
|
'use strict'
|
2017-03-08 10:45:51 +00:00
|
|
|
// history
|
|
|
|
// external modules
|
2019-04-12 09:25:02 +00:00
|
|
|
var LZString = require('@hackmd/lz-string')
|
2016-10-10 12:51:46 +00:00
|
|
|
|
2017-03-08 10:45:51 +00:00
|
|
|
// core
|
2020-01-04 22:30:23 +00:00
|
|
|
var config = require('../config')
|
|
|
|
var logger = require('../logger')
|
|
|
|
var response = require('../response')
|
|
|
|
var models = require('../models')
|
2016-10-10 12:51:46 +00:00
|
|
|
|
2017-03-08 10:45:51 +00:00
|
|
|
function getHistory (userid, callback) {
|
|
|
|
models.User.findOne({
|
|
|
|
where: {
|
|
|
|
id: userid
|
|
|
|
}
|
|
|
|
}).then(function (user) {
|
|
|
|
if (!user) {
|
|
|
|
return callback(null, null)
|
|
|
|
}
|
|
|
|
var history = {}
|
|
|
|
if (user.history) {
|
2018-02-26 08:46:59 +00:00
|
|
|
history = JSON.parse(user.history)
|
|
|
|
// migrate LZString encoded note id to base64url encoded note id
|
|
|
|
for (let i = 0, l = history.length; i < l; i++) {
|
2018-07-27 11:44:45 +00:00
|
|
|
// Calculate minimal string length for an UUID that is encoded
|
|
|
|
// base64 encoded and optimize comparsion by using -1
|
|
|
|
// this should make a lot of LZ-String parsing errors obsolete
|
|
|
|
// as we can assume that a nodeId that is 48 chars or longer is a
|
|
|
|
// noteID.
|
|
|
|
const base64UuidLength = ((4 * 36) / 3) - 1
|
|
|
|
if (!(history[i].id.length > base64UuidLength)) {
|
|
|
|
continue
|
|
|
|
}
|
2018-03-10 08:51:00 +00:00
|
|
|
try {
|
2019-08-01 17:01:13 +00:00
|
|
|
const id = LZString.decompressFromBase64(history[i].id)
|
2018-02-26 08:46:59 +00:00
|
|
|
if (id && models.Note.checkNoteIdValid(id)) {
|
|
|
|
history[i].id = models.Note.encodeNoteId(id)
|
|
|
|
}
|
2018-03-10 08:51:00 +00:00
|
|
|
} catch (err) {
|
|
|
|
// most error here comes from LZString, ignore
|
2018-07-27 11:56:07 +00:00
|
|
|
if (err.message === 'Cannot read property \'charAt\' of undefined') {
|
|
|
|
logger.warning('Looks like we can not decode "' + history[i].id + '" with LZString. Can be ignored.')
|
|
|
|
} else {
|
|
|
|
logger.error(err)
|
|
|
|
}
|
2018-02-26 08:46:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
history = parseHistoryToObject(history)
|
2017-03-08 10:45:51 +00:00
|
|
|
}
|
|
|
|
if (config.debug) {
|
|
|
|
logger.info('read history success: ' + user.id)
|
|
|
|
}
|
|
|
|
return callback(null, history)
|
|
|
|
}).catch(function (err) {
|
|
|
|
logger.error('read history failed: ' + err)
|
|
|
|
return callback(err, null)
|
|
|
|
})
|
2016-11-16 05:58:54 +00:00
|
|
|
}
|
2016-10-10 12:51:46 +00:00
|
|
|
|
2017-03-08 10:45:51 +00:00
|
|
|
function setHistory (userid, history, callback) {
|
|
|
|
models.User.update({
|
|
|
|
history: JSON.stringify(parseHistoryToArray(history))
|
|
|
|
}, {
|
|
|
|
where: {
|
|
|
|
id: userid
|
2016-10-10 12:51:46 +00:00
|
|
|
}
|
2017-03-08 10:45:51 +00:00
|
|
|
}).then(function (count) {
|
|
|
|
return callback(null, count)
|
|
|
|
}).catch(function (err) {
|
|
|
|
logger.error('set history failed: ' + err)
|
|
|
|
return callback(err, null)
|
|
|
|
})
|
2016-10-10 12:51:46 +00:00
|
|
|
}
|
|
|
|
|
2017-03-08 10:45:51 +00:00
|
|
|
function updateHistory (userid, noteId, document, time) {
|
|
|
|
if (userid && noteId && typeof document !== 'undefined') {
|
|
|
|
getHistory(userid, function (err, history) {
|
|
|
|
if (err || !history) return
|
|
|
|
if (!history[noteId]) {
|
|
|
|
history[noteId] = {}
|
|
|
|
}
|
|
|
|
var noteHistory = history[noteId]
|
|
|
|
var noteInfo = models.Note.parseNoteInfo(document)
|
|
|
|
noteHistory.id = noteId
|
|
|
|
noteHistory.text = noteInfo.title
|
|
|
|
noteHistory.time = time || Date.now()
|
|
|
|
noteHistory.tags = noteInfo.tags
|
|
|
|
setHistory(userid, history, function (err, count) {
|
|
|
|
if (err) {
|
|
|
|
logger.log(err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
2016-11-16 05:58:54 +00:00
|
|
|
}
|
|
|
|
|
2017-03-08 10:45:51 +00:00
|
|
|
function parseHistoryToArray (history) {
|
|
|
|
var _history = []
|
|
|
|
Object.keys(history).forEach(function (key) {
|
|
|
|
var item = history[key]
|
|
|
|
_history.push(item)
|
|
|
|
})
|
|
|
|
return _history
|
2016-11-16 05:58:54 +00:00
|
|
|
}
|
|
|
|
|
2017-03-08 10:45:51 +00:00
|
|
|
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)
|
|
|
|
}
|
2016-10-10 12:51:46 +00:00
|
|
|
}
|
|
|
|
|
2017-03-08 10:45:51 +00:00
|
|
|
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, function (err, count) {
|
|
|
|
if (err) return response.errorInternalError(res)
|
|
|
|
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 (!history[noteId]) return response.errorNotFound(res)
|
|
|
|
if (req.body.pinned === 'true' || req.body.pinned === 'false') {
|
|
|
|
history[noteId].pinned = (req.body.pinned === 'true')
|
|
|
|
setHistory(req.user.id, history, function (err, count) {
|
|
|
|
if (err) return response.errorInternalError(res)
|
|
|
|
res.end()
|
|
|
|
})
|
2016-10-10 12:51:46 +00:00
|
|
|
} else {
|
2017-03-08 10:45:51 +00:00
|
|
|
return response.errorBadRequest(res)
|
2016-10-10 12:51:46 +00:00
|
|
|
}
|
2017-03-08 10:45:51 +00:00
|
|
|
})
|
2016-10-10 12:51:46 +00:00
|
|
|
}
|
2017-03-08 10:45:51 +00:00
|
|
|
} else {
|
|
|
|
return response.errorForbidden(res)
|
|
|
|
}
|
2016-10-10 12:51:46 +00:00
|
|
|
}
|
|
|
|
|
2017-03-08 10:45:51 +00:00
|
|
|
function historyDelete (req, res) {
|
|
|
|
if (req.isAuthenticated()) {
|
|
|
|
var noteId = req.params.noteId
|
|
|
|
if (!noteId) {
|
|
|
|
setHistory(req.user.id, [], function (err, count) {
|
|
|
|
if (err) return response.errorInternalError(res)
|
|
|
|
res.end()
|
|
|
|
})
|
2016-10-10 12:51:46 +00:00
|
|
|
} else {
|
2017-03-08 10:45:51 +00:00
|
|
|
getHistory(req.user.id, function (err, history) {
|
|
|
|
if (err) return response.errorInternalError(res)
|
|
|
|
if (!history) return response.errorNotFound(res)
|
|
|
|
delete history[noteId]
|
|
|
|
setHistory(req.user.id, history, function (err, count) {
|
|
|
|
if (err) return response.errorInternalError(res)
|
|
|
|
res.end()
|
|
|
|
})
|
|
|
|
})
|
2016-10-10 12:51:46 +00:00
|
|
|
}
|
2017-03-08 10:45:51 +00:00
|
|
|
} else {
|
|
|
|
return response.errorForbidden(res)
|
|
|
|
}
|
2016-10-10 12:51:46 +00:00
|
|
|
}
|
|
|
|
|
2019-08-04 19:51:00 +00:00
|
|
|
// public
|
|
|
|
exports.historyGet = historyGet
|
|
|
|
exports.historyPost = historyPost
|
|
|
|
exports.historyDelete = historyDelete
|
|
|
|
exports.updateHistory = updateHistory
|