complete v4 migration

This commit is contained in:
Philipinho
2026-07-18 01:18:51 +01:00
parent f008e536a2
commit 9fb0ab3ad6
2 changed files with 24 additions and 19 deletions
@@ -15,6 +15,7 @@ import {
RedisSyncExtension, RedisSyncExtension,
SerializedHTTPRequest, SerializedHTTPRequest,
} from './extensions/redis-sync'; } from './extensions/redis-sync';
import { toWebRequest } from './extensions/redis-sync/redis-sync.types';
import { WsSocketWrapper } from './extensions/redis-sync/ws-socket-wrapper'; import { WsSocketWrapper } from './extensions/redis-sync/ws-socket-wrapper';
import RedisClient from 'ioredis'; import RedisClient from 'ioredis';
import { pack, unpack } from 'msgpackr'; import { pack, unpack } from 'msgpackr';
@@ -98,34 +99,36 @@ export class CollaborationGateway {
const serializedHTTPRequest = this.serializeRequest(request); const serializedHTTPRequest = this.serializeRequest(request);
const socketId = serializedHTTPRequest.headers['sec-websocket-key']; const socketId = serializedHTTPRequest.headers['sec-websocket-key'];
// Create wrapper socket that only receives events via emit()
// This prevents double-handling since Hocuspocus won't listen to raw WebSocket events
const wrappedSocket = new WsSocketWrapper(client); const wrappedSocket = new WsSocketWrapper(client);
// Route through RedisSync extension (this calls handleConnection internally) // Route through RedisSync extension (this calls handleConnection internally)
this.redisSync.onSocketOpen(wrappedSocket as any, serializedHTTPRequest); this.redisSync.onSocketOpen(wrappedSocket, serializedHTTPRequest);
// Forward raw WebSocket messages to the extension
client.on('message', (data: ArrayBuffer) => { client.on('message', (data: ArrayBuffer) => {
this.redisSync!.onSocketMessage( this.redisSync!.onSocketMessage(serializedHTTPRequest, data);
wrappedSocket as any,
serializedHTTPRequest,
data,
);
}); });
// Forward close events
client.on('close', (code: number, reason: Buffer) => { client.on('close', (code: number, reason: Buffer) => {
this.redisSync!.onSocketClose(socketId, code, reason.buffer as ArrayBuffer); this.redisSync!.onSocketClose(
}); socketId,
code,
// Forward pong events for keepalive new Uint8Array(reason).buffer,
client.on('pong', (data: Buffer) => { );
wrappedSocket.emit('pong', data);
}); });
} else { } else {
// Fallback to direct Hocuspocus connection // Fallback to direct Hocuspocus connection
this.hocuspocus.handleConnection(client, request); const clientConnection = this.hocuspocus.handleConnection(
client,
toWebRequest(this.serializeRequest(request)),
);
client.on('message', (data: Buffer) => {
clientConnection.handleMessage(new Uint8Array(data));
});
client.on('close', (code: number, reason: Buffer) => {
clientConnection.handleClose({ code, reason: reason.toString() });
});
} }
} }
@@ -178,6 +181,7 @@ export class CollaborationGateway {
if (this.hocuspocus.getDocumentsCount() === 0) resolve(''); if (this.hocuspocus.getDocumentsCount() === 0) resolve('');
this.hocuspocus.closeConnections(); this.hocuspocus.closeConnections();
this.hocuspocus.flushPendingStores();
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
@@ -2,8 +2,9 @@ import type WebSocket from 'ws';
import type { WebSocketLike } from '@hocuspocus/server'; import type { WebSocketLike } from '@hocuspocus/server';
/** /**
* Wrapper around ws WebSocket that only receives events via emit(). * Wrapper around ws WebSocket that Hocuspocus only writes to.
* This prevents double-handling when used with RedisSyncExtension. * Incoming socket events are forwarded separately by the gateway,
* which prevents double-handling with RedisSyncExtension.
*/ */
export class WsSocketWrapper implements WebSocketLike { export class WsSocketWrapper implements WebSocketLike {
private ws: WebSocket; private ws: WebSocket;