mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-19 01:35:18 -04:00
Migrate editor-socketio-server.js to TypeScript
Signed-off-by: David Mehren <dmehren1@gmail.com>
This commit is contained in:
parent
c7478157e2
commit
dc3a3f2994
3 changed files with 157 additions and 165 deletions
|
@ -1,164 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var EventEmitter = require('events').EventEmitter;
|
|
||||||
var TextOperation = require('./text-operation');
|
|
||||||
var WrappedOperation = require('./wrapped-operation');
|
|
||||||
var Server = require('./server');
|
|
||||||
var Selection = require('./selection');
|
|
||||||
var util = require('util');
|
|
||||||
|
|
||||||
var logger = require('../logger');
|
|
||||||
|
|
||||||
function EditorSocketIOServer(document, operations, docId, mayWrite, operationCallback) {
|
|
||||||
EventEmitter.call(this);
|
|
||||||
Server.call(this, document, operations);
|
|
||||||
this.users = {};
|
|
||||||
this.docId = docId;
|
|
||||||
this.mayWrite = mayWrite || function (_, cb) {
|
|
||||||
cb(true);
|
|
||||||
};
|
|
||||||
this.operationCallback = operationCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
util.inherits(EditorSocketIOServer, Server);
|
|
||||||
extend(EditorSocketIOServer.prototype, EventEmitter.prototype);
|
|
||||||
|
|
||||||
function extend(target, source) {
|
|
||||||
for (var key in source) {
|
|
||||||
if (source.hasOwnProperty(key)) {
|
|
||||||
target[key] = source[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EditorSocketIOServer.prototype.addClient = function (socket) {
|
|
||||||
var self = this;
|
|
||||||
socket.join(this.docId);
|
|
||||||
var docOut = {
|
|
||||||
str: this.document,
|
|
||||||
revision: this.operations.length,
|
|
||||||
clients: this.users
|
|
||||||
};
|
|
||||||
socket.emit('doc', docOut);
|
|
||||||
socket.on('operation', function (revision, operation, selection) {
|
|
||||||
socket.origin = 'operation';
|
|
||||||
self.mayWrite(socket, function (mayWrite) {
|
|
||||||
if (!mayWrite) {
|
|
||||||
logger.info("User doesn't have the right to edit.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
self.onOperation(socket, revision, operation, selection);
|
|
||||||
if (typeof self.operationCallback === 'function')
|
|
||||||
self.operationCallback(socket, operation);
|
|
||||||
} catch (err) {
|
|
||||||
setTimeout(function() {
|
|
||||||
var docOut = {
|
|
||||||
str: self.document,
|
|
||||||
revision: self.operations.length,
|
|
||||||
clients: self.users,
|
|
||||||
force: true
|
|
||||||
};
|
|
||||||
socket.emit('doc', docOut);
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
socket.on('get_operations', function (base, head) {
|
|
||||||
self.onGetOperations(socket, base, head);
|
|
||||||
});
|
|
||||||
socket.on('selection', function (obj) {
|
|
||||||
socket.origin = 'selection';
|
|
||||||
self.mayWrite(socket, function (mayWrite) {
|
|
||||||
if (!mayWrite) {
|
|
||||||
logger.info("User doesn't have the right to edit.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.updateSelection(socket, obj && Selection.fromJSON(obj));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
socket.on('disconnect', function () {
|
|
||||||
logger.debug("Disconnect");
|
|
||||||
socket.leave(self.docId);
|
|
||||||
self.onDisconnect(socket);
|
|
||||||
/*
|
|
||||||
if (socket.manager && socket.manager.sockets.clients(self.docId).length === 0) {
|
|
||||||
self.emit('empty-room');
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
EditorSocketIOServer.prototype.onOperation = function (socket, revision, operation, selection) {
|
|
||||||
var wrapped;
|
|
||||||
try {
|
|
||||||
wrapped = new WrappedOperation(
|
|
||||||
TextOperation.fromJSON(operation),
|
|
||||||
selection && Selection.fromJSON(selection)
|
|
||||||
);
|
|
||||||
} catch (exc) {
|
|
||||||
logger.error("Invalid operation received: ");
|
|
||||||
logger.error(exc);
|
|
||||||
throw new Error(exc);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
var clientId = socket.id;
|
|
||||||
var wrappedPrime = this.receiveOperation(revision, wrapped);
|
|
||||||
if(!wrappedPrime) return;
|
|
||||||
logger.debug("new operation: " + JSON.stringify(wrapped));
|
|
||||||
this.getClient(clientId).selection = wrappedPrime.meta;
|
|
||||||
revision = this.operations.length;
|
|
||||||
socket.emit('ack', revision);
|
|
||||||
socket.broadcast.in(this.docId).emit(
|
|
||||||
'operation', clientId, revision,
|
|
||||||
wrappedPrime.wrapped.toJSON(), wrappedPrime.meta
|
|
||||||
);
|
|
||||||
//set document is dirty
|
|
||||||
this.isDirty = true;
|
|
||||||
} catch (exc) {
|
|
||||||
logger.error(exc);
|
|
||||||
throw new Error(exc);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
EditorSocketIOServer.prototype.onGetOperations = function (socket, base, head) {
|
|
||||||
var operations = this.operations.slice(base, head).map(function (op) {
|
|
||||||
return op.wrapped.toJSON();
|
|
||||||
});
|
|
||||||
socket.emit('operations', head, operations);
|
|
||||||
};
|
|
||||||
|
|
||||||
EditorSocketIOServer.prototype.updateSelection = function (socket, selection) {
|
|
||||||
var clientId = socket.id;
|
|
||||||
if (selection) {
|
|
||||||
this.getClient(clientId).selection = selection;
|
|
||||||
} else {
|
|
||||||
delete this.getClient(clientId).selection;
|
|
||||||
}
|
|
||||||
socket.broadcast.to(this.docId).emit('selection', clientId, selection);
|
|
||||||
};
|
|
||||||
|
|
||||||
EditorSocketIOServer.prototype.setName = function (socket, name) {
|
|
||||||
var clientId = socket.id;
|
|
||||||
this.getClient(clientId).name = name;
|
|
||||||
socket.broadcast.to(this.docId).emit('set_name', clientId, name);
|
|
||||||
};
|
|
||||||
|
|
||||||
EditorSocketIOServer.prototype.setColor = function (socket, color) {
|
|
||||||
var clientId = socket.id;
|
|
||||||
this.getClient(clientId).color = color;
|
|
||||||
socket.broadcast.to(this.docId).emit('set_color', clientId, color);
|
|
||||||
};
|
|
||||||
|
|
||||||
EditorSocketIOServer.prototype.getClient = function (clientId) {
|
|
||||||
return this.users[clientId] || (this.users[clientId] = {});
|
|
||||||
};
|
|
||||||
|
|
||||||
EditorSocketIOServer.prototype.onDisconnect = function (socket) {
|
|
||||||
var clientId = socket.id;
|
|
||||||
delete this.users[clientId];
|
|
||||||
socket.broadcast.to(this.docId).emit('client_left', clientId);
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = EditorSocketIOServer;
|
|
155
lib/ot/editor-socketio-server.ts
Executable file
155
lib/ot/editor-socketio-server.ts
Executable file
|
@ -0,0 +1,155 @@
|
||||||
|
import { EventEmitter } from 'events'
|
||||||
|
import { logger } from '../logger'
|
||||||
|
import Selection from './selection'
|
||||||
|
import Server from './server'
|
||||||
|
import TextOperation from './text-operation'
|
||||||
|
import WrappedOperation from './wrapped-operation'
|
||||||
|
|
||||||
|
export class EditorSocketIOServer extends Server {
|
||||||
|
private readonly users: {}
|
||||||
|
private readonly docId: any
|
||||||
|
private mayWrite: any
|
||||||
|
|
||||||
|
constructor (document, operations, docId, mayWrite, operationCallback) {
|
||||||
|
super(document, operations)
|
||||||
|
// Whatever that does?
|
||||||
|
EventEmitter.call(this)
|
||||||
|
this.users = {}
|
||||||
|
this.docId = docId
|
||||||
|
this.mayWrite = mayWrite || function (_, cb) {
|
||||||
|
cb(true)
|
||||||
|
}
|
||||||
|
this.operationCallback = operationCallback
|
||||||
|
}
|
||||||
|
|
||||||
|
addClient (socket) {
|
||||||
|
const self = this
|
||||||
|
socket.join(this.docId)
|
||||||
|
const docOut = {
|
||||||
|
str: this.document,
|
||||||
|
revision: this.operations.length,
|
||||||
|
clients: this.users
|
||||||
|
}
|
||||||
|
socket.emit('doc', docOut)
|
||||||
|
socket.on('operation', function (revision, operation, selection) {
|
||||||
|
socket.origin = 'operation'
|
||||||
|
self.mayWrite(socket, function (mayWrite) {
|
||||||
|
if (!mayWrite) {
|
||||||
|
logger.info("User doesn't have the right to edit.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
self.onOperation(socket, revision, operation, selection)
|
||||||
|
if (typeof self.operationCallback === 'function')
|
||||||
|
self.operationCallback(socket, operation)
|
||||||
|
} catch (err) {
|
||||||
|
setTimeout(function () {
|
||||||
|
const docOut = {
|
||||||
|
str: self.document,
|
||||||
|
revision: self.operations.length,
|
||||||
|
clients: self.users,
|
||||||
|
force: true
|
||||||
|
}
|
||||||
|
socket.emit('doc', docOut)
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
socket.on('get_operations', function (base, head) {
|
||||||
|
self.onGetOperations(socket, base, head)
|
||||||
|
})
|
||||||
|
socket.on('selection', function (obj) {
|
||||||
|
socket.origin = 'selection'
|
||||||
|
self.mayWrite(socket, function (mayWrite) {
|
||||||
|
if (!mayWrite) {
|
||||||
|
logger.info("User doesn't have the right to edit.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.updateSelection(socket, obj && Selection.fromJSON(obj))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
socket.on('disconnect', function () {
|
||||||
|
logger.debug("Disconnect")
|
||||||
|
socket.leave(self.docId)
|
||||||
|
self.onDisconnect(socket)
|
||||||
|
/*
|
||||||
|
if (socket.manager && socket.manager.sockets.clients(self.docId).length === 0) {
|
||||||
|
self.emit('empty-room');
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
onOperation (socket, revision, operation, selection) {
|
||||||
|
let wrapped
|
||||||
|
try {
|
||||||
|
wrapped = new WrappedOperation(
|
||||||
|
TextOperation.fromJSON(operation),
|
||||||
|
selection && Selection.fromJSON(selection)
|
||||||
|
)
|
||||||
|
} catch (exc) {
|
||||||
|
logger.error("Invalid operation received: ")
|
||||||
|
logger.error(exc)
|
||||||
|
throw new Error(exc)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const clientId = socket.id
|
||||||
|
const wrappedPrime = this.receiveOperation(revision, wrapped)
|
||||||
|
if (!wrappedPrime) return
|
||||||
|
logger.debug("new operation: " + JSON.stringify(wrapped))
|
||||||
|
this.getClient(clientId).selection = wrappedPrime.meta
|
||||||
|
revision = this.operations.length
|
||||||
|
socket.emit('ack', revision)
|
||||||
|
socket.broadcast.in(this.docId).emit(
|
||||||
|
'operation', clientId, revision,
|
||||||
|
wrappedPrime.wrapped.toJSON(), wrappedPrime.meta
|
||||||
|
)
|
||||||
|
//set document is dirty
|
||||||
|
this.isDirty = true
|
||||||
|
} catch (exc) {
|
||||||
|
logger.error(exc)
|
||||||
|
throw new Error(exc)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onGetOperations (socket, base, head) {
|
||||||
|
const operations = this.operations.slice(base, head).map(function (op) {
|
||||||
|
return op.wrapped.toJSON()
|
||||||
|
})
|
||||||
|
socket.emit('operations', head, operations)
|
||||||
|
};
|
||||||
|
|
||||||
|
updateSelection (socket, selection) {
|
||||||
|
const clientId = socket.id
|
||||||
|
if (selection) {
|
||||||
|
this.getClient(clientId).selection = selection
|
||||||
|
} else {
|
||||||
|
delete this.getClient(clientId).selection
|
||||||
|
}
|
||||||
|
socket.broadcast.to(this.docId).emit('selection', clientId, selection)
|
||||||
|
};
|
||||||
|
|
||||||
|
setName (socket, name) {
|
||||||
|
const clientId = socket.id
|
||||||
|
this.getClient(clientId).name = name
|
||||||
|
socket.broadcast.to(this.docId).emit('set_name', clientId, name)
|
||||||
|
};
|
||||||
|
|
||||||
|
setColor (socket, color) {
|
||||||
|
const clientId = socket.id
|
||||||
|
this.getClient(clientId).color = color
|
||||||
|
socket.broadcast.to(this.docId).emit('set_color', clientId, color)
|
||||||
|
};
|
||||||
|
|
||||||
|
getClient (clientId) {
|
||||||
|
return this.users[clientId] || (this.users[clientId] = {})
|
||||||
|
};
|
||||||
|
|
||||||
|
onDisconnect (socket) {
|
||||||
|
const clientId = socket.id
|
||||||
|
delete this.users[clientId]
|
||||||
|
socket.broadcast.to(this.docId).emit('client_left', clientId)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import async from 'async'
|
||||||
import cookieParser from 'cookie-parser'
|
import cookieParser from 'cookie-parser'
|
||||||
import cookie from 'cookie'
|
import cookie from 'cookie'
|
||||||
import Chance from 'chance'
|
import Chance from 'chance'
|
||||||
|
import { EditorSocketIOServer } from './ot/editor-socketio-server'
|
||||||
|
|
||||||
const chance = new Chance()
|
const chance = new Chance()
|
||||||
|
|
||||||
|
@ -577,7 +578,7 @@ function startConnection (socket) {
|
||||||
const body = note.content
|
const body = note.content
|
||||||
const createtime = note.createdAt
|
const createtime = note.createdAt
|
||||||
const updatetime = note.lastchangeAt
|
const updatetime = note.lastchangeAt
|
||||||
const server = new ot.EditorSocketIOServer(body, [], noteId, ifMayEdit, operationCallback)
|
const server = new EditorSocketIOServer(body, [], noteId, ifMayEdit, operationCallback)
|
||||||
|
|
||||||
const authors = {}
|
const authors = {}
|
||||||
for (let i = 0; i < note.authors.length; i++) {
|
for (let i = 0; i < note.authors.length; i++) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue