mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-01 03:26:04 +01:00
43
public/src/admin/advanced/events.js
Normal file
43
public/src/admin/advanced/events.js
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
"use strict";
|
||||||
|
/* global define, socket, app, templates */
|
||||||
|
|
||||||
|
|
||||||
|
define('admin/advanced/events', ['forum/infinitescroll'], function(infinitescroll) {
|
||||||
|
var Events = {};
|
||||||
|
|
||||||
|
Events.init = function() {
|
||||||
|
|
||||||
|
$('[data-action="clear"]').on('click', function() {
|
||||||
|
socket.emit('admin.deleteAllEvents', function(err) {
|
||||||
|
if (err) {
|
||||||
|
return app.alertError(err.message);
|
||||||
|
}
|
||||||
|
$('.events-list').empty();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
infinitescroll.init(function(direction) {
|
||||||
|
if (direction < 0 || !$('.events').length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
infinitescroll.loadMore('admin.getMoreEvents', $('[data-next]').attr('data-next'), function(data, done) {
|
||||||
|
console.log(data.events);
|
||||||
|
if (data.events && data.events.length) {
|
||||||
|
templates.parse('admin/advanced/events', 'events', {events: data.events}, function(html) {
|
||||||
|
console.log(html);
|
||||||
|
$('.events-list').append(html);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('[data-next]').attr('data-next', data.next);
|
||||||
|
} else {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
return Events;
|
||||||
|
});
|
||||||
@@ -174,14 +174,14 @@ adminController.database.get = function(req, res, next) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
adminController.events.get = function(req, res, next) {
|
adminController.events.get = function(req, res, next) {
|
||||||
events.getLog(-1, 5000, function(err, data) {
|
events.getEvents(0, 19, function(err, events) {
|
||||||
if(err || !data) {
|
if(err || !events) {
|
||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
res.render('admin/advanced/events', {
|
res.render('admin/advanced/events', {
|
||||||
eventdata: data.data,
|
events: events,
|
||||||
next: data.next
|
next: 20
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
232
src/events.js
232
src/events.js
@@ -1,154 +1,128 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var fs = require('fs'),
|
var async = require('async'),
|
||||||
winston = require('winston'),
|
|
||||||
path = require('path'),
|
db = require('./database'),
|
||||||
nconf = require('nconf'),
|
batch = require('./batch'),
|
||||||
user = require('./user');
|
user = require('./user'),
|
||||||
|
utils = require('../public/src/utils');
|
||||||
|
|
||||||
|
|
||||||
(function(events) {
|
(function(events) {
|
||||||
var logFileName = 'logs/events.log';
|
events.log = function(data, callback) {
|
||||||
|
callback = callback || function() {};
|
||||||
|
|
||||||
events.logPasswordChange = function(uid) {
|
async.waterfall([
|
||||||
events.logWithUser(uid, 'changed password');
|
function(next) {
|
||||||
};
|
db.incrObjectField('global', 'nextEid', next);
|
||||||
|
},
|
||||||
|
function(eid, next) {
|
||||||
|
data.timestamp = Date.now();
|
||||||
|
data.eid = eid;
|
||||||
|
|
||||||
events.logAdminChangeUserPassword = function(adminUid, theirUid, callback) {
|
async.parallel([
|
||||||
logAdminEvent(adminUid, theirUid, 'changed password of', callback);
|
function(next) {
|
||||||
};
|
db.sortedSetAdd('events:time', data.timestamp, eid, next);
|
||||||
|
},
|
||||||
events.logAdminUserDelete = function(adminUid, theirUid, callback) {
|
function(next) {
|
||||||
logAdminEvent(adminUid, theirUid, 'deleted', callback);
|
db.setObject('event:' + eid, data, next);
|
||||||
};
|
|
||||||
|
|
||||||
function logAdminEvent(adminUid, theirUid, message, callback) {
|
|
||||||
user.getMultipleUserFields([adminUid, theirUid], ['username'], function(err, userData) {
|
|
||||||
if(err) {
|
|
||||||
return winston.error('Error logging event. ' + err.message);
|
|
||||||
}
|
}
|
||||||
|
], next);
|
||||||
var msg = userData[0].username + '(uid ' + adminUid + ') ' + message + ' ' + userData[1].username + '(uid ' + theirUid + ')';
|
|
||||||
events.log(msg, callback);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
], function(err, result) {
|
||||||
events.logPasswordReset = function(uid) {
|
|
||||||
events.logWithUser(uid, 'reset password');
|
|
||||||
};
|
|
||||||
|
|
||||||
events.logEmailChange = function(uid, oldEmail, newEmail) {
|
|
||||||
events.logWithUser(uid,'changed email from "' + oldEmail + '" to "' + newEmail +'"');
|
|
||||||
};
|
|
||||||
|
|
||||||
events.logUsernameChange = function(uid, oldUsername, newUsername) {
|
|
||||||
events.logWithUser(uid,'changed username from "' + oldUsername + '" to "' + newUsername +'"');
|
|
||||||
};
|
|
||||||
|
|
||||||
events.logAdminLogin = function(uid) {
|
|
||||||
events.logWithUser(uid, 'logged into admin panel');
|
|
||||||
};
|
|
||||||
|
|
||||||
events.logPostEdit = function(uid, pid) {
|
|
||||||
events.logWithUser(uid, 'edited post (pid ' + pid + ')');
|
|
||||||
};
|
|
||||||
|
|
||||||
events.logPostDelete = function(uid, pid) {
|
|
||||||
events.logWithUser(uid, 'deleted post (pid ' + pid + ')');
|
|
||||||
};
|
|
||||||
|
|
||||||
events.logPostRestore = function(uid, pid) {
|
|
||||||
events.logWithUser(uid, 'restored post (pid ' + pid + ')');
|
|
||||||
};
|
|
||||||
|
|
||||||
events.logPostPurge = function(uid, pid) {
|
|
||||||
events.logWithUser(uid, 'purged post (pid ' + pid + ')');
|
|
||||||
};
|
|
||||||
|
|
||||||
events.logTopicMove = function(uid, tid) {
|
|
||||||
events.logWithUser(uid, 'moved topic (tid ' + tid + ')');
|
|
||||||
};
|
|
||||||
|
|
||||||
events.logTopicDelete = function(uid, tid) {
|
|
||||||
events.logWithUser(uid, 'deleted topic (tid ' + tid + ')');
|
|
||||||
};
|
|
||||||
|
|
||||||
events.logTopicRestore = function(uid, tid) {
|
|
||||||
events.logWithUser(uid, 'restored topic (tid ' + tid + ')');
|
|
||||||
};
|
|
||||||
|
|
||||||
events.logAccountLock = function(uid, until) {
|
|
||||||
var date = new Date();
|
|
||||||
date.setTime(date.getTime() + until);
|
|
||||||
|
|
||||||
events.logWithUser(uid, 'locked out until ' + date.toString());
|
|
||||||
};
|
|
||||||
|
|
||||||
events.logWithUser = function(uid, string) {
|
|
||||||
user.getUserField(uid, 'username', function(err, username) {
|
|
||||||
if(err) {
|
|
||||||
return winston.error('Error logging event. ' + err.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
var msg = username + '(uid ' + uid + ') ' + string;
|
|
||||||
events.log(msg);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
events.log = function(msg, callback) {
|
|
||||||
var logFile = path.join(nconf.get('base_dir'), logFileName);
|
|
||||||
|
|
||||||
msg = '[' + new Date().toUTCString() + '] - ' + msg;
|
|
||||||
|
|
||||||
fs.appendFile(logFile, msg + '\n', function(err) {
|
|
||||||
if(err) {
|
|
||||||
winston.error('Error logging event. ' + err.message);
|
|
||||||
if (typeof callback === 'function') {
|
|
||||||
callback(err);
|
callback(err);
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof callback === 'function') {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
events.getLog = function(end, len, callback) {
|
events.getEvents = function(start, stop, callback) {
|
||||||
var logFile = path.join(nconf.get('base_dir'), logFileName);
|
async.waterfall([
|
||||||
|
function(next) {
|
||||||
|
db.getSortedSetRevRange('events:time', start, stop, next);
|
||||||
|
},
|
||||||
|
function(eids, next) {
|
||||||
|
var keys = eids.map(function(eid) {
|
||||||
|
return 'event:' + eid;
|
||||||
|
});
|
||||||
|
db.getObjects(keys, next);
|
||||||
|
},
|
||||||
|
function(eventsData, next) {
|
||||||
|
eventsData.forEach(function(event) {
|
||||||
|
var e = utils.merge(event);
|
||||||
|
e.eid = e.uid = e.type = e.ip = undefined;
|
||||||
|
event.jsonString = JSON.stringify(e, null, 4);
|
||||||
|
event.timestampISO = new Date(parseInt(event.timestamp, 10)).toUTCString();
|
||||||
|
});
|
||||||
|
addUserData(eventsData, 'uid', 'user', next);
|
||||||
|
},
|
||||||
|
function(eventsData, next) {
|
||||||
|
addUserData(eventsData, 'targetUid', 'targetUser', next);
|
||||||
|
}
|
||||||
|
], callback);
|
||||||
|
};
|
||||||
|
|
||||||
fs.stat(logFile, function(err, stat) {
|
function addUserData(eventsData, field, objectName, callback) {
|
||||||
|
var uids = eventsData.map(function(event) {
|
||||||
|
return event && event[field];
|
||||||
|
}).filter(function(uid, index, array) {
|
||||||
|
return uid && array.indexOf(uid) === index;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!uids.length) {
|
||||||
|
return callback(null, eventsData);
|
||||||
|
}
|
||||||
|
|
||||||
|
async.parallel({
|
||||||
|
isAdmin: function(next) {
|
||||||
|
user.isAdministrator(uids, next);
|
||||||
|
},
|
||||||
|
userData: function(next) {
|
||||||
|
user.getMultipleUserFields(uids, ['username', 'userslug', 'picture'], next);
|
||||||
|
}
|
||||||
|
}, function(err, results) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(null, 'No logs found!');
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
var buffer = '';
|
var userData = results.userData;
|
||||||
var size = stat.size;
|
|
||||||
if (end === -1) {
|
var map = {};
|
||||||
end = size;
|
userData.forEach(function(user, index) {
|
||||||
|
user.isAdmin = results.isAdmin[index];
|
||||||
|
map[user.uid] = user;
|
||||||
|
});
|
||||||
|
|
||||||
|
eventsData.forEach(function(event) {
|
||||||
|
if (map[event[field]]) {
|
||||||
|
event[objectName] = map[event[field]];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
callback(null, eventsData);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
end = parseInt(end, 10);
|
events.deleteEvents = function(eids, callback) {
|
||||||
var start = Math.max(0, end - len);
|
callback = callback || function() {};
|
||||||
|
async.parallel([
|
||||||
var rs = fs.createReadStream(logFile, {start: start, end: end});
|
function(next) {
|
||||||
rs.addListener('data', function(lines) {
|
var keys = eids.map(function(eid) {
|
||||||
buffer += lines.toString();
|
return 'event:' + eid;
|
||||||
});
|
});
|
||||||
|
db.deleteAll(keys, next);
|
||||||
rs.addListener('end', function() {
|
},
|
||||||
var firstNewline = buffer.indexOf('\n');
|
function(next) {
|
||||||
if (firstNewline !== -1) {
|
db.sortedSetRemove('events:time', eids, next);
|
||||||
buffer = buffer.slice(firstNewline);
|
|
||||||
buffer = buffer.split('\n').reverse().join('\n');
|
|
||||||
}
|
}
|
||||||
|
], callback);
|
||||||
callback(null, {data: buffer, next: end - buffer.length});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
events.deleteAll = function(callback) {
|
||||||
|
callback = callback || function() {};
|
||||||
|
|
||||||
|
batch.processSortedSet('events:time', function(eids, next) {
|
||||||
|
events.deleteEvents(eids, callback);
|
||||||
|
}, {alwaysStartAt: 0}, callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
}(module.exports));
|
}(module.exports));
|
||||||
|
|||||||
@@ -43,10 +43,6 @@ var async = require('async'),
|
|||||||
|
|
||||||
db.sortedSetAdd('users:reputation', newreputation, postData.uid);
|
db.sortedSetAdd('users:reputation', newreputation, postData.uid);
|
||||||
|
|
||||||
if (type === 'downvote') {
|
|
||||||
banUserForLowReputation(postData.uid, newreputation);
|
|
||||||
}
|
|
||||||
|
|
||||||
adjustPostVotes(pid, uid, type, unvote, function(err, votes) {
|
adjustPostVotes(pid, uid, type, unvote, function(err, votes) {
|
||||||
postData.votes = votes;
|
postData.votes = votes;
|
||||||
callback(err, {
|
callback(err, {
|
||||||
@@ -62,23 +58,6 @@ var async = require('async'),
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function banUserForLowReputation(uid, newreputation) {
|
|
||||||
if (parseInt(meta.config['autoban:downvote'], 10) === 1 && newreputation < parseInt(meta.config['autoban:downvote:threshold'], 10)) {
|
|
||||||
user.getUserField(uid, 'banned', function(err, banned) {
|
|
||||||
if (err || parseInt(banned, 10) === 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var adminUser = require('./socket.io/admin/user');
|
|
||||||
adminUser.banUser(uid, function(err) {
|
|
||||||
if (err) {
|
|
||||||
return winston.error(err.message);
|
|
||||||
}
|
|
||||||
winston.info('uid ' + uid + ' was banned for reaching ' + newreputation + ' reputation');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function adjustPostVotes(pid, uid, type, unvote, callback) {
|
function adjustPostVotes(pid, uid, type, unvote, callback) {
|
||||||
var notType = (type === 'upvote' ? 'downvote' : 'upvote');
|
var notType = (type === 'upvote' ? 'downvote' : 'upvote');
|
||||||
|
|
||||||
|
|||||||
@@ -311,8 +311,6 @@ var async = require('async'),
|
|||||||
if (process.env.NODE_ENV === 'development') {
|
if (process.env.NODE_ENV === 'development') {
|
||||||
winston.info('[notifications.prune] Notification pruning completed. ' + numPruned + ' expired notification' + (numPruned !== 1 ? 's' : '') + ' removed.');
|
winston.info('[notifications.prune] Notification pruning completed. ' + numPruned + ' expired notification' + (numPruned !== 1 ? 's' : '') + ' removed.');
|
||||||
}
|
}
|
||||||
var diff = process.hrtime(start);
|
|
||||||
events.log('Pruning '+ numPruned + ' notifications took : ' + (diff[0] * 1e3 + diff[1] / 1e6) + ' ms');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ var winston = require('winston'),
|
|||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
results.content = results.postData.content;
|
results.content = results.postData.content;
|
||||||
//events.logPostEdit(uid, pid);
|
|
||||||
plugins.fireHook('action:post.edit', postData);
|
plugins.fireHook('action:post.edit', postData);
|
||||||
callback(null, results);
|
callback(null, results);
|
||||||
});
|
});
|
||||||
@@ -146,7 +146,6 @@ var winston = require('winston'),
|
|||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
events[isDelete ? 'logPostDelete' : 'logPostRestore'](uid, pid);
|
|
||||||
if (isDelete) {
|
if (isDelete) {
|
||||||
posts.delete(pid, callback);
|
posts.delete(pid, callback);
|
||||||
} else {
|
} else {
|
||||||
@@ -165,7 +164,7 @@ var winston = require('winston'),
|
|||||||
if (err || !canEdit) {
|
if (err || !canEdit) {
|
||||||
return callback(err || new Error('[[error:no-privileges]]'));
|
return callback(err || new Error('[[error:no-privileges]]'));
|
||||||
}
|
}
|
||||||
events.logPostPurge(uid, pid);
|
|
||||||
posts.purge(pid, callback);
|
posts.purge(pid, callback);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -68,7 +68,7 @@
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Auth.login = function(username, password, next) {
|
Auth.login = function(req, username, password, next) {
|
||||||
if (!username || !password) {
|
if (!username || !password) {
|
||||||
return next(new Error('[[error:invalid-password]]'));
|
return next(new Error('[[error:invalid-password]]'));
|
||||||
}
|
}
|
||||||
@@ -85,7 +85,7 @@
|
|||||||
return next(new Error('[[error:no-user]]'));
|
return next(new Error('[[error:no-user]]'));
|
||||||
}
|
}
|
||||||
uid = _uid;
|
uid = _uid;
|
||||||
user.auth.logAttempt(uid, next);
|
user.auth.logAttempt(uid, req.ip, next);
|
||||||
},
|
},
|
||||||
function(next) {
|
function(next) {
|
||||||
db.getObjectFields('user:' + uid, ['password', 'banned'], next);
|
db.getObjectFields('user:' + uid, ['password', 'banned'], next);
|
||||||
@@ -109,7 +109,7 @@
|
|||||||
], next);
|
], next);
|
||||||
};
|
};
|
||||||
|
|
||||||
passport.use(new passportLocal(Auth.login));
|
passport.use(new passportLocal({passReqToCallback: true}, Auth.login));
|
||||||
|
|
||||||
passport.serializeUser(function(user, done) {
|
passport.serializeUser(function(user, done) {
|
||||||
done(null, user.uid);
|
done(null, user.uid);
|
||||||
|
|||||||
@@ -49,7 +49,11 @@ SocketAdmin.before = function(socket, method, next) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
SocketAdmin.reload = function(socket, data, callback) {
|
SocketAdmin.reload = function(socket, data, callback) {
|
||||||
events.logWithUser(socket.uid, ' is reloading NodeBB');
|
events.log({
|
||||||
|
type: 'reload',
|
||||||
|
uid: socket.uid,
|
||||||
|
ip: socket.ip
|
||||||
|
});
|
||||||
if (process.send) {
|
if (process.send) {
|
||||||
process.send({
|
process.send({
|
||||||
action: 'reload'
|
action: 'reload'
|
||||||
@@ -60,7 +64,11 @@ SocketAdmin.reload = function(socket, data, callback) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
SocketAdmin.restart = function(socket, data, callback) {
|
SocketAdmin.restart = function(socket, data, callback) {
|
||||||
events.logWithUser(socket.uid, ' is restarting NodeBB');
|
events.log({
|
||||||
|
type: 'restart',
|
||||||
|
uid: socket.uid,
|
||||||
|
ip: socket.ip
|
||||||
|
});
|
||||||
meta.restart();
|
meta.restart();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -274,10 +282,21 @@ function getMonthlyPageViews(callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SocketAdmin.getMoreEvents = function(socket, next, callback) {
|
SocketAdmin.getMoreEvents = function(socket, next, callback) {
|
||||||
if (parseInt(next, 10) < 0) {
|
var start = parseInt(next, 10);
|
||||||
|
if (start < 0) {
|
||||||
return callback(null, {data: [], next: next});
|
return callback(null, {data: [], next: next});
|
||||||
}
|
}
|
||||||
events.getLog(next, 5000, callback);
|
var end = next + 10;
|
||||||
|
events.getEvents(start, end, function(err, events) {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
callback(null, {events: events, next: end + 1});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
SocketAdmin.deleteAllEvents = function(socket, data, callback) {
|
||||||
|
events.deleteAll(callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
SocketAdmin.dismissFlag = function(socket, pid, callback) {
|
SocketAdmin.dismissFlag = function(socket, pid, callback) {
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ User.deleteUsers = function(socket, uids, callback) {
|
|||||||
async.each(uids, function(uid, next) {
|
async.each(uids, function(uid, next) {
|
||||||
user.isAdministrator(uid, function(err, isAdmin) {
|
user.isAdministrator(uid, function(err, isAdmin) {
|
||||||
if (err || isAdmin) {
|
if (err || isAdmin) {
|
||||||
return callback(err || new Error('[[error:cant-ban-other-admins]]'));
|
return callback(err || new Error('[[error:cant-delete-other-admins]]'));
|
||||||
}
|
}
|
||||||
|
|
||||||
user.delete(uid, function(err) {
|
user.delete(uid, function(err) {
|
||||||
@@ -165,7 +165,12 @@ User.deleteUsers = function(socket, uids, callback) {
|
|||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
events.logAdminUserDelete(socket.uid, uid);
|
events.log({
|
||||||
|
type: 'user-delete',
|
||||||
|
uid: socket.uid,
|
||||||
|
targetUid: uid,
|
||||||
|
ip: socket.ip
|
||||||
|
});
|
||||||
|
|
||||||
websockets.logoutUser(uid);
|
websockets.logoutUser(uid);
|
||||||
next();
|
next();
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ var async = require('async'),
|
|||||||
groups = require('../groups'),
|
groups = require('../groups'),
|
||||||
user = require('../user'),
|
user = require('../user'),
|
||||||
websockets = require('./index'),
|
websockets = require('./index'),
|
||||||
|
events = require('../events'),
|
||||||
utils = require('../../public/src/utils'),
|
utils = require('../../public/src/utils'),
|
||||||
|
|
||||||
SocketPosts = {};
|
SocketPosts = {};
|
||||||
@@ -138,7 +139,38 @@ SocketPosts.upvote = function(socket, data, callback) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
SocketPosts.downvote = function(socket, data, callback) {
|
SocketPosts.downvote = function(socket, data, callback) {
|
||||||
favouriteCommand(socket, 'downvote', 'voted', '', data, callback);
|
function banUserForLowReputation(uid, callback) {
|
||||||
|
if (parseInt(meta.config['autoban:downvote'], 10) === 1) {
|
||||||
|
user.getUserFields(uid, ['reputation', 'banned'], function(err, userData) {
|
||||||
|
if (err || parseInt(userData.banned, 10) === 1 || parseInt(userData.reputation) >= parseInt(meta.config['autoban:downvote:threshold'], 10)) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
var adminUser = require('./admin/user');
|
||||||
|
adminUser.banUser(uid, function(err) {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
events.log({
|
||||||
|
type: 'banned',
|
||||||
|
reason: 'low-reputation',
|
||||||
|
uid: socket.uid,
|
||||||
|
ip: socket.ip,
|
||||||
|
targetUid: data.uid,
|
||||||
|
reputation: userData.reputation
|
||||||
|
});
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
favouriteCommand(socket, 'downvote', 'voted', '', data, function(err) {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
banUserForLowReputation(data.uid, callback);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
SocketPosts.unvote = function(socket, data, callback) {
|
SocketPosts.unvote = function(socket, data, callback) {
|
||||||
@@ -309,9 +341,16 @@ function deleteOrRestore(command, socket, data, callback) {
|
|||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
var eventName = command === 'restore' ? 'event:post_restored' : 'event:post_deleted';
|
var eventName = command === 'delete' ? 'event:post_deleted' : 'event:post_restored';
|
||||||
websockets.in('topic_' + data.tid).emit(eventName, postData);
|
websockets.in('topic_' + data.tid).emit(eventName, postData);
|
||||||
|
|
||||||
|
events.log({
|
||||||
|
type: command === 'delete' ? 'post-delete' : 'post-restore',
|
||||||
|
uid: socket.uid,
|
||||||
|
pid: data.pid,
|
||||||
|
ip: socket.ip
|
||||||
|
});
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -327,6 +366,13 @@ SocketPosts.purge = function(socket, data, callback) {
|
|||||||
|
|
||||||
websockets.in('topic_' + data.tid).emit('event:post_purged', data.pid);
|
websockets.in('topic_' + data.tid).emit('event:post_purged', data.pid);
|
||||||
|
|
||||||
|
events.log({
|
||||||
|
type: 'post-purge',
|
||||||
|
uid: socket.uid,
|
||||||
|
pid: data.pid,
|
||||||
|
ip: socket.ip
|
||||||
|
});
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ var nconf = require('nconf'),
|
|||||||
user = require('../user'),
|
user = require('../user'),
|
||||||
db = require('../database'),
|
db = require('../database'),
|
||||||
meta = require('../meta'),
|
meta = require('../meta'),
|
||||||
|
events = require('../events'),
|
||||||
utils = require('../../public/src/utils'),
|
utils = require('../../public/src/utils'),
|
||||||
SocketPosts = require('./posts'),
|
SocketPosts = require('./posts'),
|
||||||
|
|
||||||
@@ -259,7 +260,21 @@ function doTopicAction(action, socket, data, callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(typeof threadTools[action] === 'function') {
|
if(typeof threadTools[action] === 'function') {
|
||||||
threadTools[action](tid, socket.uid, next);
|
threadTools[action](tid, socket.uid, function(err) {
|
||||||
|
if (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action === 'delete' || action === 'restore' || action === 'purge') {
|
||||||
|
events.log({
|
||||||
|
type: 'topic-' + action,
|
||||||
|
uid: socket.uid,
|
||||||
|
ip: socket.ip,
|
||||||
|
tid: tid
|
||||||
|
});
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, callback);
|
}, callback);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ var async = require('async'),
|
|||||||
utils = require('../../public/src/utils'),
|
utils = require('../../public/src/utils'),
|
||||||
websockets = require('./index'),
|
websockets = require('./index'),
|
||||||
meta = require('../meta'),
|
meta = require('../meta'),
|
||||||
|
events = require('../events'),
|
||||||
SocketUser = {};
|
SocketUser = {};
|
||||||
|
|
||||||
SocketUser.exists = function(socket, data, callback) {
|
SocketUser.exists = function(socket, data, callback) {
|
||||||
@@ -56,7 +57,7 @@ SocketUser.emailConfirm = function(socket, data, callback) {
|
|||||||
|
|
||||||
SocketUser.search = function(socket, data, callback) {
|
SocketUser.search = function(socket, data, callback) {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return callback(new Error('[[error:invalid-data]]'))
|
return callback(new Error('[[error:invalid-data]]'));
|
||||||
}
|
}
|
||||||
if (!socket.uid) {
|
if (!socket.uid) {
|
||||||
return callback(new Error('[[error:not-logged-in]]'));
|
return callback(new Error('[[error:not-logged-in]]'));
|
||||||
@@ -87,7 +88,16 @@ SocketUser.reset.valid = function(socket, code, callback) {
|
|||||||
|
|
||||||
SocketUser.reset.commit = function(socket, data, callback) {
|
SocketUser.reset.commit = function(socket, data, callback) {
|
||||||
if(data && data.code && data.password) {
|
if(data && data.code && data.password) {
|
||||||
user.reset.commit(data.code, data.password, callback);
|
user.reset.commit(data.code, data.password, function(err) {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
events.log({
|
||||||
|
type: 'password-reset',
|
||||||
|
uid: socket.uid,
|
||||||
|
ip: socket.ip
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -109,22 +119,59 @@ SocketUser.checkStatus = function(socket, uid, callback) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
SocketUser.changePassword = function(socket, data, callback) {
|
SocketUser.changePassword = function(socket, data, callback) {
|
||||||
if (data && socket.uid) {
|
if (!data || !data.uid) {
|
||||||
user.changePassword(socket.uid, data, callback);
|
return callback(new Error('[[error:invalid-data]]'));
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
SocketUser.updateProfile = function(socket, data, callback) {
|
|
||||||
if (!socket.uid) {
|
if (!socket.uid) {
|
||||||
return callback('[[error:invalid-uid]]');
|
return callback('[[error:invalid-uid]]');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!data || !data.uid) {
|
user.changePassword(socket.uid, data, function(err) {
|
||||||
return callback(new Error('[[error:invalid-data]]'));
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
events.log({
|
||||||
|
type: 'password-change',
|
||||||
|
uid: socket.uid,
|
||||||
|
targetUid: data.uid,
|
||||||
|
ip: socket.ip
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
SocketUser.updateProfile = function(socket, data, callback) {
|
||||||
|
function update(oldUserData) {
|
||||||
|
function done(err, userData) {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userData.email !== oldUserData.email) {
|
||||||
|
events.log({
|
||||||
|
type: 'email-change',
|
||||||
|
uid: socket.uid,
|
||||||
|
targetUid: data.uid,
|
||||||
|
ip: socket.ip,
|
||||||
|
oldEmail: oldUserData.email,
|
||||||
|
newEmail: userData.email
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userData.username !== oldUserData.username) {
|
||||||
|
events.log({
|
||||||
|
type: 'username-change',
|
||||||
|
uid: socket.uid,
|
||||||
|
targetUid: data.uid,
|
||||||
|
ip: socket.ip,
|
||||||
|
oldUsername: oldUserData.username,
|
||||||
|
newUsername: userData.username
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (socket.uid === parseInt(data.uid, 10)) {
|
if (socket.uid === parseInt(data.uid, 10)) {
|
||||||
return user.updateProfile(socket.uid, data, callback);
|
return user.updateProfile(socket.uid, data, done);
|
||||||
}
|
}
|
||||||
|
|
||||||
user.isAdministrator(socket.uid, function(err, isAdmin) {
|
user.isAdministrator(socket.uid, function(err, isAdmin) {
|
||||||
@@ -136,7 +183,24 @@ SocketUser.updateProfile = function(socket, data, callback) {
|
|||||||
return callback(new Error('[[error:no-privileges]]'));
|
return callback(new Error('[[error:no-privileges]]'));
|
||||||
}
|
}
|
||||||
|
|
||||||
user.updateProfile(data.uid, data, callback);
|
user.updateProfile(data.uid, data, done);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!socket.uid) {
|
||||||
|
return callback('[[error:invalid-uid]]');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data || !data.uid) {
|
||||||
|
return callback(new Error('[[error:invalid-data]]'));
|
||||||
|
}
|
||||||
|
|
||||||
|
user.getUserFields(data.uid, ['email', 'username'], function(err, oldUserData) {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(oldUserData, callback);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -56,8 +56,6 @@ var winston = require('winston'),
|
|||||||
plugins.fireHook('action:topic.restore', topicData);
|
plugins.fireHook('action:topic.restore', topicData);
|
||||||
}
|
}
|
||||||
|
|
||||||
events[isDelete ? 'logTopicDelete' : 'logTopicRestore'](uid, tid);
|
|
||||||
|
|
||||||
emitTo('topic_' + tid);
|
emitTo('topic_' + tid);
|
||||||
emitTo('category_' + topicData.cid);
|
emitTo('category_' + topicData.cid);
|
||||||
|
|
||||||
@@ -214,8 +212,6 @@ var winston = require('winston'),
|
|||||||
|
|
||||||
topics.setTopicField(tid, 'cid', cid, callback);
|
topics.setTopicField(tid, 'cid', cid, callback);
|
||||||
|
|
||||||
events.logTopicMove(uid, tid);
|
|
||||||
|
|
||||||
plugins.fireHook('action:topic.move', {
|
plugins.fireHook('action:topic.move', {
|
||||||
tid: tid,
|
tid: tid,
|
||||||
fromCid: oldCid,
|
fromCid: oldCid,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ var async = require('async'),
|
|||||||
module.exports = function(User) {
|
module.exports = function(User) {
|
||||||
User.auth = {};
|
User.auth = {};
|
||||||
|
|
||||||
User.auth.logAttempt = function(uid, callback) {
|
User.auth.logAttempt = function(uid, ip, callback) {
|
||||||
db.exists('lockout:' + uid, function(err, exists) {
|
db.exists('lockout:' + uid, function(err, exists) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
@@ -33,9 +33,12 @@ module.exports = function(User) {
|
|||||||
|
|
||||||
db.delete('loginAttempts:' + uid);
|
db.delete('loginAttempts:' + uid);
|
||||||
db.pexpire('lockout:' + uid, duration);
|
db.pexpire('lockout:' + uid, duration);
|
||||||
|
events.log({
|
||||||
events.logAccountLock(uid, duration);
|
type: 'account-locked',
|
||||||
callback(new Error('account-locked'));
|
uid: uid,
|
||||||
|
ip: ip
|
||||||
|
});
|
||||||
|
callback(new Error('[[error:account-locked]]'));
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
db.pexpire('loginAttempts:' + uid, 1000 * 60 * 60);
|
db.pexpire('loginAttempts:' + uid, 1000 * 60 * 60);
|
||||||
|
|||||||
@@ -148,8 +148,6 @@ module.exports = function(User) {
|
|||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
events.logEmailChange(uid, userData.email, newEmail);
|
|
||||||
|
|
||||||
var gravatarpicture = User.createGravatarURLFromEmail(newEmail);
|
var gravatarpicture = User.createGravatarURLFromEmail(newEmail);
|
||||||
async.parallel([
|
async.parallel([
|
||||||
function(next) {
|
function(next) {
|
||||||
@@ -183,9 +181,13 @@ module.exports = function(User) {
|
|||||||
if (!newUsername) {
|
if (!newUsername) {
|
||||||
return callback();
|
return callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
User.getUserFields(uid, ['username', 'userslug'], function(err, userData) {
|
User.getUserFields(uid, ['username', 'userslug'], function(err, userData) {
|
||||||
function update(field, object, value, callback) {
|
function update(field, object, value, callback) {
|
||||||
async.parallel([
|
async.series([
|
||||||
|
function(next) {
|
||||||
|
db.deleteObjectField(field + ':uid', userData[field], next);
|
||||||
|
},
|
||||||
function(next) {
|
function(next) {
|
||||||
User.setUserField(uid, field, value, next);
|
User.setUserField(uid, field, value, next);
|
||||||
},
|
},
|
||||||
@@ -198,7 +200,6 @@ module.exports = function(User) {
|
|||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
var userslug = utils.slugify(newUsername);
|
|
||||||
|
|
||||||
async.parallel([
|
async.parallel([
|
||||||
function(next) {
|
function(next) {
|
||||||
@@ -206,25 +207,15 @@ module.exports = function(User) {
|
|||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
db.deleteObjectField('username:uid', userData.username, function(err) {
|
|
||||||
if (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
events.logUsernameChange(uid, userData.username, newUsername);
|
|
||||||
update('username', 'username:uid', newUsername, next);
|
update('username', 'username:uid', newUsername, next);
|
||||||
});
|
|
||||||
},
|
},
|
||||||
function(next) {
|
function(next) {
|
||||||
if (userslug === userData.userslug) {
|
var newUserslug = utils.slugify(newUsername);
|
||||||
|
if (newUserslug === userData.userslug) {
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
db.deleteObjectField('userslug:uid', userData.userslug, function(err) {
|
update('userslug', 'userslug:uid', newUserslug, next);
|
||||||
if (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
update('userslug', 'userslug:uid', userslug, next);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
], callback);
|
], callback);
|
||||||
});
|
});
|
||||||
@@ -265,19 +256,7 @@ module.exports = function(User) {
|
|||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
User.setUserField(data.uid, 'password', hash, function(err) {
|
User.setUserField(data.uid, 'password', hash, callback);
|
||||||
if(err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(parseInt(uid, 10) === parseInt(data.uid, 10)) {
|
|
||||||
events.logPasswordChange(data.uid);
|
|
||||||
} else {
|
|
||||||
events.logAdminChangeUserPassword(uid, data.uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -83,7 +83,6 @@ var async = require('async'),
|
|||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
user.setUserField(uid, 'password', hash);
|
user.setUserField(uid, 'password', hash);
|
||||||
events.logPasswordReset(uid);
|
|
||||||
|
|
||||||
db.deleteObjectField('reset:uid', code);
|
db.deleteObjectField('reset:uid', code);
|
||||||
db.deleteObjectField('reset:expiry', code);
|
db.deleteObjectField('reset:expiry', code);
|
||||||
|
|||||||
@@ -1,31 +1,32 @@
|
|||||||
<div class="events">
|
<div class="events">
|
||||||
<div class="col-sm-9">
|
<div class="col-lg-9">
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading"><i class="fa fa-calendar-o"></i> Events</div>
|
<div class="panel-heading"><i class="fa fa-calendar-o"></i> Events</div>
|
||||||
<div class="panel-body" data-next="{next}">
|
<div class="panel-body" data-next="{next}">
|
||||||
<pre>{eventdata}</pre>
|
<!-- IF !events.length -->
|
||||||
|
<div class="alert alert-info">There are no events</div>
|
||||||
|
<!-- ENDIF !events.length -->
|
||||||
|
<div class="events-list">
|
||||||
|
<!-- BEGIN events -->
|
||||||
|
<div>
|
||||||
|
<span>#{events.eid} </span><span class="label label-info">{events.type}</span>
|
||||||
|
<a href="{config.relative_path}/user/{events.user.userslug}"><img class="user-img" src="{events.user.picture}"/></a> <a href="{config.relative_path}/user/{events.user.userslug}">{events.user.username}</a> (uid {events.user.uid}) (IP {events.ip})
|
||||||
|
<span class="pull-right">{events.timestampISO}</span>
|
||||||
|
<br/><br/>
|
||||||
|
<pre>{events.jsonString}</pre>
|
||||||
|
</div>
|
||||||
|
<!-- END events -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-3">
|
||||||
|
<div class="panel panel-default affix">
|
||||||
|
<div class="panel-heading">Events Control Panel</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<button class="btn btn-warning" data-action="clear"><i class="fa fa-eraser"></i> Delete Events</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<script>
|
|
||||||
require(['forum/infinitescroll'], function(infinitescroll) {
|
|
||||||
|
|
||||||
infinitescroll.init(function(direction) {
|
|
||||||
if (direction < 0 || !$('.events').length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
infinitescroll.loadMore('admin.getMoreEvents', $('[data-next]').attr('data-next'), function(events, done) {
|
|
||||||
if (events.data && events.data.length) {
|
|
||||||
$('.panel-body pre').append(events.data);
|
|
||||||
$('[data-next]').attr('data-next', events.next);
|
|
||||||
}
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
|
||||||
Reference in New Issue
Block a user