1- import type { CloseEvent } from "@hocuspocus/common" ;
21import {
3- beforeHandleMessagePayload ,
4- Extension ,
5- IncomingMessage ,
6- MessageType ,
2+ type beforeSyncPayload ,
3+ Extension
74} from "@hocuspocus/server" ;
85import * as syncProtocol from "y-protocols/sync" ;
96import * as Y from "yjs" ;
@@ -18,55 +15,7 @@ import * as Y from "yjs";
1815 * - if the update is accepted, we do nothing
1916 */
2017export class RejectUnauthorized implements Extension {
21- constructor ( private readonly threadsMapKey : string ) { }
22- /**
23- * Extract the yjsUpdate from the incoming message
24- * @param message
25- * @returns
26- */
27- private getYUpdate ( message : Uint8Array ) {
28- /**
29- * The messages we are interested in are of the following format:
30- * [docIdLength: number, ...docIdString: string, hocuspocusMessageType: number, ySyncMessageType: number, ...yjsUpdate: Uint8Array]
31- *
32- * We check that the hocuspocusMessageType is Sync and that the ySyncMessageType is messageYjsUpdate.
33- */
34- const incomingMessage = new IncomingMessage ( message ) ;
35- // Read the docID string, but don't use it
36- incomingMessage . readVarString ( ) ;
37-
38- // Read the hocuspocusMessageType
39- const hocuspocusMessageType = incomingMessage . readVarUint ( ) ;
40- // If the hocuspocusMessageType is not Sync, we don't handle the message, since it is not an update
41- if (
42- ! (
43- hocuspocusMessageType === MessageType . Sync ||
44- hocuspocusMessageType === MessageType . SyncReply
45- )
46- ) {
47- return ;
48- }
49-
50- // Read the ySyncMessageType
51- const ySyncMessageType = incomingMessage . readVarUint ( ) ;
52-
53- // If the ySyncMessageType is not a messageYjsUpdate or a messageYjsSyncStep2, we don't handle the message, since it is not an update
54- if (
55- ! (
56- ySyncMessageType === syncProtocol . messageYjsUpdate ||
57- ySyncMessageType === syncProtocol . messageYjsSyncStep2
58- )
59- ) {
60- // not an update we want to handle
61- return ;
62- }
63-
64- // Read the yjsUpdate
65- const yUpdate = incomingMessage . readVarUint8Array ( ) ;
66-
67- return yUpdate ;
68- }
69-
18+ constructor ( private readonly threadsMapKey : string , private readonly onReject ?: ( payload : beforeSyncPayload ) => void ) { }
7019 /**
7120 * This function protects against changes to the restricted type.
7221 * It does this by:
@@ -112,29 +61,31 @@ export class RejectUnauthorized implements Extension {
11261 return didNeedToUndo ;
11362 }
11463
115- async beforeHandleMessage ( {
116- update,
117- document : ydoc ,
118- } : beforeHandleMessagePayload ) {
119- const yUpdate = this . getYUpdate ( update ) ;
120-
121- if ( ! yUpdate ) {
64+ /**
65+ * Before the document is synchronized, we check if the update modifies the restricted type.
66+ * If it does, we reject the update by undoing it, and calling the onReject callback.
67+ */
68+ async beforeSync ( data : beforeSyncPayload ) {
69+ // If the ySyncMessageType is not a messageYjsUpdate or a messageYjsSyncStep2, we don't handle the message, since it is not an update
70+ if (
71+ ! (
72+ data . type === syncProtocol . messageYjsUpdate ||
73+ data . type === syncProtocol . messageYjsSyncStep2
74+ )
75+ ) {
76+ // not an update we want to handle
12277 return ;
12378 }
12479
125- const protectedType = ydoc . getMap ( this . threadsMapKey ) ;
80+ const protectedType = data . document . getMap ( this . threadsMapKey ) ;
12681 const didRollback = this . applyUpdateAndRollbackIfNeeded (
127- yUpdate ,
128- ydoc ,
82+ data . payload ,
83+ data . document ,
12984 protectedType
13085 ) ;
13186
13287 if ( didRollback ) {
133- // TODO, we can close their connection or just let them continue, since we've already undone their changes (and our changes are newer than theirs)
134- const error = {
135- reason : `Modification of a restricted type: ${ this . threadsMapKey } was rejected` ,
136- } satisfies Partial < CloseEvent > ;
137- throw error ;
88+ this . onReject ?.( data )
13889 }
13990 }
14091}
0 commit comments