io.js 5.2 KB

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