Router.coffee 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  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"]
  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.err 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. WebsocketController.joinProject client, user, data.project_id, (err, args...) ->
  59. if err?
  60. Router._handleError callback, err, client, "joinProject", {project_id: data.project_id, user_id: user?.id}
  61. else
  62. callback(null, args...)
  63. client.on "disconnect", () ->
  64. metrics.inc('socket-io.disconnect')
  65. WebsocketController.leaveProject io, client, (err) ->
  66. if err?
  67. Router._handleError null, err, client, "leaveProject"
  68. # Variadic. The possible arguments:
  69. # doc_id, callback
  70. # doc_id, fromVersion, callback
  71. # doc_id, options, callback
  72. # doc_id, fromVersion, options, callback
  73. client.on "joinDoc", (doc_id, fromVersion, options, callback) ->
  74. if typeof fromVersion == "function" and !options
  75. callback = fromVersion
  76. fromVersion = -1
  77. options = {}
  78. else if typeof fromVersion == "number" and typeof options == "function"
  79. callback = options
  80. options = {}
  81. else if typeof fromVersion == "object" and typeof options == "function"
  82. callback = options
  83. options = fromVersion
  84. fromVersion = -1
  85. else if typeof fromVersion == "number" and typeof options == "object"
  86. # Called with 4 args, things are as expected
  87. else
  88. logger.error { arguments: arguments }, "unexpected arguments"
  89. return callback?(new Error("unexpected arguments"))
  90. WebsocketController.joinDoc client, doc_id, fromVersion, options, (err, args...) ->
  91. if err?
  92. Router._handleError callback, err, client, "joinDoc", {doc_id, fromVersion}
  93. else
  94. callback(null, args...)
  95. client.on "leaveDoc", (doc_id, callback) ->
  96. WebsocketController.leaveDoc client, doc_id, (err, args...) ->
  97. if err?
  98. Router._handleError callback, err, client, "leaveDoc"
  99. else
  100. callback(null, args...)
  101. client.on "clientTracking.getConnectedUsers", (callback = (error, users) ->) ->
  102. WebsocketController.getConnectedUsers client, (err, users) ->
  103. if err?
  104. Router._handleError callback, err, client, "clientTracking.getConnectedUsers"
  105. else
  106. callback(null, users)
  107. client.on "clientTracking.updatePosition", (cursorData, callback = (error) ->) ->
  108. WebsocketController.updateClientPosition client, cursorData, (err) ->
  109. if err?
  110. Router._handleError callback, err, client, "clientTracking.updatePosition"
  111. else
  112. callback()
  113. client.on "applyOtUpdate", (doc_id, update, callback = (error) ->) ->
  114. WebsocketController.applyOtUpdate client, doc_id, update, (err) ->
  115. if err?
  116. Router._handleError callback, err, client, "applyOtUpdate", {doc_id, update}
  117. else
  118. callback()