diff --git a/public/less/admin/general/dashboard.less b/public/less/admin/general/dashboard.less index 1001893ed7..ebf65e9fd0 100644 --- a/public/less/admin/general/dashboard.less +++ b/public/less/admin/general/dashboard.less @@ -38,7 +38,7 @@ li { float: left; - width: 48%; + width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -90,7 +90,7 @@ border-color: #46BFBD; background-color: #5AD3D1; } - &.on-homepage { + &.on-categories { border-color: #F7464A; background-color: #FF5A5E; } diff --git a/public/src/admin/general/dashboard.js b/public/src/admin/general/dashboard.js index 7a2243465d..40ace1ecc1 100644 --- a/public/src/admin/general/dashboard.js +++ b/public/src/admin/general/dashboard.js @@ -141,10 +141,10 @@ define('admin/general/dashboard', ['semver'], function(semver) { '
Connections
' + ''; - var idle = data.socketCount - (data.users.home + data.users.topics + data.users.category); + var idle = data.socketCount - (data.users.categories + data.users.topics + data.users.category); updateRegisteredGraph(data.onlineRegisteredCount, data.onlineGuestCount); - updatePresenceGraph(data.users.home, data.users.topics, data.users.category, idle); + updatePresenceGraph(data.users.categories, data.users.topics, data.users.category, idle); updateTopicsGraph(data.topics); $('#active-users').html(html); @@ -266,7 +266,7 @@ define('admin/general/dashboard', ['semver'], function(semver) { value: 1, color:"#F7464A", highlight: "#FF5A5E", - label: "On homepage" + label: "On categories list" }, { value: 1, @@ -345,8 +345,8 @@ define('admin/general/dashboard', ['semver'], function(semver) { graphs.registered.update(); } - function updatePresenceGraph(homepage, posts, topics, idle) { - graphs.presence.segments[0].value = homepage; + function updatePresenceGraph(categories, posts, topics, idle) { + graphs.presence.segments[0].value = categories; graphs.presence.segments[1].value = posts; graphs.presence.segments[2].value = topics; graphs.presence.segments[3].value = idle; @@ -424,6 +424,9 @@ define('admin/general/dashboard', ['semver'], function(semver) { function buildTopicsLegend() { var legend = $('#topics-legend').html(''); + segments.sort(function(a, b) { + return b.value - a.value; + }); for (var i = 0, ii = segments.length; i < ii; i++) { var topic = segments[i], label = topic.tid === '0' ? topic.label : ' ' + topic.label + ''; diff --git a/public/src/app.js b/public/src/app.js index 8974d2316a..dadf141702 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -105,8 +105,8 @@ app.cacheBuster = null; case 'admin': room = 'admin'; break; - case 'home': - room = 'home'; + case 'categories': + room = 'categories'; break; } app.currentRoom = ''; diff --git a/src/socket.io/index.js b/src/socket.io/index.js index 337320e226..e0fd83f7b7 100644 --- a/src/socket.io/index.js +++ b/src/socket.io/index.js @@ -12,6 +12,7 @@ var SocketIO = require('socket.io'), user = require('../user'), logger = require('../logger'), ratelimit = require('../middleware/ratelimit'), + rooms = require('./rooms'), Sockets = {}, Namespaces = {}; @@ -63,8 +64,8 @@ function onConnection(socket) { function onConnect(socket) { if (socket.uid) { - socket.join('uid_' + socket.uid); - socket.join('online_users'); + rooms.enter(socket, 'uid_' + socket.uid); + rooms.enter(socket, 'online_users'); user.getUserFields(socket.uid, ['status'], function(err, userData) { if (err || !userData) { @@ -77,7 +78,7 @@ function onConnect(socket) { } }); } else { - socket.join('online_guests'); + rooms.enter(socket, 'online_guests'); socket.emit('event:connect'); } } @@ -85,7 +86,7 @@ function onConnect(socket) { function onDisconnect(socket, data) { if (socket.uid) { var socketCount = Sockets.getUserSocketCount(socket.uid); - if (socketCount <= 0) { + if (socketCount <= 1) { socket.broadcast.emit('event:user_status_change', {uid: socket.uid, status: 'offline'}); } @@ -96,6 +97,7 @@ function onDisconnect(socket, data) { } }); } + rooms.leaveAll(socket, data.rooms); } function onMessage(socket, payload) { @@ -183,7 +185,7 @@ function authorize(socket, callback) { socket.uid = parseInt(sessionData.passport.user, 10); } else { socket.uid = 0; - } + } next(); }); } @@ -218,27 +220,25 @@ Sockets.in = function(room) { }; Sockets.getSocketCount = function() { - // TODO: io.sockets.adapter.sids is local to this worker - // use redis-adapter - - var clients = Object.keys(io.sockets.adapter.sids || {}); - return Array.isArray(clients) ? clients.length : 0; + return rooms.socketCount(); }; Sockets.getUserSocketCount = function(uid) { - // TODO: io.sockets.adapter.rooms is local to this worker - // use .clients('uid_' + uid, fn) + return rooms.clients('uid_' + uid).length; +}; - var roomClients = Object.keys(io.sockets.adapter.rooms['uid_' + uid] || {}); - return Array.isArray(roomClients) ? roomClients.length : 0; +Sockets.getOnlineUserCount = function() { + var count = 0; + Object.keys(rooms.roomClients()).forEach(function(roomName) { + if (roomName.startsWith('uid_')) { + ++ count; + } + }); + return count; }; Sockets.getOnlineAnonCount = function () { - // TODO: io.sockets.adapter.rooms is local to this worker - // use .clients() - - var guestSocketIds = Object.keys(io.sockets.adapter.rooms.online_guests || {}); - return Array.isArray(guestSocketIds) ? guestSocketIds.length : 0; + return rooms.clients('online_guests').length; }; Sockets.reqFromSocket = function(socket) { @@ -258,9 +258,7 @@ Sockets.reqFromSocket = function(socket) { }; Sockets.isUserOnline = function(uid) { - // TODO: io.sockets.adapter.rooms is local to this worker - // use .clients('uid_' + uid, fn) - return io ? !!io.sockets.adapter.rooms['uid_' + uid] : false; + return !!rooms.clients('uid_' + uid).length; }; Sockets.isUsersOnline = function(uids, callback) { @@ -301,26 +299,29 @@ Sockets.getUsersInRoom = function (uid, roomName, callback) { Sockets.getUidsInRoom = function(roomName, callback) { callback = callback || function() {}; - // TODO : doesnt work in cluster var uids = []; - var socketids = Object.keys(io.sockets.adapter.rooms[roomName] || {}); + var socketids = rooms.clients(roomName); if (!Array.isArray(socketids) || !socketids.length) { callback(null, []); return []; } for(var i=0; i 0) { + topTenTopics = topTenTopics.concat(scores[mostActive.pop()]); + } + + topTenTopics = topTenTopics.slice(0, 10); + + topics.getTopicsFields(topTenTopics, ['title'], function(err, titles) { if (err) { return callback(err); } - - var rooms = {}; // TODO: websockets.server.sockets.manager.rooms; doesnt work in socket.io 1.x - var socketData = { - onlineGuestCount: websockets.getOnlineAnonCount(), - onlineRegisteredCount: onlineRegisteredCount, - socketCount: websockets.getSocketCount(), - users: { - home: rooms['/home'] ? rooms['/home'].length : 0, - topics: 0, - category: 0 - }, - topics: {} + topTenTopics.forEach(function(tid, id) { + socketData.topics[tid] = { + value: Array.isArray(roomClients['topic_' + tid]) ? roomClients['topic_' + tid].length : 0, + title: validator.escape(titles[id].title) }; - - var scores = {}, - topTenTopics = [], - tid; - - for (var room in rooms) { - if (rooms.hasOwnProperty(room)) { - if (tid = room.match(/^\/topic_(\d+)/)) { - var length = rooms[room].length; - socketData.users.topics += length; - - if (scores[length]) { - scores[length].push(tid[1]); - } else { - scores[length] = [tid[1]]; - } - } else if (room.match(/^\/category/)) { - socketData.users.category += rooms[room].length; - } - } - } - - var scoreKeys = Object.keys(scores), - mostActive = scoreKeys.sort(); - - while(topTenTopics.length < 10 && mostActive.length > 0) { - topTenTopics = topTenTopics.concat(scores[mostActive.pop()]); - } - - topTenTopics = topTenTopics.slice(0, 10); - - topics.getTopicsFields(topTenTopics, ['title'], function(err, titles) { - if (err) { - return callback(err); - } - topTenTopics.forEach(function(tid, id) { - socketData.topics[tid] = { - value: rooms['/topic_' + tid].length, - title: validator.escape(titles[id].title) - }; - }); - - callback(null, socketData); }); + + callback(null, socketData); }); + }; /* Exports */ diff --git a/src/socket.io/rooms.js b/src/socket.io/rooms.js new file mode 100644 index 0000000000..e392abf3f9 --- /dev/null +++ b/src/socket.io/rooms.js @@ -0,0 +1,95 @@ +'use strict'; + + +// Temp solution until +// https://github.com/NodeBB/NodeBB/issues/2486 +// and +// https://github.com/Automattic/socket.io/issues/1945 +// are closed. +// Once they are closed switch to .clients() and async calls + + +var pubsub = require('../pubsub'); + +var rooms = {}; + +var clientRooms = {}; +var roomClients = {}; + +rooms.enter = function(socket, room) { + socket.join(room); + pubsub.publish('socket:join', {id: socket.id, room: room}); +}; + +rooms.leave = function(socket, room) { + socket.leave(room); + pubsub.publish('socket:leave', {id: socket.id, room: room}); +}; + +rooms.leaveAll = function(socket, roomsToLeave) { + roomsToLeave.forEach(function(room) { + rooms.leave(socket, room); + }); +}; + +pubsub.on('socket:join', onSocketJoin); +pubsub.on('socket:leave', onSocketLeave); + +function onSocketJoin(data) { + clientRooms[data.id] = clientRooms[data.id] || []; + if (clientRooms[data.id].indexOf(data.room) === -1) { + clientRooms[data.id].push(data.room); + } + + roomClients[data.room] = roomClients[data.room] || []; + if (roomClients[data.room].indexOf(data.id) === -1) { + roomClients[data.room].push(data.id); + } +} + + +function onSocketLeave(data) { + var index; + if (Array.isArray(clientRooms[data.id])) { + index = clientRooms[data.id].indexOf(data.room); + if (index !== -1) { + clientRooms[data.id].splice(index, 1); + if (!clientRooms[data.id].length) { + delete clientRooms[data.id]; + } + } + } + + if (Array.isArray(roomClients[data.room])) { + index = roomClients[data.room].indexOf(data.id); + if (index !== -1) { + roomClients[data.room].splice(index, 1); + if (!roomClients[data.room].length) { + delete roomClients[data.room]; + } + } + } +} + + +rooms.clients = function(room) { + return Array.isArray(roomClients[room]) ? roomClients[room] : []; +}; + +rooms.clientRooms = function(id) { + return Array.isArray(clientRooms[id]) ? clientRooms[id] : []; +}; + +rooms.socketCount = function() { + return Object.keys(clientRooms || {}).length; +}; + +rooms.roomClients = function() { + return roomClients; +}; + + + + +module.exports = rooms; + diff --git a/src/views/admin/general/dashboard.tpl b/src/views/admin/general/dashboard.tpl index 759144152d..94f9125fab 100644 --- a/src/views/admin/general/dashboard.tpl +++ b/src/views/admin/general/dashboard.tpl @@ -1,6 +1,5 @@
- -
+
Forum Traffic
@@ -88,8 +87,8 @@
- -
+ +
Anonymous vs Registered Users
@@ -108,7 +107,7 @@
    -
  • On Homepage
  • +
  • On categories list
  • Reading posts
  • Browsing topics
  • Idle