diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/config.js | 44 | ||||
-rw-r--r-- | lib/history.js | 147 | ||||
-rw-r--r-- | lib/migrations/20161009040430-support-delete-note.js | 2 | ||||
-rw-r--r-- | lib/migrations/20161201050312-support-email-signin.js | 4 | ||||
-rw-r--r-- | lib/models/note.js | 4 | ||||
-rw-r--r-- | lib/models/user.js | 36 | ||||
-rw-r--r-- | lib/realtime.js | 41 |
7 files changed, 134 insertions, 144 deletions
diff --git a/lib/config.js b/lib/config.js index ab2f67b5..1e5838e8 100644 --- a/lib/config.js +++ b/lib/config.js @@ -1,4 +1,5 @@ // external modules +var fs = require('fs'); var path = require('path'); var fs = require('fs'); @@ -27,8 +28,16 @@ var allowanonymous = process.env.HMD_ALLOW_ANONYMOUS ? (process.env.HMD_ALLOW_AN var allowfreeurl = process.env.HMD_ALLOW_FREEURL ? (process.env.HMD_ALLOW_FREEURL === 'true') : !!config.allowfreeurl; +var permissions = ['editable', 'limited', 'locked', 'protected', 'private']; +if (allowanonymous) { + permissions.unshift('freely'); +} + +var defaultpermission = process.env.HMD_DEFAULT_PERMISSION || config.defaultpermission; +defaultpermission = permissions.indexOf(defaultpermission) != -1 ? defaultpermission : 'editable'; + // db -var dburl = config.dburl || process.env.HMD_DB_URL || process.env.DATABASE_URL; +var dburl = process.env.HMD_DB_URL || process.env.DATABASE_URL || config.dburl; var db = config.db || {}; // ssl path @@ -91,15 +100,16 @@ var gitlab = (process.env.HMD_GITLAB_CLIENTID && process.env.HMD_GITLAB_CLIENTSE clientID: handleDockerSecret('gitlab_clientID') || process.env.HMD_GITLAB_CLIENTID, clientSecret: handleDockerSecret('gitlab_clientSecret') || process.env.HMD_GITLAB_CLIENTSECRET } : config.gitlab || false; -var dropbox = (process.env.HMD_DROPBOX_CLIENTID && process.env.HMD_DROPBOX_CLIENTSECRET || fs.existsSync('/run/secrets/dropbox_clientID') && fs.existsSync('/run/secrets/dropbox_clientSecret')) ? { +var dropbox = ((process.env.HMD_DROPBOX_CLIENTID && process.env.HMD_DROPBOX_CLIENTSECRET) || (fs.existsSync('/run/secrets/dropbox_clientID') && fs.existsSync('/run/secrets/dropbox_clientSecret'))) ? { clientID: handleDockerSecret('dropbox_clientID') || process.env.HMD_DROPBOX_CLIENTID, clientSecret: handleDockerSecret('dropbox_clientSecret') || process.env.HMD_DROPBOX_CLIENTSECRET -} : config.dropbox || false; -var google = (process.env.HMD_GOOGLE_CLIENTID && process.env.HMD_GOOGLE_CLIENTSECRET || fs.existsSync('/run/secrets/google_clientID') && fs.existsSync('/run/secrets/google_clientSecret')) ? { - clientID: process.env.HMD_GOOGLE_CLIENTID, - clientSecret: process.env.HMD_GOOGLE_CLIENTSECRET -} : config.google || false; -var ldap = config.ldap || ( +} : (config.dropbox && config.dropbox.clientID && config.dropbox.clientSecret && config.dropbox) || false; +var google = ((process.env.HMD_GOOGLE_CLIENTID && process.env.HMD_GOOGLE_CLIENTSECRET) + || (fs.existsSync('/run/secrets/google_clientID') && fs.existsSync('/run/secrets/google_clientSecret'))) ? { + clientID: handleDockerSecret('google_clientID') || process.env.HMD_GOOGLE_CLIENTID, + clientSecret: handleDockerSecret('google_clientSecret') || process.env.HMD_GOOGLE_CLIENTSECRET +} : (config.google && config.google.clientID && config.google.clientSecret && config.google) || false; +var ldap = config.ldap || (( process.env.HMD_LDAP_URL || process.env.HMD_LDAP_BINDDN || process.env.HMD_LDAP_BINDCREDENTIALS || @@ -107,10 +117,9 @@ var ldap = config.ldap || ( process.env.HMD_LDAP_SEARCHBASE || process.env.HMD_LDAP_SEARCHFILTER || process.env.HMD_LDAP_SEARCHATTRIBUTES || + process.env.HMD_LDAP_TLS_CA || process.env.HMD_LDAP_PROVIDERNAME -) || false; -if (ldap == true) - ldap = {}; +) ? {} : false); if (process.env.HMD_LDAP_URL) ldap.url = process.env.HMD_LDAP_URL; if (process.env.HMD_LDAP_BINDDN) @@ -127,9 +136,17 @@ if (process.env.HMD_LDAP_SEARCHATTRIBUTES) ldap.searchAttributes = process.env.HMD_LDAP_SEARCHATTRIBUTES; if (process.env.HMD_LDAP_TLS_CA) { var ca = { - ca: process.env.HMD_LDAP_TLS_CA + ca: process.env.HMD_LDAP_TLS_CA.split(',') + } + ldap.tlsOptions = ldap.tlsOptions ? Object.assign(ldap.tlsOptions, ca) : ca; + if (Array.isArray(ldap.tlsOptions.ca) && ldap.tlsOptions.ca.length > 0) { + var i, len, results; + results = []; + for (i = 0, len = ldap.tlsOptions.ca.length; i < len; i++) { + results.push(fs.readFileSync(ldap.tlsOptions.ca[i], 'utf8')); + } + ldap.tlsOptions.ca = results; } - ldap.tlsOptions = ldap.tlsOptions ? Object.assign(ldap.tlsOptions, ca) : ca } if (process.env.HMD_LDAP_PROVIDERNAME) { ldap.providerName = process.env.HMD_LDAP_PROVIDERNAME; @@ -169,6 +186,7 @@ module.exports = { usecdn: usecdn, allowanonymous: allowanonymous, allowfreeurl: allowfreeurl, + defaultpermission: defaultpermission, dburl: dburl, db: db, sslkeypath: path.join(cwd, sslkeypath), diff --git a/lib/history.js b/lib/history.js index 2723422c..e7fb3087 100644 --- a/lib/history.js +++ b/lib/history.js @@ -1,7 +1,6 @@ //history //external modules var async = require('async'); -var moment = require('moment'); //core var config = require("./config.js"); @@ -14,45 +13,32 @@ 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); - cache.isDirty = false; - finishUpdateHistory(key, history, function (err, count) { - if (err) return callback(err, null); - if (!count) return callback(null, null); - 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 getHistory(userid, callback) { + models.User.findOne({ + where: { + id: userid } - }, function (err) { - if (err) return logger.error('history updater error', err); + }).then(function (user) { + if (!user) + return callback(null, null); + var history = {}; + if (user.history) + history = parseHistoryToObject(JSON.parse(user.history)); + 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); }); - // 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) { +function setHistory(userid, history, callback) { models.User.update({ - history: JSON.stringify(history) + history: JSON.stringify(parseHistoryToArray(history)) }, { where: { id: userid @@ -60,72 +46,27 @@ function finishUpdateHistory(userid, history, callback) { }).then(function (count) { return callback(null, count); }).catch(function (err) { + logger.error('set history failed: ' + 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) { +function updateHistory(userid, noteId, document, time) { if (userid && noteId && typeof document !== 'undefined') { getHistory(userid, function (err, history) { if (err || !history) return; - if (!caches[userid].history[noteId]) { - caches[userid].history[noteId] = {}; + if (!history[noteId]) { + history[noteId] = {}; } - var noteHistory = caches[userid].history[noteId]; + var noteHistory = history[noteId]; var noteInfo = models.Note.parseNoteInfo(document); noteHistory.id = noteId; noteHistory.text = noteInfo.title; - noteHistory.time = moment().valueOf(); + noteHistory.time = time || Date.now(); noteHistory.tags = noteInfo.tags; - caches[userid].isDirty = true; + setHistory(userid, history, function (err, count) { + return; + }); }); } } @@ -175,9 +116,10 @@ function historyPost(req, res) { return response.errorBadRequest(res); } if (Array.isArray(history)) { - setHistory(req.user.id, history); - caches[req.user.id].isDirty = true; - res.end(); + setHistory(req.user.id, history, function (err, count) { + if (err) return response.errorInternalError(res); + res.end(); + }); } else { return response.errorBadRequest(res); } @@ -186,11 +128,13 @@ function historyPost(req, 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 (!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(); + history[noteId].pinned = (req.body.pinned === 'true'); + setHistory(req.user.id, history, function (err, count) { + if (err) return response.errorInternalError(res); + res.end(); + }); } else { return response.errorBadRequest(res); } @@ -205,16 +149,19 @@ 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(); + setHistory(req.user.id, [], function (err, count) { + if (err) return response.errorInternalError(res); + 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(); + delete history[noteId]; + setHistory(req.user.id, history, function (err, count) { + if (err) return response.errorInternalError(res); + res.end(); + }); }); } } else { diff --git a/lib/migrations/20161009040430-support-delete-note.js b/lib/migrations/20161009040430-support-delete-note.js index f478b6fe..92ff6f7b 100644 --- a/lib/migrations/20161009040430-support-delete-note.js +++ b/lib/migrations/20161009040430-support-delete-note.js @@ -6,6 +6,6 @@ module.exports = { }, down: function (queryInterface, Sequelize) { - queryInterface.removeColumn('Notes', 'deletedAt', Sequelize.DATE); + queryInterface.removeColumn('Notes', 'deletedAt'); } }; diff --git a/lib/migrations/20161201050312-support-email-signin.js b/lib/migrations/20161201050312-support-email-signin.js index bdea7c8c..b5aaf777 100644 --- a/lib/migrations/20161201050312-support-email-signin.js +++ b/lib/migrations/20161201050312-support-email-signin.js @@ -7,7 +7,7 @@ module.exports = { }, down: function (queryInterface, Sequelize) { - queryInterface.removeColumn('Users', 'email', Sequelize.TEXT); - queryInterface.removeColumn('Users', 'password', Sequelize.TEXT); + queryInterface.removeColumn('Users', 'email'); + queryInterface.removeColumn('Users', 'password'); } }; diff --git a/lib/models/note.js b/lib/models/note.js index 86112973..8b38d3f9 100644 --- a/lib/models/note.js +++ b/lib/models/note.js @@ -513,10 +513,10 @@ module.exports = function (sequelize, DataTypes) { } } } - // if no permission specified and have owner then give editable permission, else default permission is freely + // if no permission specified and have owner then give default permission in config, else default permission is freely if (!note.permission) { if (note.ownerId) { - note.permission = "editable"; + note.permission = config.defaultpermission; } else { note.permission = "freely"; } diff --git a/lib/models/user.js b/lib/models/user.js index 7d27242c..dd93bf78 100644 --- a/lib/models/user.js +++ b/lib/models/user.js @@ -79,39 +79,54 @@ module.exports = function (sequelize, DataTypes) { if (profile) { profile = { name: profile.displayName || profile.username, - photo: User.parsePhotoByProfile(profile) + photo: User.parsePhotoByProfile(profile), + biggerphoto: User.parsePhotoByProfile(profile, true) } } return profile; }, - parsePhotoByProfile: function (profile) { + parsePhotoByProfile: function (profile, bigger) { var photo = null; switch (profile.provider) { case "facebook": - photo = 'https://graph.facebook.com/' + profile.id + '/picture?width=96'; + photo = 'https://graph.facebook.com/' + profile.id + '/picture'; + if (bigger) photo += '?width=400'; + else photo += '?width=96'; break; case "twitter": - photo = 'https://twitter.com/' + profile.username + '/profile_image?size=bigger'; + photo = 'https://twitter.com/' + profile.username + '/profile_image'; + if (bigger) photo += '?size=original'; + else photo += '?size=bigger'; break; case "github": - photo = 'https://avatars.githubusercontent.com/u/' + profile.id + '?s=96'; + photo = 'https://avatars.githubusercontent.com/u/' + profile.id; + if (bigger) photo += '?s=400'; + else photo += '?s=96'; break; case "gitlab": - photo = profile.avatarUrl.replace(/(\?s=)\d*$/i, '$196'); + photo = profile.avatarUrl; + if (bigger) photo = photo.replace(/(\?s=)\d*$/i, '$1400'); + else photo = photo.replace(/(\?s=)\d*$/i, '$196'); break; case "dropbox": //no image api provided, use gravatar - photo = 'https://www.gravatar.com/avatar/' + md5(profile.emails[0].value) + '?s=96'; + photo = 'https://www.gravatar.com/avatar/' + md5(profile.emails[0].value); + if (bigger) photo += '?s=400'; + else photo += '?s=96'; break; case "google": - photo = profile.photos[0].value.replace(/(\?sz=)\d*$/i, '$196'); + photo = profile.photos[0].value; + if (bigger) photo = photo.replace(/(\?sz=)\d*$/i, '$1400'); + else photo = photo.replace(/(\?sz=)\d*$/i, '$196'); break; case "ldap": //no image api provided, //use gravatar if email exists, //otherwise generate a letter avatar if (profile.emails[0]) { - photo = 'https://www.gravatar.com/avatar/' + md5(profile.emails[0]) + '?s=96'; + photo = 'https://www.gravatar.com/avatar/' + md5(profile.emails[0]); + if (bigger) photo += '?s=400'; + else photo += '?s=96'; } else { photo = letterAvatars(profile.username); } @@ -123,7 +138,8 @@ module.exports = function (sequelize, DataTypes) { var photoUrl = 'https://www.gravatar.com/avatar/' + md5(email); return { name: email.substring(0, email.lastIndexOf("@")), - photo: photoUrl += '?s=96' + photo: photoUrl += '?s=96', + biggerphoto: photoUrl += '?s=400' }; } } diff --git a/lib/realtime.js b/lib/realtime.js index fadea4f2..c1db6886 100644 --- a/lib/realtime.js +++ b/lib/realtime.js @@ -122,6 +122,12 @@ function updateNote(note, callback) { } }).then(function (_note) { if (!_note) return callback(null, null); + // update user note history + var tempUsers = Object.assign({}, note.tempUsers); + note.tempUsers = {}; + Object.keys(tempUsers).forEach(function (key) { + updateHistory(key, note, tempUsers[key]); + }); if (note.lastchangeuser) { if (_note.lastchangeuserId != note.lastchangeuser) { models.User.findOne({ @@ -348,9 +354,12 @@ function clearSocketQueue(queue, socket) { } function connectNextSocket() { - isConnectionBusy = false; - if (connectionSocketQueue.length > 0) - startConnection(connectionSocketQueue[0]); + setTimeout(function () { + isConnectionBusy = false; + if (connectionSocketQueue.length > 0) { + startConnection(connectionSocketQueue[0]); + } + }, 1); } function interruptConnection(socket, note, user) { @@ -405,10 +414,7 @@ function finishConnection(socket, note, user) { note.server.setColor(socket, user.color); // update user note history - setTimeout(function () { - var noteId = note.alias ? note.alias : LZString.compressToBase64(note.id); - if (note.server) history.updateHistory(user.userid, noteId, note.server.document); - }, 0); + updateHistory(user.userid, note); emitOnlineUsers(socket); emitRefresh(socket); @@ -497,6 +503,7 @@ function startConnection(socket) { lastchangeuserprofile: lastchangeuserprofile, socks: [], users: {}, + tempUsers: {}, createtime: moment(createtime).valueOf(), updatetime: moment(updatetime).valueOf(), server: server, @@ -687,15 +694,17 @@ function operationCallback(socket, operation) { return logger.error('operation callback failed: ' + err); }); } - // update user note history - setTimeout(function() { - var noteId = note.alias ? note.alias : LZString.compressToBase64(note.id); - if (note.server) history.updateHistory(userId, noteId, note.server.document); - }, 0); - + note.tempUsers[userId] = Date.now(); } - // save authorship - note.authorship = models.Note.updateAuthorshipByOperation(operation, userId, note.authorship); + // save authorship - use timer here because it's an O(n) complexity algorithm + setImmediate(function () { + note.authorship = models.Note.updateAuthorshipByOperation(operation, userId, note.authorship); + }); +} + +function updateHistory(userId, note, time) { + var noteId = note.alias ? note.alias : LZString.compressToBase64(note.id); + if (note.server) history.updateHistory(userId, noteId, note.server.document, time); } function connection(socket) { @@ -925,4 +934,4 @@ function connection(socket) { }); } -module.exports = realtime; +module.exports = realtime;
\ No newline at end of file |