mirror of
https://github.com/overleaf/overleaf.git
synced 2024-12-29 05:33:16 +00:00
aa9d6c8dc9
* [misc] decaff cleanup: RoomManager * [misc] decaff cleanup: RedisClientManager * [misc] decaff cleanup: SafeJsonParse * [misc] decaff cleanup: WebApiManager * [misc] decaff cleanup: WebsocketController * [misc] decaff cleanup: WebsocketLoadBalancer * [misc] decaff cleanup: SessionSockets * [misc] decaff cleanup: HttpController * [misc] decaff cleanup: HttpApiController * [misc] decaff cleanup: HealthCheckManager * [misc] decaff cleanup: EventLogger * [misc] decaff cleanup: Errors o-error will eliminate most of it -- when we migrate over. * [misc] decaff cleanup: DrainManager * [misc] decaff cleanup: DocumentUpdaterManager * [misc] decaff cleanup: DocumentUpdaterController: no-unused-vars * [misc] decaff cleanup: DocumentUpdaterController: Array.from * [misc] decaff cleanup: DocumentUpdaterController: implicit return * [misc] decaff cleanup: DocumentUpdaterController: IIFE * [misc] decaff cleanup: DocumentUpdaterController: null checks * [misc] decaff cleanup: DocumentUpdaterController: simpler loops * [misc] decaff cleanup: DocumentUpdaterController: move module name def * [misc] decaff cleanup: ConnectedUsersManager: handle-callback-err * [misc] decaff cleanup: ConnectedUsersManager: implicit returns * [misc] decaff cleanup: ConnectedUsersManager: null checks * [misc] decaff cleanup: ChannelManager: no-unused-vars * [misc] decaff cleanup: ChannelManager: implicit returns * [misc] decaff cleanup: ChannelManager: other cleanup - var -> const - drop variable assignment before return * [misc] decaff cleanup: AuthorizationManager: handle-callback-err Note: This requires a change in WebsocketController to provide a dummy callback. * [misc] decaff cleanup: AuthorizationManager: Array.from * [misc] decaff cleanup: AuthorizationManager: implicit returns * [misc] decaff cleanup: AuthorizationManager: null checks * [misc] decaff cleanup: Router: handle-callback-err * [misc] decaff cleanup: Router: standard/no-callback-literal * [misc] decaff cleanup: Router: Array.from * [misc] decaff cleanup: Router: implicit returns * [misc] decaff cleanup: Router: refactor __guard__ wrapper * [misc] decaff cleanup: Router: null checks And a minor bug fix: user.id -> user._id * [misc] decaff cleanup: Router: move variable declarations to assignments * [misc] decaff cleanup: app: implicit returns * [misc] decaff cleanup: app: __guard__ * [misc] decaff cleanup: app: null checks * [misc] decaff cleanup: app: function definitions * [misc] decaff cleanup: app: drop unused next argument * [misc] decaff cleanup: app: var -> const
170 lines
4.6 KiB
JavaScript
170 lines
4.6 KiB
JavaScript
/* eslint-disable
|
|
camelcase,
|
|
*/
|
|
const async = require('async')
|
|
const Settings = require('settings-sharelatex')
|
|
const logger = require('logger-sharelatex')
|
|
const redis = require('redis-sharelatex')
|
|
const rclient = redis.createClient(Settings.redis.realtime)
|
|
const Keys = Settings.redis.realtime.key_schema
|
|
|
|
const ONE_HOUR_IN_S = 60 * 60
|
|
const ONE_DAY_IN_S = ONE_HOUR_IN_S * 24
|
|
const FOUR_DAYS_IN_S = ONE_DAY_IN_S * 4
|
|
|
|
const USER_TIMEOUT_IN_S = ONE_HOUR_IN_S / 4
|
|
const REFRESH_TIMEOUT_IN_S = 10 // only show clients which have responded to a refresh request in the last 10 seconds
|
|
|
|
module.exports = {
|
|
// Use the same method for when a user connects, and when a user sends a cursor
|
|
// update. This way we don't care if the connected_user key has expired when
|
|
// we receive a cursor update.
|
|
updateUserPosition(project_id, client_id, user, cursorData, callback) {
|
|
logger.log({ project_id, client_id }, 'marking user as joined or connected')
|
|
|
|
const multi = rclient.multi()
|
|
|
|
multi.sadd(Keys.clientsInProject({ project_id }), client_id)
|
|
multi.expire(Keys.clientsInProject({ project_id }), FOUR_DAYS_IN_S)
|
|
|
|
multi.hset(
|
|
Keys.connectedUser({ project_id, client_id }),
|
|
'last_updated_at',
|
|
Date.now()
|
|
)
|
|
multi.hset(
|
|
Keys.connectedUser({ project_id, client_id }),
|
|
'user_id',
|
|
user._id
|
|
)
|
|
multi.hset(
|
|
Keys.connectedUser({ project_id, client_id }),
|
|
'first_name',
|
|
user.first_name || ''
|
|
)
|
|
multi.hset(
|
|
Keys.connectedUser({ project_id, client_id }),
|
|
'last_name',
|
|
user.last_name || ''
|
|
)
|
|
multi.hset(
|
|
Keys.connectedUser({ project_id, client_id }),
|
|
'email',
|
|
user.email || ''
|
|
)
|
|
|
|
if (cursorData) {
|
|
multi.hset(
|
|
Keys.connectedUser({ project_id, client_id }),
|
|
'cursorData',
|
|
JSON.stringify(cursorData)
|
|
)
|
|
}
|
|
multi.expire(
|
|
Keys.connectedUser({ project_id, client_id }),
|
|
USER_TIMEOUT_IN_S
|
|
)
|
|
|
|
multi.exec(function (err) {
|
|
if (err) {
|
|
logger.err(
|
|
{ err, project_id, client_id },
|
|
'problem marking user as connected'
|
|
)
|
|
}
|
|
callback(err)
|
|
})
|
|
},
|
|
|
|
refreshClient(project_id, client_id) {
|
|
logger.log({ project_id, client_id }, 'refreshing connected client')
|
|
const multi = rclient.multi()
|
|
multi.hset(
|
|
Keys.connectedUser({ project_id, client_id }),
|
|
'last_updated_at',
|
|
Date.now()
|
|
)
|
|
multi.expire(
|
|
Keys.connectedUser({ project_id, client_id }),
|
|
USER_TIMEOUT_IN_S
|
|
)
|
|
multi.exec(function (err) {
|
|
if (err) {
|
|
logger.err(
|
|
{ err, project_id, client_id },
|
|
'problem refreshing connected client'
|
|
)
|
|
}
|
|
})
|
|
},
|
|
|
|
markUserAsDisconnected(project_id, client_id, callback) {
|
|
logger.log({ project_id, client_id }, 'marking user as disconnected')
|
|
const multi = rclient.multi()
|
|
multi.srem(Keys.clientsInProject({ project_id }), client_id)
|
|
multi.expire(Keys.clientsInProject({ project_id }), FOUR_DAYS_IN_S)
|
|
multi.del(Keys.connectedUser({ project_id, client_id }))
|
|
multi.exec(callback)
|
|
},
|
|
|
|
_getConnectedUser(project_id, client_id, callback) {
|
|
rclient.hgetall(Keys.connectedUser({ project_id, client_id }), function (
|
|
err,
|
|
result
|
|
) {
|
|
if (!(result && result.user_id)) {
|
|
result = {
|
|
connected: false,
|
|
client_id
|
|
}
|
|
} else {
|
|
result.connected = true
|
|
result.client_id = client_id
|
|
result.client_age =
|
|
(Date.now() - parseInt(result.last_updated_at, 10)) / 1000
|
|
if (result.cursorData) {
|
|
try {
|
|
result.cursorData = JSON.parse(result.cursorData)
|
|
} catch (e) {
|
|
logger.error(
|
|
{
|
|
err: e,
|
|
project_id,
|
|
client_id,
|
|
cursorData: result.cursorData
|
|
},
|
|
'error parsing cursorData JSON'
|
|
)
|
|
return callback(e)
|
|
}
|
|
}
|
|
}
|
|
callback(err, result)
|
|
})
|
|
},
|
|
|
|
getConnectedUsers(project_id, callback) {
|
|
const self = this
|
|
rclient.smembers(Keys.clientsInProject({ project_id }), function (
|
|
err,
|
|
results
|
|
) {
|
|
if (err) {
|
|
return callback(err)
|
|
}
|
|
const jobs = results.map((client_id) => (cb) =>
|
|
self._getConnectedUser(project_id, client_id, cb)
|
|
)
|
|
async.series(jobs, function (err, users) {
|
|
if (err) {
|
|
return callback(err)
|
|
}
|
|
users = users.filter(
|
|
(user) =>
|
|
user && user.connected && user.client_age < REFRESH_TIMEOUT_IN_S
|
|
)
|
|
callback(null, users)
|
|
})
|
|
})
|
|
}
|
|
}
|