feat(workspace): tabula-rasa outliner architecture, SQLite blocks/block_tags schema & echo-protected live room sync
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m12s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m12s
This commit is contained in:
1 parent
f3b3f07e04
commit
86867b0d6f
17 files changed
+3409
-7178
No files matched your search
+6
-84
@@ -1,7 +1,6 @@
|
||||
package ws
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
@@ -47,10 +46,9 @@ type docLockItem struct {
|
||||
expiresAt time.Time
|
||||
}
|
||||
|
||||
type CRDTStore interface {
|
||||
GetDocumentCRDTState(docID string) ([]byte, [][]byte, error)
|
||||
AppendDocumentCRDTUpdate(docID string, update []byte, userID string) error
|
||||
SaveDocumentCRDTState(docID string, stateBlob []byte, userID string) error
|
||||
type DocStore interface {
|
||||
LoadDocumentState(docID string) (string, []domain.DocumentBlock, error)
|
||||
FlushDocumentState(docID string, title string, blocks []domain.DocumentBlock) error
|
||||
}
|
||||
|
||||
type Hub struct {
|
||||
@@ -63,7 +61,7 @@ type Hub struct {
|
||||
sseRooms map[string]map[*SSEClient]bool
|
||||
locks map[string]*docLockItem
|
||||
docRooms map[string]*DocRoom
|
||||
crdtStore CRDTStore
|
||||
docStore DocStore
|
||||
}
|
||||
|
||||
func NewHub() *Hub {
|
||||
@@ -81,10 +79,10 @@ func NewHub() *Hub {
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *Hub) SetCRDTStore(store CRDTStore) {
|
||||
func (h *Hub) SetDocStore(store DocStore) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
h.crdtStore = store
|
||||
h.docStore = store
|
||||
}
|
||||
|
||||
func (h *Hub) Register(c *Client) {
|
||||
@@ -688,39 +686,6 @@ func (h *Hub) ServeSSE(w http.ResponseWriter, r *http.Request, roomID, userID st
|
||||
fmt.Fprintf(w, "data: %s\n\n", connectedMsg)
|
||||
flusher.Flush()
|
||||
|
||||
// If initial rooms had doc rooms, send CRDT_INIT
|
||||
if h.crdtStore != nil {
|
||||
for rID := range client.subscribedRooms {
|
||||
if strings.HasPrefix(rID, "doc:") {
|
||||
docID := strings.TrimPrefix(rID, "doc:")
|
||||
stateBlob, updates, err := h.crdtStore.GetDocumentCRDTState(docID)
|
||||
if err == nil && (len(stateBlob) > 0 || len(updates) > 0) {
|
||||
var b64Updates []string
|
||||
for _, u := range updates {
|
||||
b64Updates = append(b64Updates, base64.StdEncoding.EncodeToString(u))
|
||||
}
|
||||
b64State := ""
|
||||
if len(stateBlob) > 0 {
|
||||
b64State = base64.StdEncoding.EncodeToString(stateBlob)
|
||||
}
|
||||
initMsg := domain.WSMessage{
|
||||
Event: "CRDT_INIT",
|
||||
Payload: map[string]interface{}{
|
||||
"docId": docID,
|
||||
"state": b64State,
|
||||
"updates": b64Updates,
|
||||
},
|
||||
Timestamp: domain.NowISO(),
|
||||
}
|
||||
if initBytes, err := json.Marshal(initMsg); err == nil {
|
||||
fmt.Fprintf(w, "data: %s\n\n", initBytes)
|
||||
flusher.Flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(15 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
@@ -811,49 +776,6 @@ func (c *Client) readPump() {
|
||||
if c.hub.ReleaseDocLock(docID, c.userID, false) {
|
||||
c.hub.BroadcastDocEvent(docID, "DOC_UNLOCKED", map[string]string{"docId": docID, "doc_id": docID})
|
||||
}
|
||||
case "CRDT_JOIN":
|
||||
c.hub.JoinRoom(c, "doc:"+docID)
|
||||
room := c.hub.GetOrCreateDocRoom(docID)
|
||||
b64State, b64Updates := room.GetInitialSnapshot()
|
||||
if len(b64State) > 0 || len(b64Updates) > 0 {
|
||||
c.SendEvent("CRDT_INIT", map[string]interface{}{
|
||||
"docId": docID,
|
||||
"state": b64State,
|
||||
"updates": b64Updates,
|
||||
})
|
||||
}
|
||||
case "CRDT_UPDATE":
|
||||
var p struct {
|
||||
Update string `json:"update"`
|
||||
}
|
||||
_ = json.Unmarshal(incoming.Payload, &p)
|
||||
if p.Update != "" {
|
||||
if raw, err := base64.StdEncoding.DecodeString(p.Update); err == nil && len(raw) > 0 {
|
||||
room := c.hub.GetOrCreateDocRoom(docID)
|
||||
room.ApplyUpdate(raw, c.userID, nil, nil)
|
||||
}
|
||||
}
|
||||
case "CRDT_COMPACT":
|
||||
var p struct {
|
||||
State string `json:"state"`
|
||||
}
|
||||
_ = json.Unmarshal(incoming.Payload, &p)
|
||||
if p.State != "" {
|
||||
if raw, err := base64.StdEncoding.DecodeString(p.State); err == nil && len(raw) > 0 {
|
||||
room := c.hub.GetOrCreateDocRoom(docID)
|
||||
room.CompactState(raw, c.userID)
|
||||
}
|
||||
}
|
||||
case "CRDT_AWARENESS":
|
||||
c.hub.BroadcastRoom("doc:"+docID, "CRDT_AWARENESS", incoming.Payload, c)
|
||||
room := c.hub.GetOrCreateDocRoom(docID)
|
||||
room.BroadcastAwareness(incoming.Payload, c.userID, nil, nil)
|
||||
case "CRDT_LEAVE":
|
||||
c.hub.LeaveRoom(c, "doc:"+docID)
|
||||
c.hub.BroadcastRoom("doc:"+docID, "CRDT_AWARENESS_LEAVE", map[string]interface{}{
|
||||
"docId": docID,
|
||||
"userId": c.userID,
|
||||
}, c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user