You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

sockets.js 4.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. var iolib = require('socket.io')
  2. , log = require("./log.js").log
  3. , BoardData = require("./boardData.js").BoardData
  4. , config = require("./configuration");
  5. /** Map from name to *promises* of BoardData
  6. @type {Object<string, Promise<BoardData>>}
  7. */
  8. var boards = {};
  9. function noFail(fn) {
  10. return function noFailWrapped(arg) {
  11. try {
  12. return fn(arg);
  13. } catch (e) {
  14. console.trace(e);
  15. }
  16. }
  17. }
  18. function startIO(app) {
  19. io = iolib(app);
  20. io.on('connection', noFail(socketConnection));
  21. return io;
  22. }
  23. /** Returns a promise to a BoardData with the given name
  24. * @returns {Promise<BoardData>}
  25. */
  26. function getBoard(name) {
  27. if (boards.hasOwnProperty(name)) {
  28. return boards[name];
  29. } else {
  30. var board = BoardData.load(name);
  31. boards[name] = board;
  32. return board;
  33. }
  34. }
  35. function socketConnection(socket) {
  36. async function joinBoard(name) {
  37. // Default to the public board
  38. if (!name) name = "anonymous";
  39. // Join the board
  40. socket.join(name);
  41. var board = await getBoard(name);
  42. board.users.add(socket.id);
  43. log('board joined', { 'board': board.name, 'users': board.users.size });
  44. return board;
  45. }
  46. socket.on("error", noFail(function onError(error) {
  47. log("ERROR", error);
  48. }));
  49. socket.on("getboard", async function onGetBoard(name) {
  50. var board = await joinBoard(name);
  51. //Send all the board's data as soon as it's loaded
  52. socket.emit("broadcast", { _children: board.getAll() });
  53. });
  54. socket.on("joinboard", noFail(joinBoard));
  55. var lastEmitSecond = Date.now() / config.MAX_EMIT_COUNT_PERIOD | 0;
  56. var emitCount = 0;
  57. socket.on('broadcast', noFail(function onBroadcast(message) {
  58. var currentSecond = Date.now() / config.MAX_EMIT_COUNT_PERIOD | 0;
  59. if (currentSecond === lastEmitSecond) {
  60. emitCount++;
  61. if (emitCount > config.MAX_EMIT_COUNT) {
  62. var request = socket.client.request;
  63. if (emitCount % 100 === 0) {
  64. log('BANNED', {
  65. user_agent: request.headers['user-agent'],
  66. original_ip: request.headers['x-forwarded-for'] || request.headers['forwarded'],
  67. emit_count: emitCount
  68. });
  69. }
  70. return;
  71. }
  72. } else {
  73. emitCount = 0;
  74. lastEmitSecond = currentSecond;
  75. }
  76. var boardName = message.board || "anonymous";
  77. var data = message.data;
  78. if (!socket.rooms.hasOwnProperty(boardName)) socket.join(boardName);
  79. if (!data) {
  80. console.warn("Received invalid message: %s.", JSON.stringify(message));
  81. return;
  82. }
  83. if (!message.data.tool || config.BLOCKED_TOOLS.includes(message.data.tool)) {
  84. log('BLOCKED MESSAGE', message.data);
  85. return;
  86. }
  87. // Save the message in the board
  88. handleMessage(boardName, data, socket);
  89. //Send data to all other users connected on the same board
  90. socket.broadcast.to(boardName).emit('broadcast', data);
  91. }));
  92. socket.on('disconnecting', function onDisconnecting(reason) {
  93. Object.keys(socket.rooms).forEach(async function disconnectFrom(room) {
  94. if (boards.hasOwnProperty(room)) {
  95. var board = await boards[room];
  96. board.users.delete(socket.id);
  97. var userCount = board.users.size;
  98. log('disconnection', { 'board': board.name, 'users': board.users.size });
  99. if (userCount === 0) {
  100. board.save();
  101. delete boards[room];
  102. }
  103. }
  104. });
  105. });
  106. }
  107. function handleMessage(boardName, message, socket) {
  108. if (message.tool === "Cursor") {
  109. message.socket = socket.id;
  110. } else {
  111. saveHistory(boardName, message);
  112. }
  113. }
  114. async function saveHistory(boardName, message) {
  115. var id = message.id;
  116. var board = await getBoard(boardName);
  117. switch (message.type) {
  118. case "delete":
  119. if (id) board.delete(id);
  120. break;
  121. case "update":
  122. if (id) board.update(id, message);
  123. break;
  124. case "child":
  125. board.addChild(message.parent, message);
  126. break;
  127. default: //Add data
  128. if (!id) throw new Error("Invalid message: ", message);
  129. board.set(id, message);
  130. }
  131. }
  132. function generateUID(prefix, suffix) {
  133. var uid = Date.now().toString(36); //Create the uids in chronological order
  134. uid += (Math.round(Math.random() * 36)).toString(36); //Add a random character at the end
  135. if (prefix) uid = prefix + uid;
  136. if (suffix) uid = uid + suffix;
  137. return uid;
  138. }
  139. if (exports) {
  140. exports.start = startIO;
  141. }