feat(workspace): implement Yjs CRDT real-time collaborative editing (resolves FLAW-01)
MindSift CI & Docker Release / Build, Test in Container & Push (push) Failing after 29s

This commit is contained in:
sandrodach committed 2026-10-26 15:18:48 +01:00
1 parent 0bdeb5f320
commit ef8cf8f022
13 files changed
+1028 -85

No files matched your search

+72
View File
@@ -232,6 +232,25 @@ func (s *Store) migrate() error {
PRIMARY KEY(user_id, tag)
);
CREATE TABLE IF NOT EXISTS document_crdt_state (
doc_id TEXT PRIMARY KEY,
state_blob BLOB NOT NULL,
updated_at TEXT NOT NULL,
updated_by TEXT NOT NULL DEFAULT '',
FOREIGN KEY(doc_id) REFERENCES documents(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS document_crdt_updates (
id INTEGER PRIMARY KEY AUTOINCREMENT,
doc_id TEXT NOT NULL,
update_blob BLOB NOT NULL,
created_at TEXT NOT NULL,
created_by TEXT NOT NULL DEFAULT '',
FOREIGN KEY(doc_id) REFERENCES documents(id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_doc_crdt_updates_doc ON document_crdt_updates(doc_id);
CREATE TABLE IF NOT EXISTS lists (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
@@ -1559,6 +1578,59 @@ func (s *Store) PatchDocumentBlocks(userID, docID string, req domain.DocBlocksPa
return doc, mergedOrder, nil
}
// GetDocumentCRDTState returns the compacted CRDT state blob and any pending updates for a document.
func (s *Store) GetDocumentCRDTState(docID string) ([]byte, [][]byte, error) {
var stateBlob []byte
err := s.db.QueryRow(`SELECT state_blob FROM document_crdt_state WHERE doc_id = ?`, docID).Scan(&stateBlob)
if err != nil && err != sql.ErrNoRows {
return nil, nil, err
}
rows, err := s.db.Query(`SELECT update_blob FROM document_crdt_updates WHERE doc_id = ? ORDER BY id ASC`, docID)
if err != nil {
return stateBlob, nil, nil
}
defer rows.Close()
var updates [][]byte
for rows.Next() {
var u []byte
if err := rows.Scan(&u); err == nil && len(u) > 0 {
updates = append(updates, u)
}
}
return stateBlob, updates, nil
}
// AppendDocumentCRDTUpdate appends a new CRDT update delta for a document.
func (s *Store) AppendDocumentCRDTUpdate(docID string, update []byte, userID string) error {
now := domain.NowISO()
_, err := s.db.Exec(`
INSERT INTO document_crdt_updates (doc_id, update_blob, created_at, created_by)
VALUES (?, ?, ?, ?)
`, docID, update, now, userID)
return err
}
// SaveDocumentCRDTState saves a compacted snapshot of the document CRDT state and prunes older incremental updates.
func (s *Store) SaveDocumentCRDTState(docID string, stateBlob []byte, userID string) error {
now := domain.NowISO()
_, err := s.db.Exec(`
INSERT INTO document_crdt_state (doc_id, state_blob, updated_at, updated_by)
VALUES (?, ?, ?, ?)
ON CONFLICT(doc_id) DO UPDATE SET
state_blob = excluded.state_blob,
updated_at = excluded.updated_at,
updated_by = excluded.updated_by
`, docID, stateBlob, now, userID)
if err != nil {
return err
}
// Prune incremental updates older than the snapshot
_, _ = s.db.Exec(`DELETE FROM document_crdt_updates WHERE doc_id = ?`, docID)
return nil
}
// GetAuthorizedDocUsers returns all user IDs who have read/write/owner access to docID.
func (s *Store) GetAuthorizedDocUsers(docID string) ([]string, error) {
userSet := make(map[string]bool)