♻️(y-provider) replace y-webrtc-signaling by server-y-provider
We replace the y-webrtc-signaling app by the server-y-provider server. The server-y-provider server uses @hocuspocus to do collaborative editing on docs.
This commit is contained in:
@@ -1,168 +0,0 @@
|
|||||||
/**
|
|
||||||
* Based on https://github.com/yjs/y-webrtc/blob/master/bin/server.js
|
|
||||||
*/
|
|
||||||
import http from 'http';
|
|
||||||
|
|
||||||
import * as map from 'lib0/map';
|
|
||||||
import WebSocket, { WebSocketServer } from 'ws';
|
|
||||||
|
|
||||||
type MessageYJSType = {
|
|
||||||
type: string;
|
|
||||||
topics?: string[];
|
|
||||||
topic?: string;
|
|
||||||
clients?: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
type MessageYJSTypes = MessageYJSType | string;
|
|
||||||
|
|
||||||
const wsReadyStateConnecting = 0;
|
|
||||||
const wsReadyStateOpen = 1;
|
|
||||||
const pingTimeout = 30000;
|
|
||||||
const port = process.env.PORT || 4444;
|
|
||||||
|
|
||||||
const wss = new WebSocketServer({ noServer: true });
|
|
||||||
const topics = new Map<string, Set<{ url: string; conn: WebSocket }>>();
|
|
||||||
|
|
||||||
const send = (conn: WebSocket, message: MessageYJSTypes) => {
|
|
||||||
if (
|
|
||||||
conn.readyState !== wsReadyStateConnecting &&
|
|
||||||
conn.readyState !== wsReadyStateOpen
|
|
||||||
) {
|
|
||||||
conn.close();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
conn.send(JSON.stringify(message));
|
|
||||||
} catch (e) {
|
|
||||||
conn.close();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Setup a new client
|
|
||||||
*/
|
|
||||||
const onconnection = (conn: WebSocket, url: string) => {
|
|
||||||
const subscribedTopics = new Set<string>();
|
|
||||||
let closed = false;
|
|
||||||
// Check if connection is still alive
|
|
||||||
let pongReceived = true;
|
|
||||||
|
|
||||||
const pingInterval = setInterval(() => {
|
|
||||||
if (!pongReceived) {
|
|
||||||
conn.close();
|
|
||||||
clearInterval(pingInterval);
|
|
||||||
} else {
|
|
||||||
pongReceived = false;
|
|
||||||
try {
|
|
||||||
conn.ping();
|
|
||||||
} catch (e) {
|
|
||||||
conn.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, pingTimeout);
|
|
||||||
|
|
||||||
conn.on('pong', () => {
|
|
||||||
pongReceived = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
conn.on('close', () => {
|
|
||||||
subscribedTopics.forEach((topicName) => {
|
|
||||||
const subs = topics.get(topicName) || new Set();
|
|
||||||
subs.forEach((sub) => {
|
|
||||||
if (sub.url === url && sub.conn === conn) {
|
|
||||||
subs.delete(sub);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (subs.size === 0) {
|
|
||||||
topics.delete(topicName);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
subscribedTopics.clear();
|
|
||||||
closed = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
conn.on('message', (message: MessageYJSTypes) => {
|
|
||||||
if (typeof message === 'string' || message instanceof Buffer) {
|
|
||||||
message = JSON.parse(message.toString()) as MessageYJSType;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message && message.type && !closed) {
|
|
||||||
switch (message.type) {
|
|
||||||
case 'subscribe':
|
|
||||||
(message.topics || []).forEach((topicName) => {
|
|
||||||
if (typeof topicName === 'string') {
|
|
||||||
// add conn to topic
|
|
||||||
const topic = map.setIfUndefined(
|
|
||||||
topics,
|
|
||||||
topicName,
|
|
||||||
() => new Set(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let isAlreadyAdded = false;
|
|
||||||
topic.forEach((sub) => {
|
|
||||||
if (sub.url === url && sub.conn === conn) {
|
|
||||||
isAlreadyAdded = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!isAlreadyAdded) {
|
|
||||||
topic.add({ url, conn });
|
|
||||||
subscribedTopics.add(topicName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'unsubscribe':
|
|
||||||
(message.topics || []).forEach((topicName) => {
|
|
||||||
const subs = topics.get(topicName);
|
|
||||||
if (subs) {
|
|
||||||
subs.forEach((sub) => {
|
|
||||||
if (sub.conn === conn) {
|
|
||||||
subs.delete(sub);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'publish':
|
|
||||||
if (message.topic) {
|
|
||||||
const receivers = topics.get(message.topic);
|
|
||||||
if (receivers) {
|
|
||||||
message.clients = receivers.size;
|
|
||||||
receivers.forEach(({ url: receiverUrl, conn: receiverConn }) => {
|
|
||||||
if (receiverUrl === url) {
|
|
||||||
send(receiverConn, message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'ping':
|
|
||||||
send(conn, { type: 'pong' });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
wss.on('connection', (conn, request) => {
|
|
||||||
const url = request.url;
|
|
||||||
onconnection(conn, url || '');
|
|
||||||
});
|
|
||||||
|
|
||||||
const server = http.createServer((request, response) => {
|
|
||||||
response.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
||||||
response.end('okay');
|
|
||||||
});
|
|
||||||
|
|
||||||
server.on('upgrade', (request, socket, head) => {
|
|
||||||
const handleAuth = (ws: WebSocket) => {
|
|
||||||
wss.emit('connection', ws, request);
|
|
||||||
};
|
|
||||||
wss.handleUpgrade(request, socket, head, handleAuth);
|
|
||||||
});
|
|
||||||
|
|
||||||
server.listen(port);
|
|
||||||
|
|
||||||
console.log('Signaling server running on port :', port);
|
|
||||||
@@ -5,7 +5,8 @@
|
|||||||
"workspaces": {
|
"workspaces": {
|
||||||
"packages": [
|
"packages": [
|
||||||
"apps/*",
|
"apps/*",
|
||||||
"packages/*"
|
"packages/*",
|
||||||
|
"servers/*"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "y-webrtc-signaling",
|
"name": "server-y-provider",
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"description": "WebRTC server for Yjs",
|
"description": "Y.js provider for docs",
|
||||||
"repository": "https://github.com/numerique-gouv/impress",
|
"repository": "https://github.com/numerique-gouv/impress",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -15,12 +15,10 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lib0": "0.2.97",
|
"@hocuspocus/server": "2.13.5"
|
||||||
"ws": "8.18.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
"@types/ws": "8.5.12",
|
|
||||||
"eslint-config-impress": "*",
|
"eslint-config-impress": "*",
|
||||||
"nodemon": "3.1.4",
|
"nodemon": "3.1.4",
|
||||||
"ts-jest": "29.2.5",
|
"ts-jest": "29.2.5",
|
||||||
18
src/frontend/servers/y-provider/src/server.ts
Normal file
18
src/frontend/servers/y-provider/src/server.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { Server } from '@hocuspocus/server';
|
||||||
|
|
||||||
|
const port = Number(process.env.PORT || 4444);
|
||||||
|
|
||||||
|
const server = Server.configure({
|
||||||
|
name: 'docs-y-provider',
|
||||||
|
port: port,
|
||||||
|
timeout: 30000,
|
||||||
|
debounce: 2000,
|
||||||
|
maxDebounce: 30000,
|
||||||
|
quiet: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
server.listen().catch((error) => {
|
||||||
|
console.error('Failed to start the server:', error);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Websocket server running on port :', port);
|
||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user