Merge branch 'master' into feature/addSecrets
This commit is contained in:
@@ -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),
|
||||
|
||||
147
lib/history.js
147
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 {
|
||||
|
||||
@@ -6,6 +6,6 @@ module.exports = {
|
||||
},
|
||||
|
||||
down: function (queryInterface, Sequelize) {
|
||||
queryInterface.removeColumn('Notes', 'deletedAt', Sequelize.DATE);
|
||||
queryInterface.removeColumn('Notes', 'deletedAt');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user