io.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. 'use strict';
  2. // This file contains all the logic for Socket.IO
  3. const app = require('./app');
  4. const actions = require('./actions');
  5. const async = require('async');
  6. const config = require('config');
  7. const cache = require('./cache');
  8. const utils = require('./utils');
  9. const db = require('./db');
  10. const logger = require('./logger');
  11. const punishments = require('./punishments');
  12. let initialized = false;
  13. let lockdown = false;
  14. module.exports = {
  15. io: null,
  16. init: (cb) => {
  17. //TODO Check every 30s/60s, for all sockets, if they are still allowed to be in the rooms they are in, and on socket at all (permission changing/banning)
  18. this.io = require('socket.io')(app.server);
  19. this.io.use((socket, next) => {
  20. if (lockdown) return;
  21. let cookies = socket.request.headers.cookie;
  22. let SID = utils.cookies.parseCookies(cookies)[config.cookie.sidName];
  23. socket.ip = socket.request.headers['x-forwarded-for'] || '0.0.0.0';
  24. async.waterfall([
  25. (next) => {
  26. if (!SID) return next('No SID.');
  27. next();
  28. },
  29. (next) => {
  30. cache.hget('sessions', SID, next);
  31. },
  32. (session, next) => {
  33. if (!session) return next('No session found.');
  34. session.refreshDate = Date.now();
  35. socket.session = session;
  36. cache.hset('sessions', SID, session, next);
  37. },
  38. (res, next) => {
  39. punishments.getPunishments((err, punishments) => {
  40. const isLoggedIn = !!(socket.session && socket.session.refreshDate);
  41. const userId = (isLoggedIn) ? socket.session.userId : null;
  42. let ban = 0;
  43. let banned = false;
  44. punishments.forEach(punishment => {
  45. if (punishment.expiresAt > ban) ban = punishment;
  46. if (punishment.type === 'banUserId' && isLoggedIn && punishment.value === userId) banned = true;
  47. if (punishment.type === 'banUserIp' && punishment.value === socket.ip) banned = true;
  48. });
  49. socket.banned = banned;
  50. socket.ban = ban;
  51. next();
  52. });
  53. }
  54. ], () => {
  55. if (!socket.session) {
  56. socket.session = { socketId: socket.id };
  57. } else socket.session.socketId = socket.id;
  58. next();
  59. });
  60. });
  61. this.io.on('connection', socket => {
  62. if (lockdown) return socket.disconnect(true);
  63. let sessionInfo = '';
  64. if (socket.session.sessionId) sessionInfo = ` UserID: ${socket.session.userId}.`;
  65. if (socket.banned) {
  66. logger.info('IO_BANNED_CONNECTION', `A user tried to connect, but is currently banned. IP: ${socket.ip}.${sessionInfo}`);
  67. socket.emit('keep.event:banned', socket.ban);
  68. socket.disconnect(true);
  69. } else {
  70. logger.info('IO_CONNECTION', `User connected. IP: ${socket.ip}.${sessionInfo}`);
  71. // catch when the socket has been disconnected
  72. socket.on('disconnect', (reason) => {
  73. let sessionInfo = '';
  74. if (socket.session.sessionId) sessionInfo = ` UserID: ${socket.session.userId}.`;
  75. logger.info('IO_DISCONNECTION', `User disconnected. IP: ${socket.ip}.${sessionInfo}`);
  76. });
  77. // catch errors on the socket (internal to socket.io)
  78. socket.on('error', err => console.error(err));
  79. // have the socket listen for each action
  80. Object.keys(actions).forEach((namespace) => {
  81. Object.keys(actions[namespace]).forEach((action) => {
  82. // the full name of the action
  83. let name = `${namespace}.${action}`;
  84. // listen for this action to be called
  85. socket.on(name, function () {
  86. let args = Array.prototype.slice.call(arguments, 0, -1);
  87. let cb = arguments[arguments.length - 1];
  88. if (lockdown) return cb({status: 'failure', message: 'Lockdown'});
  89. // load the session from the cache
  90. cache.hget('sessions', socket.session.sessionId, (err, session) => {
  91. if (err && err !== true) {
  92. if (typeof cb === 'function') return cb({
  93. status: 'error',
  94. message: 'An error occurred while obtaining your session'
  95. });
  96. }
  97. // make sure the sockets sessionId isn't set if there is no session
  98. if (socket.session.sessionId && session === null) delete socket.session.sessionId;
  99. // call the action, passing it the session, and the arguments socket.io passed us
  100. actions[namespace][action].apply(null, [socket.session].concat(args).concat([
  101. (result) => {
  102. // respond to the socket with our message
  103. if (typeof cb === 'function') return cb(result);
  104. }
  105. ]));
  106. });
  107. })
  108. })
  109. });
  110. if (socket.session.sessionId) {
  111. cache.hget('sessions', socket.session.sessionId, (err, session) => {
  112. if (err && err !== true) socket.emit('ready', false);
  113. else if (session && session.userId) {
  114. db.models.user.findOne({ _id: session.userId }, (err, user) => {
  115. if (err || !user) return socket.emit('ready', false);
  116. let role = '';
  117. let username = '';
  118. let userId = '';
  119. if (user) {
  120. role = user.role;
  121. username = user.username;
  122. userId = session.userId;
  123. }
  124. socket.emit('ready', true, role, username, userId);
  125. });
  126. } else socket.emit('ready', false);
  127. })
  128. } else socket.emit('ready', false);
  129. }
  130. });
  131. initialized = true;
  132. if (lockdown) return this._lockdown();
  133. cb();
  134. },
  135. _lockdown: () => {
  136. this.io.close();
  137. let connected = this.io.of('/').connected;
  138. for (let key in connected) {
  139. connected[key].disconnect('Lockdown');
  140. }
  141. lockdown = true;
  142. }
  143. };