WebsocketController.coffee 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. logger = require "logger-sharelatex"
  2. WebApiManager = require "./WebApiManager"
  3. AuthorizationManager = require "./AuthorizationManager"
  4. DocumentUpdaterManager = require "./DocumentUpdaterManager"
  5. ConnectedUsersManager = require "./ConnectedUsersManager"
  6. Utils = require "./Utils"
  7. module.exports = WebsocketController =
  8. # If the protocol version changes when the client reconnects,
  9. # it will force a full refresh of the page. Useful for non-backwards
  10. # compatible protocol changes. Use only in extreme need.
  11. PROTOCOL_VERSION: 2
  12. joinProject: (client, user, project_id, callback = (error, project, privilegeLevel, protocolVersion) ->) ->
  13. user_id = user?._id
  14. logger.log {user_id, project_id, client_id: client.id}, "user joining project"
  15. WebApiManager.joinProject project_id, user_id, (error, project, privilegeLevel) ->
  16. return callback(error) if error?
  17. if !privilegeLevel or privilegeLevel == ""
  18. err = new Error("not authorized")
  19. logger.error {err, project_id, user_id, client_id: client.id}, "user is not authorized to join project"
  20. return callback(err)
  21. client.join project_id
  22. client.set("privilege_level", privilegeLevel)
  23. client.set("user_id", user_id)
  24. client.set("project_id", project_id)
  25. client.set("owner_id", project?.owner?._id)
  26. client.set("first_name", user?.first_name)
  27. client.set("last_name", user?.last_name)
  28. client.set("email", user?.email)
  29. client.set("connected_time", new Date())
  30. client.set("signup_date", user?.signUpDate)
  31. client.set("login_count", user?.loginCount)
  32. callback null, project, privilegeLevel, WebsocketController.PROTOCOL_VERSION
  33. # No need to block for setting the user as connected in the cursor tracking
  34. ConnectedUsersManager.updateUserPosition project_id, client.id, user, null, () ->
  35. joinDoc: (client, doc_id, fromVersion = -1, callback = (error, doclines, version, ops) ->) ->
  36. Utils.getClientAttributes client, ["project_id", "user_id"], (error, {project_id, user_id}) ->
  37. logger.log {user_id, project_id, doc_id, fromVersion, client_id: client.id}, "client joining doc"
  38. AuthorizationManager.assertClientCanViewProject client, (error) ->
  39. return callback(error) if error?
  40. client.get "project_id", (error, project_id) ->
  41. return callback(error) if error?
  42. return callback(new Error("no project_id found on client")) if !project_id?
  43. DocumentUpdaterManager.getDocument project_id, doc_id, fromVersion, (error, lines, version, ops) ->
  44. return callback(error) if error?
  45. # Encode any binary bits of data so it can go via WebSockets
  46. # See http://ecmanaut.blogspot.co.uk/2006/07/encoding-decoding-utf8-in-javascript.html
  47. escapedLines = []
  48. for line in lines
  49. try
  50. line = unescape(encodeURIComponent(line))
  51. catch err
  52. logger.err {err, project_id, doc_id, fromVersion, line, client_id: client.id}, "error encoding line uri component"
  53. return callback(err)
  54. escapedLines.push line
  55. client.join(doc_id)
  56. callback null, escapedLines, version, ops
  57. leaveDoc: (client, doc_id, callback = (error) ->) ->
  58. Utils.getClientAttributes client, ["project_id", "user_id"], (error, {project_id, user_id}) ->
  59. logger.log {user_id, project_id, doc_id, client_id: client.id}, "client leaving doc"
  60. client.leave doc_id
  61. callback()
  62. updateClientPosition: (client, cursorData, callback = (error) ->) ->
  63. Utils.getClientAttributes client, [
  64. "project_id", "first_name", "last_name", "email", "user_id"
  65. ], (error, {project_id, first_name, last_name, email, user_id}) ->
  66. return callback(error) if error?
  67. logger.log {user_id, project_id, client_id: client.id, cursorData: cursorData}, "updating client position"
  68. cursorData.id = client.id
  69. cursorData.user_id = user_id if user_id?
  70. cursorData.email = email if email?
  71. if first_name? and last_name?
  72. cursorData.name = first_name + " " + last_name
  73. ConnectedUsersManager.updateUserPosition(project_id, client.id, {
  74. first_name: first_name,
  75. last_name: last_name,
  76. email: email,
  77. user_id: user_id
  78. }, {
  79. row: cursorData.row,
  80. column: cursorData.column,
  81. doc_id: cursorData.doc_id
  82. }, callback)
  83. else
  84. cursorData.name = "Anonymous"
  85. callback()
  86. #EditorRealTimeController.emitToRoom(project_id, "clientTracking.clientUpdated", cursorData)
  87. #callback()
  88. getConnectedUsers: (client, callback = (error, users) ->) ->
  89. Utils.getClientAttributes client, ["project_id", "user_id"], (error, {project_id, user_id}) ->
  90. logger.log {user_id, project_id, client_id: client.id}, "getting connected users"
  91. AuthorizationManager.assertClientCanViewProject client, (error) ->
  92. return callback(error) if error?
  93. client.get "project_id", (error, project_id) ->
  94. return callback(error) if error?
  95. return callback(new Error("no project_id found on client")) if !project_id?
  96. ConnectedUsersManager.getConnectedUsers project_id, (error, users) ->
  97. return callback(error) if error?
  98. callback null, users