11package backend
22
33import (
4+ "encoding/base64"
45 "errors"
56 "fmt"
67 automerge "github.com/automerge/automerge-go"
@@ -11,20 +12,31 @@ import (
1112
1213type (
1314 SyncRequest struct {
14- Type string `json:"type" xml:"type" form:"type" query:"type"`
15- RequestId string `json:"requestId" xml:"requestId" form:"requestId" query:"requestId"`
16- DocumentId string `json:"documentId" xml:"documentId" form:"documentId" query:"documentId"`
17- SyncState * automerge.SyncMessage `json:"syncState" xml:"syncState" form:"syncState" query:"syncState"`
15+ Type string `json:"type" xml:"type" form:"type" query:"type"`
16+ RequestId string `json:"requestId" xml:"requestId" form:"requestId" query:"requestId"`
17+ DocumentId string `json:"documentId" xml:"documentId" form:"documentId" query:"documentId"`
18+ // *automerge.Doc as base64 encoded string
19+ DocumentState string `json:"documentState" xml:"documentState" form:"documentState" query:"documentState"`
20+ // *automerge.SyncMessage as base64 encoded string
21+ SyncMessage string `json:"syncMessage" xml:"syncMessage" form:"syncMessage" query:"syncMessage"`
1822 }
1923)
2024
25+ func (s SyncRequest ) GetSyncMessageBytes () ([]byte , error ) {
26+ decodedBytes , err := base64 .StdEncoding .DecodeString (s .SyncMessage )
27+ if err != nil {
28+ return nil , err
29+ }
30+ return decodedBytes , nil
31+ }
32+
2133// AutomergeSyncManager manages processing of SyncRequests from clients
2234type AutomergeSyncManager struct {
2335 treeManager * TreeManager
2436 websocketConnectionManager * WebsocketConnectionManager
2537
2638 // automergeDocuments client connection -> automerge.Doc
27- automergeDocuments map [* websocket.Conn ]string
39+ automergeDocuments map [* websocket.Conn ]* automerge. Doc
2840
2941 // lock for synchronizing the tree to the disk
3042 lock mutexSync.RWMutex
@@ -35,7 +47,7 @@ func NewAutomergeSyncManager(
3547) * AutomergeSyncManager {
3648 s := & AutomergeSyncManager {
3749 treeManager : treeManager ,
38- automergeDocuments : make (map [* websocket.Conn ]string ),
50+ automergeDocuments : make (map [* websocket.Conn ]* automerge. Doc ),
3951 }
4052
4153 return s
@@ -59,8 +71,8 @@ func (sm *AutomergeSyncManager) IsItemBeingEditedRecursive(s *Section) (err erro
5971}
6072
6173// sets the initial server shadow for a new client connection
62- func (sm * AutomergeSyncManager ) initClient (conn * websocket.Conn , shadowContent string ) {
63- sm .automergeDocuments [conn ] = shadowContent
74+ func (sm * AutomergeSyncManager ) initClient (conn * websocket.Conn , document * automerge. Doc ) {
75+ sm .automergeDocuments [conn ] = document
6476}
6577
6678// removes the shadow for the given client
@@ -98,19 +110,25 @@ func (sm *AutomergeSyncManager) handleSyncRequest(client *websocket.Conn, syncRe
98110 syncState := automerge .NewSyncState (automergeDocument )
99111 // sm.automergeDocuments[client]
100112
101- _ , err = syncState .ReceiveMessage (syncRequest .SyncState .Bytes ())
113+ syncMessageBytes , err := syncRequest .GetSyncMessageBytes ()
114+ if err != nil {
115+ log .Printf ("%v: error getting sync message bytes: %v" , client .RemoteAddr (), err )
116+ return err
117+ }
118+ _ , err = syncState .ReceiveMessage (syncMessageBytes )
102119 if err != nil {
103120 log .Printf ("%v: error receiving sync state: %v" , client .RemoteAddr (), err )
104121 return err
105122 }
106123
124+ // NOTE: this is not needed when using the SyncState API
107125 // apply patches to the automerge document
108- for _ , change := range syncRequest .SyncState .Changes () {
109- err := automergeDocument .Apply (change )
110- if err != nil {
111- return err
112- }
113- }
126+ // for _, change := range syncRequest.SyncMessage .Changes() {
127+ // err := automergeDocument.Apply(change)
128+ // if err != nil {
129+ // return err
130+ // }
131+ // }
114132
115133 // then patch the server document version
116134 d := sm .treeManager .GetDocument (documentId )
@@ -121,21 +139,21 @@ func (sm *AutomergeSyncManager) handleSyncRequest(client *websocket.Conn, syncRe
121139 }
122140 d .Content = patchedText
123141
124- err = sm .sendSyncRequestResponse (client , documentId )
125- if err != nil {
126- log .Printf ("%v: error sending response: %v" , client .RemoteAddr (), err )
127- return err
128- }
142+ // err = sm.sendSyncRequestResponse(client, documentId)
143+ // if err != nil {
144+ // log.Printf("%v: error sending response: %v", client.RemoteAddr(), err)
145+ // return err
146+ // }
129147
130148 return err
131149}
132150
133151// send the latest document state to the client
134152func (sm * AutomergeSyncManager ) sendInitialTextResponse (client * websocket.Conn , document * Document ) (err error ) {
135- // set initial state in backend
136- sm .initClient (client , document .Content )
153+ automergeDocument , err := sm .getDocument (document .ID )
154+
155+ heads := automergeDocument .Heads ()
137156
138- automergeDocument := automerge .New ()
139157 syncState := automerge .NewSyncState (automergeDocument )
140158
141159 documentContentText := automergeDocument .Path ("content" ).Text ()
@@ -144,26 +162,34 @@ func (sm *AutomergeSyncManager) sendInitialTextResponse(client *websocket.Conn,
144162 return err
145163 }
146164
147- commitMessage := "Initial commit "
165+ commitMessage := "Initial Text "
148166 commit , err := automergeDocument .Commit (commitMessage )
149167 log .Printf ("Commit: %v" , commit )
150168 if err != nil {
151169 return err
152170 }
153171
172+ changes , err := automergeDocument .Changes (heads ... )
173+ changes [0 ].Save ()
174+
175+ // set initial state in backend
176+ sm .initClient (client , automergeDocument )
177+
154178 syncStateMessage , valid := syncState .GenerateMessage ()
155179 if valid == false {
156180 log .Printf ("Error generating sync state message: %v" , err )
157181 return err
158182 }
159183
160184 // Write current document state to the client
161- err = client .WriteJSON (SyncRequest {
162- Type : TypeInitialContent ,
163- DocumentId : document .ID ,
164- RequestId : "" ,
165- SyncState : syncStateMessage ,
166- })
185+ request := SyncRequest {
186+ Type : TypeInitialContent ,
187+ DocumentId : document .ID ,
188+ RequestId : "" ,
189+ DocumentState : encodeBase64 (automergeDocument .Save ()),
190+ SyncMessage : encodeBase64 (syncStateMessage .Bytes ()),
191+ }
192+ err = client .WriteJSON (request )
167193 if err != nil {
168194 log .Printf ("%v: error writing initial content response: %v" , client .RemoteAddr (), err )
169195 return err
@@ -172,6 +198,10 @@ func (sm *AutomergeSyncManager) sendInitialTextResponse(client *websocket.Conn,
172198 return
173199}
174200
201+ func encodeBase64 (buffer []byte ) string {
202+ return base64 .StdEncoding .EncodeToString (buffer )
203+ }
204+
175205// responds to a client with the changes from the server site document version
176206func (sm * AutomergeSyncManager ) sendSyncRequestResponse (client * websocket.Conn , documentId string ) (err error ) {
177207 // d := sm.treeManager.GetDocument(documentId)
@@ -192,10 +222,11 @@ func (sm *AutomergeSyncManager) sendSyncRequestResponse(client *websocket.Conn,
192222 return sm .websocketConnectionManager .syncStateToClient (
193223 client ,
194224 SyncRequest {
195- Type : TypeSyncRequest ,
196- RequestId : "" ,
197- DocumentId : documentId ,
198- SyncState : syncStateMessage ,
225+ Type : TypeSyncRequest ,
226+ RequestId : "" ,
227+ DocumentId : documentId ,
228+ DocumentState : encodeBase64 (automergeDocument .Save ()),
229+ SyncMessage : encodeBase64 (syncStateMessage .Bytes ()),
199230 })
200231}
201232
0 commit comments