/* jslint node: true */ 'use strict'; // ENiGMA½ const logger = require('./logger.js'); const Events = require('./events.js'); const UserProps = require('./user_property.js'); // deps const _ = require('lodash'); const moment = require('moment'); const hashids = require('hashids/cjs'); exports.getActiveConnections = getActiveConnections; exports.getActiveConnectionList = getActiveConnectionList; exports.addNewClient = addNewClient; exports.removeClient = removeClient; exports.getConnectionByUserId = getConnectionByUserId; exports.getConnectionByNodeId = getConnectionByNodeId; const clientConnections = []; exports.clientConnections = clientConnections; function getActiveConnections(authUsersOnly = false) { return clientConnections.filter(conn => { return ((authUsersOnly && conn.user.isAuthenticated()) || !authUsersOnly); }); } function getActiveConnectionList(authUsersOnly) { if(!_.isBoolean(authUsersOnly)) { authUsersOnly = true; } const now = moment(); return _.map(getActiveConnections(authUsersOnly), ac => { let action; try { // attempting to fetch a bad menu stack item can blow up/assert action = _.get(ac, 'currentMenuModule.menuConfig.desc', 'Unknown'); } catch(e) { action = 'Unknown'; } const entry = { node : ac.node, authenticated : ac.user.isAuthenticated(), userId : ac.user.userId, action : action, serverName : ac.session.serverName, isSecure : ac.session.isSecure, }; // // There may be a connection, but not a logged in user as of yet // if(ac.user.isAuthenticated()) { entry.text = ac.user.username; entry.userName = ac.user.username; entry.realName = ac.user.properties[UserProps.RealName]; entry.location = ac.user.properties[UserProps.Location]; entry.affils = entry.affiliation = ac.user.properties[UserProps.Affiliations]; const diff = now.diff(moment(ac.user.properties[UserProps.LastLoginTs]), 'minutes'); entry.timeOn = moment.duration(diff, 'minutes'); } return entry; }); } function addNewClient(client, clientSock) { // // Find a node ID "slot" // let nodeId; for (nodeId = 1; nodeId < Number.MAX_SAFE_INTEGER; ++nodeId) { const existing = clientConnections.find(client => nodeId === client.node); if (!existing) { break; // available slot } } client.session.id = nodeId; const remoteAddress = client.remoteAddress = clientSock.remoteAddress; // create a unique identifier one-time ID for this session client.session.uniqueId = new hashids('ENiGMA½ClientSession').encode([ nodeId, moment().valueOf() ]); clientConnections.push(client); clientConnections.sort( (c1, c2) => c1.session.id - c2.session.id); // Create a client specific logger // Note that this will be updated @ login with additional information client.log = logger.log.child( { nodeId, sessionId : client.session.uniqueId } ); const connInfo = { remoteAddress : remoteAddress, serverName : client.session.serverName, isSecure : client.session.isSecure, }; if(client.log.debug()) { connInfo.port = clientSock.localPort; connInfo.family = clientSock.localFamily; } client.log.info(connInfo, 'Client connected'); Events.emit( Events.getSystemEvents().ClientConnected, { client : client, connectionCount : clientConnections.length } ); return nodeId; } function removeClient(client) { client.end(); const i = clientConnections.indexOf(client); if(i > -1) { clientConnections.splice(i, 1); logger.log.info( { connectionCount : clientConnections.length, nodeId : client.node, }, 'Client disconnected' ); if(client.user && client.user.isValid()) { const minutesOnline = moment().diff(moment(client.user.properties[UserProps.LastLoginTs]), 'minutes'); Events.emit(Events.getSystemEvents().UserLogoff, { user : client.user, minutesOnline } ); } Events.emit( Events.getSystemEvents().ClientDisconnected, { client : client, connectionCount : clientConnections.length } ); } } function getConnectionByUserId(userId) { return getActiveConnections().find( ac => userId === ac.user.userId ); } function getConnectionByNodeId(nodeId) { return getActiveConnections().find( ac => nodeId == ac.node ); }