app.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. /*global require:true, __dirname:true */
  2. var express = require('express');
  3. var http = require('http');
  4. var path = require('path');
  5. var bodyParser = require('body-parser');
  6. var events = require('events');
  7. var _ = require('underscore');
  8. var sanitize = require('validator').sanitize;
  9. var config = {
  10. development: require('./config-dev.js'),
  11. production: require('./config.js')
  12. };
  13. var app = express(),
  14. server = http.createServer(app);
  15. server.listen(conf.port);
  16. app.use(bodyParser.json());
  17. app.use(bodyParser.urlencoded({ extended: false }));
  18. app.use(express.static(path.join(__dirname, 'app')));
  19. var io = require('socket.io')(server);
  20. var redis = require('socket.io-redis');
  21. io.adapter(redis({ host: conf.dbHost, port: conf.dbPort }));
  22. var db = require('redis').createClient(conf.dbPort,conf.dbHost);
  23. var logger = new events.EventEmitter();
  24. logger.on('newEvent', function(event, data) {
  25. console.log('%s: %s', event, JSON.stringify(data));
  26. });
  27. // ***************************************************************************
  28. // Express routes helpers
  29. // ***************************************************************************
  30. // Only authenticated users should be able to use protected methods
  31. var requireAuthentication = function(req, res, next) {
  32. // TODO
  33. next();
  34. };
  35. // Sanitize message to avoid security problems
  36. var sanitizeMessage = function(req, res, next) {
  37. if (req.body.msg) {
  38. req.sanitizedMessage = sanitize(req.body.msg).xss();
  39. next();
  40. } else {
  41. res.send(400, "No message provided");
  42. }
  43. };
  44. // Send a message to all active rooms
  45. var sendBroadcast = function(text) {
  46. _.each(io.nsps['/'].adapter.rooms, function(room) {
  47. if (room) {
  48. var message = {'room':room, 'username':'Radio-Robbot', 'msg':text, 'date':new Date()};
  49. io.to(room).emit('newMessage', message);
  50. }
  51. });
  52. logger.emit('newEvent', 'newBroadcastMessage', {'msg':text});
  53. };
  54. // ***************************************************************************
  55. // Express routes
  56. // ***************************************************************************
  57. // Welcome message
  58. app.get('/', function(req, res) {
  59. res.send(200, "Welcome to Daveo Radio");
  60. });
  61. // Broadcast message to all connected users
  62. app.post('/api/broadcast/', requireAuthentication, sanitizeMessage, function(req, res) {
  63. sendBroadcast(req.sanitizedMessage);
  64. res.send(201, "Message sent to all rooms");
  65. });
  66. // ***************************************************************************
  67. // Socket.io events
  68. // ***************************************************************************
  69. io.sockets.on('connection', function(socket) {
  70. // Welcome message on connection
  71. socket.emit('connected', 'Welcome to the chat server');
  72. logger.emit('newEvent', 'userConnected', {'socket':socket.id});
  73. // Store user data in db
  74. db.hset([socket.id, 'connectionDate', new Date()], redis.print);
  75. db.hset([socket.id, 'socketID', socket.id], redis.print);
  76. db.hset([socket.id, 'username', 'anonymous'], redis.print);
  77. // Join user to 'MainRoom'
  78. socket.join(conf.mainroom);
  79. logger.emit('newEvent', 'userJoinsRoom', {'socket':socket.id, 'room':conf.mainroom});
  80. // Confirm subscription to user
  81. socket.emit('subscriptionConfirmed', {'room':conf.mainroom});
  82. // Notify subscription to all users in room
  83. var data = {'room':conf.mainroom, 'username':'anonymous', 'msg':'----- Joined the room -----', 'id':socket.id};
  84. io.to(conf.mainroom).emit('userJoinsRoom', data);
  85. // User wants to subscribe to [data.rooms]
  86. socket.on('subscribe', function(data) {
  87. // Get user info from db
  88. db.hget([socket.id, 'username'], function(err, username) {
  89. // Subscribe user to chosen rooms
  90. _.each(data.rooms, function(room) {
  91. room = room.replace(" ","");
  92. socket.join(room);
  93. logger.emit('newEvent', 'userJoinsRoom', {'socket':socket.id, 'username':username, 'room':room});
  94. // Confirm subscription to user
  95. socket.emit('subscriptionConfirmed', {'room': room});
  96. // Notify subscription to all users in room
  97. var message = {'room':room, 'username':username, 'msg':'----- Joined the room -----', 'id':socket.id};
  98. io.to(room).emit('userJoinsRoom', message);
  99. });
  100. });
  101. });
  102. // User wants to unsubscribe from [data.rooms]
  103. socket.on('unsubscribe', function(data) {
  104. // Get user info from db
  105. db.hget([socket.id, 'username'], function(err, username) {
  106. // Unsubscribe user from chosen rooms
  107. _.each(data.rooms, function(room) {
  108. if (room != conf.mainroom) {
  109. socket.leave(room);
  110. logger.emit('newEvent', 'userLeavesRoom', {'socket':socket.id, 'username':username, 'room':room});
  111. // Confirm unsubscription to user
  112. socket.emit('unsubscriptionConfirmed', {'room': room});
  113. // Notify unsubscription to all users in room
  114. var message = {'room':room, 'username':username, 'msg':'----- Left the room -----', 'id': socket.id};
  115. io.to(room).emit('userLeavesRoom', message);
  116. }
  117. });
  118. });
  119. });
  120. // User wants to know what rooms he has joined
  121. socket.on('getRooms', function(data) {
  122. socket.emit('roomsReceived', socket.rooms);
  123. logger.emit('newEvent', 'userGetsRooms', {'socket':socket.id});
  124. });
  125. // Get users in given room
  126. socket.on('getUsersInRoom', function(data) {
  127. var usersInRoom = [];
  128. var socketsInRoom = _.keys(io.nsps['/'].adapter.rooms[data.room]);
  129. for (var i=0; i<socketsInRoom.length; i++) {
  130. db.hgetall(socketsInRoom[i], function(err, obj) {
  131. usersInRoom.push({'room':data.room, 'username':obj.username, 'id':obj.socketID});
  132. // When we've finished with the last one, notify user
  133. if (usersInRoom.length == socketsInRoom.length) {
  134. socket.emit('usersInRoom', {'users':usersInRoom});
  135. }
  136. });
  137. }
  138. });
  139. // User wants to change his nickname
  140. socket.on('setNickname', function(data) {
  141. // Get user info from db
  142. db.hget([socket.id, 'username'], function(err, username) {
  143. // Store user data in db
  144. db.hset([socket.id, 'username', data.username], redis.print);
  145. logger.emit('newEvent', 'userSetsNickname', {'socket':socket.id, 'oldUsername':username, 'newUsername':data.username});
  146. // Notify all users who belong to the same rooms that this one
  147. _.each(socket.rooms, function(room) {
  148. if (room) {
  149. var info = {'room':room, 'oldUsername':username, 'newUsername':data.username, 'id':socket.id};
  150. io.to(room).emit('userNicknameUpdated', info);
  151. }
  152. });
  153. });
  154. });
  155. // New message sent to group
  156. socket.on('newMessage', function(data) {
  157. db.hgetall(socket.id, function(err, obj) {
  158. if (err) return logger.emit('newEvent', 'error', err);
  159. // Check if user is subscribed to room before sending his message
  160. if (_.contains(_.values(socket.rooms), data.room)) {
  161. var message = {'room':data.room, 'username':obj.username, 'msg':data.msg, 'date':new Date()};
  162. // Send message to room
  163. io.to(data.room).emit('newMessage', message);
  164. logger.emit('newEvent', 'newMessage', message);
  165. }
  166. });
  167. });
  168. // Clean up on disconnect
  169. socket.on('disconnect', function() {
  170. // Get current rooms of user
  171. var rooms = socket.rooms;
  172. // Get user info from db
  173. db.hgetall(socket.id, function(err, obj) {
  174. if (err) return logger.emit('newEvent', 'error', err);
  175. logger.emit('newEvent', 'userDisconnected', {'socket':socket.id, 'username':obj.username});
  176. // Notify all users who belong to the same rooms that this one
  177. _.each(rooms, function(room) {
  178. if (room) {
  179. var message = {'room':room, 'username':obj.username, 'msg':'----- Left the room -----', 'id':obj.socketID};
  180. io.to(room).emit('userLeavesRoom', message);
  181. }
  182. });
  183. });
  184. // Delete user from db
  185. db.del(socket.id, redis.print);
  186. });
  187. });
  188. app.get('/other', function (req, res) {
  189. if (req.query.other === 'stillgame')
  190. res.sendStatus(200)
  191. else {
  192. res.writeHead(400, 'WRONG!')
  193. res.send()
  194. }
  195. })
  196. // Automatic message generation (for testing purposes)
  197. if (conf.debug) {
  198. setInterval(function() {
  199. var text = 'Testing rooms';
  200. sendBroadcast(text);
  201. }, 60000);
  202. }