RealTimeClient.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. /* eslint-disable
  2. no-return-assign,
  3. */
  4. // TODO: This file was created by bulk-decaffeinate.
  5. // Fix any style issues and re-enable lint.
  6. /*
  7. * decaffeinate suggestions:
  8. * DS102: Remove unnecessary code created because of implicit returns
  9. * DS207: Consider shorter variations of null checks
  10. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  11. */
  12. let Client
  13. const { XMLHttpRequest } = require('../../libs/XMLHttpRequest')
  14. const io = require('socket.io-client')
  15. const async = require('async')
  16. const request = require('request')
  17. const Settings = require('@overleaf/settings')
  18. const redis = require('@overleaf/redis-wrapper')
  19. const rclient = redis.createClient(Settings.redis.websessions)
  20. const uid = require('uid-safe').sync
  21. const signature = require('cookie-signature')
  22. io.util.request = function () {
  23. const xhr = new XMLHttpRequest()
  24. const _open = xhr.open
  25. xhr.open = function () {
  26. _open.apply(xhr, arguments)
  27. if (Client.cookie != null) {
  28. return xhr.setRequestHeader('Cookie', Client.cookie)
  29. }
  30. }
  31. return xhr
  32. }
  33. module.exports = Client = {
  34. cookie: null,
  35. setSession(session, callback) {
  36. if (callback == null) {
  37. callback = function () {}
  38. }
  39. const sessionId = uid(24)
  40. session.cookie = {}
  41. return rclient.set('sess:' + sessionId, JSON.stringify(session), error => {
  42. if (error != null) {
  43. return callback(error)
  44. }
  45. Client.cookieSignedWith = {}
  46. // prepare cookie strings for all supported session secrets
  47. for (const secretName of [
  48. 'sessionSecret',
  49. 'sessionSecretFallback',
  50. 'sessionSecretUpcoming',
  51. ]) {
  52. const secret = Settings.security[secretName]
  53. const cookieKey = 's:' + signature.sign(sessionId, secret)
  54. Client.cookieSignedWith[secretName] =
  55. `${Settings.cookieName}=${cookieKey}`
  56. }
  57. // default to the current session secret
  58. Client.cookie = Client.cookieSignedWith.sessionSecret
  59. return callback()
  60. })
  61. },
  62. setAnonSession(projectId, anonymousAccessToken, callback) {
  63. Client.setSession(
  64. {
  65. anonTokenAccess: {
  66. [projectId]: anonymousAccessToken,
  67. },
  68. },
  69. callback
  70. )
  71. },
  72. unsetSession(callback) {
  73. if (callback == null) {
  74. callback = function () {}
  75. }
  76. Client.cookie = null
  77. return callback()
  78. },
  79. connect(projectId, callback) {
  80. const client = io.connect('http://127.0.0.1:3026', {
  81. 'force new connection': true,
  82. query: new URLSearchParams({ projectId }).toString(),
  83. })
  84. let disconnected = false
  85. client.on('disconnect', () => {
  86. disconnected = true
  87. })
  88. client.on('connectionRejected', err => {
  89. // Wait for disconnect ahead of continuing with the test sequence.
  90. setTimeout(() => {
  91. if (!disconnected) {
  92. throw new Error('should disconnect after connectionRejected')
  93. }
  94. callback(err)
  95. }, 10)
  96. })
  97. client.on('joinProjectResponse', resp => {
  98. const { publicId, project, permissionsLevel, protocolVersion } = resp
  99. client.publicId = publicId
  100. callback(null, project, permissionsLevel, protocolVersion)
  101. })
  102. return client
  103. },
  104. getConnectedClients(callback) {
  105. if (callback == null) {
  106. callback = function () {}
  107. }
  108. return request.get(
  109. {
  110. url: 'http://127.0.0.1:3026/clients',
  111. json: true,
  112. },
  113. (error, response, data) => callback(error, data)
  114. )
  115. },
  116. countConnectedClients(projectId, callback) {
  117. request.get(
  118. {
  119. url: `http://127.0.0.1:3026/project/${projectId}/count-connected-clients`,
  120. json: true,
  121. },
  122. (error, response, data) => callback(error, data)
  123. )
  124. },
  125. getConnectedClient(clientId, callback) {
  126. if (callback == null) {
  127. callback = function () {}
  128. }
  129. return request.get(
  130. {
  131. url: `http://127.0.0.1:3026/clients/${clientId}`,
  132. json: true,
  133. },
  134. (error, response, data) => {
  135. if (response?.statusCode === 404) {
  136. callback(new Error('not found'))
  137. } else {
  138. callback(error, data)
  139. }
  140. }
  141. )
  142. },
  143. disconnectClient(clientId, callback) {
  144. request.post(
  145. {
  146. url: `http://127.0.0.1:3026/client/${clientId}/disconnect`,
  147. },
  148. (error, response, data) => callback(error, data)
  149. )
  150. return null
  151. },
  152. disconnectAllClients(callback) {
  153. return Client.getConnectedClients((error, clients) => {
  154. if (error) return callback(error)
  155. async.each(
  156. clients,
  157. (clientView, cb) => Client.disconnectClient(clientView.client_id, cb),
  158. callback
  159. )
  160. })
  161. },
  162. }