mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-06 05:55:48 +01:00
various fixes for socket.io cluster display user presence correctly
This commit is contained in:
@@ -38,7 +38,7 @@
|
|||||||
|
|
||||||
li {
|
li {
|
||||||
float: left;
|
float: left;
|
||||||
width: 48%;
|
width: 100%;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
@@ -90,7 +90,7 @@
|
|||||||
border-color: #46BFBD;
|
border-color: #46BFBD;
|
||||||
background-color: #5AD3D1;
|
background-color: #5AD3D1;
|
||||||
}
|
}
|
||||||
&.on-homepage {
|
&.on-categories {
|
||||||
border-color: #F7464A;
|
border-color: #F7464A;
|
||||||
background-color: #FF5A5E;
|
background-color: #FF5A5E;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,10 +141,10 @@ define('admin/general/dashboard', ['semver'], function(semver) {
|
|||||||
'<div>Connections</div>' +
|
'<div>Connections</div>' +
|
||||||
'</div>';
|
'</div>';
|
||||||
|
|
||||||
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);
|
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);
|
updateTopicsGraph(data.topics);
|
||||||
|
|
||||||
$('#active-users').html(html);
|
$('#active-users').html(html);
|
||||||
@@ -266,7 +266,7 @@ define('admin/general/dashboard', ['semver'], function(semver) {
|
|||||||
value: 1,
|
value: 1,
|
||||||
color:"#F7464A",
|
color:"#F7464A",
|
||||||
highlight: "#FF5A5E",
|
highlight: "#FF5A5E",
|
||||||
label: "On homepage"
|
label: "On categories list"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 1,
|
value: 1,
|
||||||
@@ -345,8 +345,8 @@ define('admin/general/dashboard', ['semver'], function(semver) {
|
|||||||
graphs.registered.update();
|
graphs.registered.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
function updatePresenceGraph(homepage, posts, topics, idle) {
|
function updatePresenceGraph(categories, posts, topics, idle) {
|
||||||
graphs.presence.segments[0].value = homepage;
|
graphs.presence.segments[0].value = categories;
|
||||||
graphs.presence.segments[1].value = posts;
|
graphs.presence.segments[1].value = posts;
|
||||||
graphs.presence.segments[2].value = topics;
|
graphs.presence.segments[2].value = topics;
|
||||||
graphs.presence.segments[3].value = idle;
|
graphs.presence.segments[3].value = idle;
|
||||||
@@ -424,6 +424,9 @@ define('admin/general/dashboard', ['semver'], function(semver) {
|
|||||||
function buildTopicsLegend() {
|
function buildTopicsLegend() {
|
||||||
var legend = $('#topics-legend').html('');
|
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++) {
|
for (var i = 0, ii = segments.length; i < ii; i++) {
|
||||||
var topic = segments[i],
|
var topic = segments[i],
|
||||||
label = topic.tid === '0' ? topic.label : '<a title="' + topic.label + '"href="' + RELATIVE_PATH + '/topic/' + topic.tid + '" target="_blank"> ' + topic.label + '</a>';
|
label = topic.tid === '0' ? topic.label : '<a title="' + topic.label + '"href="' + RELATIVE_PATH + '/topic/' + topic.tid + '" target="_blank"> ' + topic.label + '</a>';
|
||||||
|
|||||||
@@ -105,8 +105,8 @@ app.cacheBuster = null;
|
|||||||
case 'admin':
|
case 'admin':
|
||||||
room = 'admin';
|
room = 'admin';
|
||||||
break;
|
break;
|
||||||
case 'home':
|
case 'categories':
|
||||||
room = 'home';
|
room = 'categories';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
app.currentRoom = '';
|
app.currentRoom = '';
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ var SocketIO = require('socket.io'),
|
|||||||
user = require('../user'),
|
user = require('../user'),
|
||||||
logger = require('../logger'),
|
logger = require('../logger'),
|
||||||
ratelimit = require('../middleware/ratelimit'),
|
ratelimit = require('../middleware/ratelimit'),
|
||||||
|
rooms = require('./rooms'),
|
||||||
|
|
||||||
Sockets = {},
|
Sockets = {},
|
||||||
Namespaces = {};
|
Namespaces = {};
|
||||||
@@ -63,8 +64,8 @@ function onConnection(socket) {
|
|||||||
|
|
||||||
function onConnect(socket) {
|
function onConnect(socket) {
|
||||||
if (socket.uid) {
|
if (socket.uid) {
|
||||||
socket.join('uid_' + socket.uid);
|
rooms.enter(socket, 'uid_' + socket.uid);
|
||||||
socket.join('online_users');
|
rooms.enter(socket, 'online_users');
|
||||||
|
|
||||||
user.getUserFields(socket.uid, ['status'], function(err, userData) {
|
user.getUserFields(socket.uid, ['status'], function(err, userData) {
|
||||||
if (err || !userData) {
|
if (err || !userData) {
|
||||||
@@ -77,7 +78,7 @@ function onConnect(socket) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
socket.join('online_guests');
|
rooms.enter(socket, 'online_guests');
|
||||||
socket.emit('event:connect');
|
socket.emit('event:connect');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,7 +86,7 @@ function onConnect(socket) {
|
|||||||
function onDisconnect(socket, data) {
|
function onDisconnect(socket, data) {
|
||||||
if (socket.uid) {
|
if (socket.uid) {
|
||||||
var socketCount = Sockets.getUserSocketCount(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'});
|
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) {
|
function onMessage(socket, payload) {
|
||||||
@@ -218,27 +220,25 @@ Sockets.in = function(room) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Sockets.getSocketCount = function() {
|
Sockets.getSocketCount = function() {
|
||||||
// TODO: io.sockets.adapter.sids is local to this worker
|
return rooms.socketCount();
|
||||||
// use redis-adapter
|
|
||||||
|
|
||||||
var clients = Object.keys(io.sockets.adapter.sids || {});
|
|
||||||
return Array.isArray(clients) ? clients.length : 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Sockets.getUserSocketCount = function(uid) {
|
Sockets.getUserSocketCount = function(uid) {
|
||||||
// TODO: io.sockets.adapter.rooms is local to this worker
|
return rooms.clients('uid_' + uid).length;
|
||||||
// use .clients('uid_' + uid, fn)
|
};
|
||||||
|
|
||||||
var roomClients = Object.keys(io.sockets.adapter.rooms['uid_' + uid] || {});
|
Sockets.getOnlineUserCount = function() {
|
||||||
return Array.isArray(roomClients) ? roomClients.length : 0;
|
var count = 0;
|
||||||
|
Object.keys(rooms.roomClients()).forEach(function(roomName) {
|
||||||
|
if (roomName.startsWith('uid_')) {
|
||||||
|
++ count;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return count;
|
||||||
};
|
};
|
||||||
|
|
||||||
Sockets.getOnlineAnonCount = function () {
|
Sockets.getOnlineAnonCount = function () {
|
||||||
// TODO: io.sockets.adapter.rooms is local to this worker
|
return rooms.clients('online_guests').length;
|
||||||
// use .clients()
|
|
||||||
|
|
||||||
var guestSocketIds = Object.keys(io.sockets.adapter.rooms.online_guests || {});
|
|
||||||
return Array.isArray(guestSocketIds) ? guestSocketIds.length : 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Sockets.reqFromSocket = function(socket) {
|
Sockets.reqFromSocket = function(socket) {
|
||||||
@@ -258,9 +258,7 @@ Sockets.reqFromSocket = function(socket) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Sockets.isUserOnline = function(uid) {
|
Sockets.isUserOnline = function(uid) {
|
||||||
// TODO: io.sockets.adapter.rooms is local to this worker
|
return !!rooms.clients('uid_' + uid).length;
|
||||||
// use .clients('uid_' + uid, fn)
|
|
||||||
return io ? !!io.sockets.adapter.rooms['uid_' + uid] : false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Sockets.isUsersOnline = function(uids, callback) {
|
Sockets.isUsersOnline = function(uids, callback) {
|
||||||
@@ -301,26 +299,29 @@ Sockets.getUsersInRoom = function (uid, roomName, callback) {
|
|||||||
|
|
||||||
Sockets.getUidsInRoom = function(roomName, callback) {
|
Sockets.getUidsInRoom = function(roomName, callback) {
|
||||||
callback = callback || function() {};
|
callback = callback || function() {};
|
||||||
// TODO : doesnt work in cluster
|
|
||||||
|
|
||||||
var uids = [];
|
var uids = [];
|
||||||
|
|
||||||
var socketids = Object.keys(io.sockets.adapter.rooms[roomName] || {});
|
var socketids = rooms.clients(roomName);
|
||||||
if (!Array.isArray(socketids) || !socketids.length) {
|
if (!Array.isArray(socketids) || !socketids.length) {
|
||||||
callback(null, []);
|
callback(null, []);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
for(var i=0; i<socketids.length; ++i) {
|
for(var i=0; i<socketids.length; ++i) {
|
||||||
var socketRooms = Object.keys(io.sockets.adapter.sids[socketids[i]]);
|
var socketRooms = rooms.clientRooms(socketids[i]);
|
||||||
if (Array.isArray(socketRooms)) {
|
if (Array.isArray(socketRooms)) {
|
||||||
socketRooms.forEach(function(roomName) {
|
socketRooms.forEach(function(roomName) {
|
||||||
if (roomName.indexOf('uid_') === 0 ) {
|
if (roomName.startsWith('uid_')) {
|
||||||
uids.push(roomName.split('_')[1]);
|
var uid = roomName.split('_')[1];
|
||||||
|
if (uids.indexOf(uid) === -1) {
|
||||||
|
uids.push(uid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(null, uids);
|
callback(null, uids);
|
||||||
return uids;
|
return uids;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ var nconf = require('nconf'),
|
|||||||
logger = require('../logger'),
|
logger = require('../logger'),
|
||||||
plugins = require('../plugins'),
|
plugins = require('../plugins'),
|
||||||
emitter = require('../emitter'),
|
emitter = require('../emitter'),
|
||||||
|
rooms = require('./rooms'),
|
||||||
|
|
||||||
websockets = require('./'),
|
websockets = require('./'),
|
||||||
|
|
||||||
@@ -58,7 +59,7 @@ SocketMeta.rooms.enter = function(socket, data, callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (socket.currentRoom) {
|
if (socket.currentRoom) {
|
||||||
socket.leave(socket.currentRoom);
|
rooms.leave(socket, socket.currentRoom);
|
||||||
if (socket.currentRoom.indexOf('topic') !== -1) {
|
if (socket.currentRoom.indexOf('topic') !== -1) {
|
||||||
websockets.in(socket.currentRoom).emit('event:user_leave', socket.uid);
|
websockets.in(socket.currentRoom).emit('event:user_leave', socket.uid);
|
||||||
}
|
}
|
||||||
@@ -66,7 +67,7 @@ SocketMeta.rooms.enter = function(socket, data, callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (data.enter) {
|
if (data.enter) {
|
||||||
socket.join(data.enter);
|
rooms.enter(socket, data.enter);
|
||||||
socket.currentRoom = data.enter;
|
socket.currentRoom = data.enter;
|
||||||
if (data.enter.indexOf('topic') !== -1) {
|
if (data.enter.indexOf('topic') !== -1) {
|
||||||
data.uid = socket.uid;
|
data.uid = socket.uid;
|
||||||
@@ -80,19 +81,13 @@ SocketMeta.rooms.enter = function(socket, data, callback) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
SocketMeta.rooms.getAll = function(socket, data, callback) {
|
SocketMeta.rooms.getAll = function(socket, data, callback) {
|
||||||
var now = Date.now();
|
var roomClients = rooms.roomClients();
|
||||||
db.sortedSetCount('users:online', now - 300000, now, function(err, onlineRegisteredCount) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
var rooms = {}; // TODO: websockets.server.sockets.manager.rooms; doesnt work in socket.io 1.x
|
|
||||||
var socketData = {
|
var socketData = {
|
||||||
onlineGuestCount: websockets.getOnlineAnonCount(),
|
onlineGuestCount: websockets.getOnlineAnonCount(),
|
||||||
onlineRegisteredCount: onlineRegisteredCount,
|
onlineRegisteredCount: websockets.getOnlineUserCount(),
|
||||||
socketCount: websockets.getSocketCount(),
|
socketCount: websockets.getSocketCount(),
|
||||||
users: {
|
users: {
|
||||||
home: rooms['/home'] ? rooms['/home'].length : 0,
|
categories: roomClients.categories ? roomClients.categories.length : 0,
|
||||||
topics: 0,
|
topics: 0,
|
||||||
category: 0
|
category: 0
|
||||||
},
|
},
|
||||||
@@ -103,10 +98,11 @@ SocketMeta.rooms.getAll = function(socket, data, callback) {
|
|||||||
topTenTopics = [],
|
topTenTopics = [],
|
||||||
tid;
|
tid;
|
||||||
|
|
||||||
for (var room in rooms) {
|
for (var room in roomClients) {
|
||||||
if (rooms.hasOwnProperty(room)) {
|
if (roomClients.hasOwnProperty(room)) {
|
||||||
if (tid = room.match(/^\/topic_(\d+)/)) {
|
tid = room.match(/^topic_(\d+)/);
|
||||||
var length = rooms[room].length;
|
if (tid) {
|
||||||
|
var length = roomClients[room].length;
|
||||||
socketData.users.topics += length;
|
socketData.users.topics += length;
|
||||||
|
|
||||||
if (scores[length]) {
|
if (scores[length]) {
|
||||||
@@ -114,8 +110,8 @@ SocketMeta.rooms.getAll = function(socket, data, callback) {
|
|||||||
} else {
|
} else {
|
||||||
scores[length] = [tid[1]];
|
scores[length] = [tid[1]];
|
||||||
}
|
}
|
||||||
} else if (room.match(/^\/category/)) {
|
} else if (room.match(/^category/)) {
|
||||||
socketData.users.category += rooms[room].length;
|
socketData.users.category += roomClients[room].length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -135,14 +131,14 @@ SocketMeta.rooms.getAll = function(socket, data, callback) {
|
|||||||
}
|
}
|
||||||
topTenTopics.forEach(function(tid, id) {
|
topTenTopics.forEach(function(tid, id) {
|
||||||
socketData.topics[tid] = {
|
socketData.topics[tid] = {
|
||||||
value: rooms['/topic_' + tid].length,
|
value: Array.isArray(roomClients['topic_' + tid]) ? roomClients['topic_' + tid].length : 0,
|
||||||
title: validator.escape(titles[id].title)
|
title: validator.escape(titles[id].title)
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
callback(null, socketData);
|
callback(null, socketData);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Exports */
|
/* Exports */
|
||||||
|
|||||||
95
src/socket.io/rooms.js
Normal file
95
src/socket.io/rooms.js
Normal file
@@ -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;
|
||||||
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
<div class="row dashboard">
|
<div class="row dashboard">
|
||||||
<!-- Override for now, until the right sidebar graphs are fixed (pending socket.io resolution) -->
|
<div class="col-lg-9">
|
||||||
<div class="col-lg-12">
|
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">Forum Traffic</div>
|
<div class="panel-heading">Forum Traffic</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
@@ -88,8 +87,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Override for now, until the right sidebar graphs are fixed (pending socket.io resolution) -->
|
|
||||||
<div class="col-lg-3 hide">
|
<div class="col-lg-3">
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">Anonymous vs Registered Users</div>
|
<div class="panel-heading">Anonymous vs Registered Users</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
@@ -108,7 +107,7 @@
|
|||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div class="graph-container pie-chart legend-up">
|
<div class="graph-container pie-chart legend-up">
|
||||||
<ul class="graph-legend">
|
<ul class="graph-legend">
|
||||||
<li><div class="on-homepage"></div><span>On Homepage</span></li>
|
<li><div class="on-categories"></div><span>On categories list</span></li>
|
||||||
<li><div class="reading-posts"></div><span>Reading posts</span></li>
|
<li><div class="reading-posts"></div><span>Reading posts</span></li>
|
||||||
<li><div class="browsing-topics"></div><span>Browsing topics</span></li>
|
<li><div class="browsing-topics"></div><span>Browsing topics</span></li>
|
||||||
<li><div class="idle"></div><span>Idle</span></li>
|
<li><div class="idle"></div><span>Idle</span></li>
|
||||||
|
|||||||
Reference in New Issue
Block a user