metrics = require "metrics-sharelatex" logger = require "logger-sharelatex" settings = require "settings-sharelatex" WebsocketController = require "./WebsocketController" HttpController = require "./HttpController" HttpApiController = require "./HttpApiController" Utils = require "./Utils" bodyParser = require "body-parser" basicAuth = require('basic-auth-connect') httpAuth = basicAuth (user, pass)-> isValid = user == settings.internal.realTime.user and pass == settings.internal.realTime.pass if !isValid logger.err user:user, pass:pass, "invalid login details" return isValid module.exports = Router = _handleError: (callback = ((error) ->), error, client, method, extraAttrs = {}) -> Utils.getClientAttributes client, ["project_id", "doc_id", "user_id"], (_, attrs) -> for key, value of extraAttrs attrs[key] = value attrs.client_id = client.id attrs.err = error if error.message in ["not authorized", "doc updater could not load requested ops"] logger.warn attrs, error.message return callback {message: error.message} else logger.error attrs, "server side error in #{method}" # Don't return raw error to prevent leaking server side info return callback {message: "Something went wrong in real-time service"} configure: (app, io, session) -> app.set("io", io) app.get "/clients", HttpController.getConnectedClients app.get "/clients/:client_id", HttpController.getConnectedClient app.post "/project/:project_id/message/:message", httpAuth, bodyParser.json(limit: "5mb"), HttpApiController.sendMessage app.post "/drain", httpAuth, HttpApiController.startDrain session.on 'connection', (error, client, session) -> if error? logger.err err: error, "error when client connected" client?.disconnect() return metrics.inc('socket-io.connection') logger.log session: session, client_id: client.id, "client connected" if session?.passport?.user? user = session.passport.user else if session?.user? user = session.user else user = {_id: "anonymous-user"} client.on "joinProject", (data = {}, callback) -> WebsocketController.joinProject client, user, data.project_id, (err, args...) -> if err? Router._handleError callback, err, client, "joinProject", {project_id: data.project_id, user_id: user?.id} else callback(null, args...) client.on "disconnect", () -> metrics.inc('socket-io.disconnect') WebsocketController.leaveProject io, client, (err) -> if err? Router._handleError null, err, client, "leaveProject" client.on "joinDoc", (doc_id, fromVersion, callback) -> # fromVersion is optional if typeof fromVersion == "function" callback = fromVersion fromVersion = -1 WebsocketController.joinDoc client, doc_id, fromVersion, (err, args...) -> if err? Router._handleError callback, err, client, "joinDoc", {doc_id, fromVersion} else callback(null, args...) client.on "leaveDoc", (doc_id, callback) -> WebsocketController.leaveDoc client, doc_id, (err, args...) -> if err? Router._handleError callback, err, client, "leaveDoc" else callback(null, args...) client.on "clientTracking.getConnectedUsers", (callback = (error, users) ->) -> WebsocketController.getConnectedUsers client, (err, users) -> if err? Router._handleError callback, err, client, "clientTracking.getConnectedUsers" else callback(null, users) client.on "clientTracking.updatePosition", (cursorData, callback = (error) ->) -> WebsocketController.updateClientPosition client, cursorData, (err) -> if err? Router._handleError callback, err, client, "clientTracking.updatePosition" else callback() client.on "applyOtUpdate", (doc_id, update, callback = (error) ->) -> WebsocketController.applyOtUpdate client, doc_id, update, (err) -> if err? Router._handleError callback, err, client, "applyOtUpdate", {doc_id, update} else callback()