forked from onexdata/realtime-editor.js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.js
128 lines (112 loc) · 3.76 KB
/
server.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
const express = require('express');
const WebSocket = require('ws');
const http = require('http');
const ShareDB = require('sharedb');
const WebSocketJSONStream = require('websocket-json-stream');
const path = require('path');
const url = require('url');
// Configure ShareDB with in-memory database
const backend = new ShareDB();
// Create new document
const connection = backend.connect();
const doc = connection.get('documents', 'example-doc');
doc.fetch(function(err) {
if (err) throw err;
if (doc.type === null) {
doc.create({
time: Date.now(),
blocks: [],
version: "2.28.2"
}, function(err) {
if (err) throw err;
console.log('Created document');
});
}
});
// Set up Express
const app = express();
app.use(express.static('public'));
app.use(express.json());
// Create HTTP server
const server = http.createServer(app);
// Create separate WebSocket servers for ShareDB and cursors
const shareDBServer = new WebSocket.Server({
noServer: true,
path: '/sharedb'
});
const cursorServer = new WebSocket.Server({
noServer: true,
path: '/cursors'
});
// Handle ShareDB connections
shareDBServer.on('connection', function(ws) {
const stream = new WebSocketJSONStream(ws);
backend.listen(stream);
});
// Store active cursor connections
const cursorConnections = new Map();
// Handle cursor update connections
cursorServer.on('connection', function(ws) {
ws.on('message', function(data) {
try {
const message = JSON.parse(data);
if (message.type === 'cursor') {
if (message.type === 'remove') {
cursorConnections.delete(message.clientId);
} else {
cursorConnections.set(message.clientId, {
ws,
position: message.position,
clientInfo: message.clientInfo
});
}
// Broadcast to all other cursor connections
cursorServer.clients.forEach(function(client) {
if (client !== ws && client.readyState === WebSocket.OPEN) {
client.send(data.toString());
}
});
}
} catch (err) {
console.error('Error processing cursor message:', err);
}
});
ws.on('close', function() {
// Find and remove the disconnected client's cursor
cursorConnections.forEach((value, key) => {
if (value.ws === ws) {
cursorConnections.delete(key);
// Broadcast removal to other clients
cursorServer.clients.forEach(function(client) {
if (client !== ws && client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify({
type: 'cursor',
clientId: key,
type: 'remove'
}));
}
});
}
});
});
});
// Route WebSocket connections to appropriate server
server.on('upgrade', function(request, socket, head) {
const pathname = url.parse(request.url).pathname;
if (pathname === '/sharedb') {
shareDBServer.handleUpgrade(request, socket, head, function(ws) {
shareDBServer.emit('connection', ws, request);
});
} else if (pathname === '/cursors') {
cursorServer.handleUpgrade(request, socket, head, function(ws) {
cursorServer.emit('connection', ws, request);
});
} else {
socket.destroy();
}
});
// Start server
const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});