ConnectedUsersManager.coffee 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. async = require("async")
  2. Settings = require('settings-sharelatex')
  3. logger = require("logger-sharelatex")
  4. redis = require("redis-sharelatex")
  5. rclient = redis.createClient(Settings.redis.web)
  6. ONE_HOUR_IN_S = 60 * 60
  7. ONE_DAY_IN_S = ONE_HOUR_IN_S * 24
  8. FOUR_DAYS_IN_S = ONE_DAY_IN_S * 4
  9. USER_TIMEOUT_IN_S = ONE_HOUR_IN_S / 4
  10. buildProjectSetKey = (project_id)-> return "clients_in_project:#{project_id}"
  11. buildUserKey = (project_id, client_id)-> return "connected_user:#{project_id}:#{client_id}"
  12. module.exports =
  13. # Use the same method for when a user connects, and when a user sends a cursor
  14. # update. This way we don't care if the connected_user key has expired when
  15. # we receive a cursor update.
  16. updateUserPosition: (project_id, client_id, user, cursorData, callback = (err)->)->
  17. logger.log project_id:project_id, client_id:client_id, "marking user as joined or connected"
  18. multi = rclient.multi()
  19. multi.sadd buildProjectSetKey(project_id), client_id
  20. multi.expire buildProjectSetKey(project_id), FOUR_DAYS_IN_S
  21. multi.hset buildUserKey(project_id, client_id), "last_updated_at", Date.now()
  22. multi.hset buildUserKey(project_id, client_id), "user_id", user._id
  23. multi.hset buildUserKey(project_id, client_id), "first_name", user.first_name or ""
  24. multi.hset buildUserKey(project_id, client_id), "last_name", user.last_name or ""
  25. multi.hset buildUserKey(project_id, client_id), "email", user.email or ""
  26. if cursorData?
  27. multi.hset buildUserKey(project_id, client_id), "cursorData", JSON.stringify(cursorData)
  28. multi.expire buildUserKey(project_id, client_id), USER_TIMEOUT_IN_S
  29. multi.exec (err)->
  30. if err?
  31. logger.err err:err, project_id:project_id, client_id:client_id, "problem marking user as connected"
  32. callback(err)
  33. markUserAsDisconnected: (project_id, client_id, callback)->
  34. logger.log project_id:project_id, client_id:client_id, "marking user as disconnected"
  35. multi = rclient.multi()
  36. multi.srem buildProjectSetKey(project_id), client_id
  37. multi.expire buildProjectSetKey(project_id), FOUR_DAYS_IN_S
  38. multi.del buildUserKey(project_id, client_id)
  39. multi.exec callback
  40. _getConnectedUser: (project_id, client_id, callback)->
  41. rclient.hgetall buildUserKey(project_id, client_id), (err, result)->
  42. if !result?
  43. result =
  44. connected : false
  45. client_id:client_id
  46. else
  47. result.connected = true
  48. result.client_id = client_id
  49. if result.cursorData?
  50. try
  51. result.cursorData = JSON.parse(result.cursorData)
  52. catch e
  53. logger.error {err: e, project_id, client_id, cursorData: result.cursorData}, "error parsing cursorData JSON"
  54. return callback e
  55. callback err, result
  56. getConnectedUsers: (project_id, callback)->
  57. self = @
  58. rclient.smembers buildProjectSetKey(project_id), (err, results)->
  59. return callback(err) if err?
  60. jobs = results.map (client_id)->
  61. (cb)->
  62. self._getConnectedUser(project_id, client_id, cb)
  63. async.series jobs, (err, users = [])->
  64. return callback(err) if err?
  65. users = users.filter (user) ->
  66. user?.connected
  67. callback null, users