Router.coffee 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. metrics = require "metrics-sharelatex"
  2. logger = require "logger-sharelatex"
  3. settings = require "settings-sharelatex"
  4. WebsocketController = require "./WebsocketController"
  5. HttpController = require "./HttpController"
  6. HttpApiController = require "./HttpApiController"
  7. Utils = require "./Utils"
  8. bodyParser = require "body-parser"
  9. basicAuth = require('basic-auth-connect')
  10. httpAuth = basicAuth (user, pass)->
  11. isValid = user == settings.internal.realTime.user and pass == settings.internal.realTime.pass
  12. if !isValid
  13. logger.err user:user, pass:pass, "invalid login details"
  14. return isValid
  15. module.exports = Router =
  16. _handleError: (callback = ((error) ->), error, client, method, extraAttrs = {}) ->
  17. Utils.getClientAttributes client, ["project_id", "doc_id", "user_id"], (_, attrs) ->
  18. for key, value of extraAttrs
  19. attrs[key] = value
  20. attrs.client_id = client.id
  21. attrs.err = error
  22. if error.message in ["not authorized", "doc updater could not load requested ops", "no project_id found on client"]
  23. logger.warn attrs, error.message
  24. return callback {message: error.message}
  25. else
  26. logger.error attrs, "server side error in #{method}"
  27. # Don't return raw error to prevent leaking server side info
  28. return callback {message: "Something went wrong in real-time service"}
  29. configure: (app, io, session) ->
  30. app.set("io", io)
  31. app.get "/clients", HttpController.getConnectedClients
  32. app.get "/clients/:client_id", HttpController.getConnectedClient
  33. app.post "/project/:project_id/message/:message", httpAuth, bodyParser.json(limit: "5mb"), HttpApiController.sendMessage
  34. app.post "/drain", httpAuth, HttpApiController.startDrain
  35. session.on 'connection', (error, client, session) ->
  36. if client? and error?.message?.match(/could not look up session by key/)
  37. logger.warn err: error, client: client?, session: session?, "invalid session"
  38. # tell the client to reauthenticate if it has an invalid session key
  39. client.emit("connectionRejected", {message: "invalid session"})
  40. client.disconnect()
  41. return
  42. if error?
  43. logger.err err: error, client: client?, session: session?, "error when client connected"
  44. client?.emit("connectionRejected", {message: "error"})
  45. client?.disconnect()
  46. return
  47. # send positive confirmation that the client has a valid connection
  48. client.emit("connectionAccepted")
  49. metrics.inc('socket-io.connection')
  50. logger.log session: session, client_id: client.id, "client connected"
  51. if session?.passport?.user?
  52. user = session.passport.user
  53. else if session?.user?
  54. user = session.user
  55. else
  56. user = {_id: "anonymous-user"}
  57. client.on "joinProject", (data = {}, callback) ->
  58. if data.anonymousAccessToken
  59. user.anonymousAccessToken = data.anonymousAccessToken
  60. WebsocketController.joinProject client, user, data.project_id, (err, args...) ->
  61. if err?
  62. Router._handleError callback, err, client, "joinProject", {project_id: data.project_id, user_id: user?.id}
  63. else
  64. callback(null, args...)
  65. client.on "disconnect", () ->
  66. metrics.inc('socket-io.disconnect')
  67. WebsocketController.leaveProject io, client, (err) ->
  68. if err?
  69. Router._handleError null, err, client, "leaveProject"
  70. # Variadic. The possible arguments:
  71. # doc_id, callback
  72. # doc_id, fromVersion, callback
  73. # doc_id, options, callback
  74. # doc_id, fromVersion, options, callback
  75. client.on "joinDoc", (doc_id, fromVersion, options, callback) ->
  76. if typeof fromVersion == "function" and !options
  77. callback = fromVersion
  78. fromVersion = -1
  79. options = {}
  80. else if typeof fromVersion == "number" and typeof options == "function"
  81. callback = options
  82. options = {}
  83. else if typeof fromVersion == "object" and typeof options == "function"
  84. callback = options
  85. options = fromVersion
  86. fromVersion = -1
  87. else if typeof fromVersion == "number" and typeof options == "object"
  88. # Called with 4 args, things are as expected
  89. else
  90. logger.error { arguments: arguments }, "unexpected arguments"
  91. return callback?(new Error("unexpected arguments"))
  92. WebsocketController.joinDoc client, doc_id, fromVersion, options, (err, args...) ->
  93. if err?
  94. Router._handleError callback, err, client, "joinDoc", {doc_id, fromVersion}
  95. else
  96. callback(null, args...)
  97. client.on "leaveDoc", (doc_id, callback) ->
  98. WebsocketController.leaveDoc client, doc_id, (err, args...) ->
  99. if err?
  100. Router._handleError callback, err, client, "leaveDoc"
  101. else
  102. callback(null, args...)
  103. client.on "clientTracking.getConnectedUsers", (callback = (error, users) ->) ->
  104. WebsocketController.getConnectedUsers client, (err, users) ->
  105. if err?
  106. Router._handleError callback, err, client, "clientTracking.getConnectedUsers"
  107. else
  108. callback(null, users)
  109. client.on "clientTracking.updatePosition", (cursorData, callback = (error) ->) ->
  110. WebsocketController.updateClientPosition client, cursorData, (err) ->
  111. if err?
  112. Router._handleError callback, err, client, "clientTracking.updatePosition"
  113. else
  114. callback()
  115. client.on "applyOtUpdate", (doc_id, update, callback = (error) ->) ->
  116. WebsocketController.applyOtUpdate client, doc_id, update, (err) ->
  117. if err?
  118. Router._handleError callback, err, client, "applyOtUpdate", {doc_id, update}
  119. else
  120. callback()