Sync implementation using cr-sqlite + websockets, across JS and CLI clients #1
|
@ -14,9 +14,6 @@
|
||||||
const sqlite = await initWasm(() => wasmUrl);
|
const sqlite = await initWasm(() => wasmUrl);
|
||||||
db = await sqlite.open('todo.db');
|
db = await sqlite.open('todo.db');
|
||||||
|
|
||||||
await db.exec(`
|
|
||||||
DROP TABLE todos;
|
|
||||||
`);
|
|
||||||
await db.exec(`
|
await db.exec(`
|
||||||
CREATE TABLE IF NOT EXISTS todos (
|
CREATE TABLE IF NOT EXISTS todos (
|
||||||
id BLOB PRIMARY KEY NOT NULL,
|
id BLOB PRIMARY KEY NOT NULL,
|
||||||
|
@ -54,9 +51,9 @@
|
||||||
function sendChangesToServer(changes) {
|
function sendChangesToServer(changes) {
|
||||||
const formattedChanges = changes.map(change => ({
|
const formattedChanges = changes.map(change => ({
|
||||||
TableName: change[0],
|
TableName: change[0],
|
||||||
PK: Array.isArray(change[1]) ? btoa(change[1]) : btoa(change[1]),
|
PK: Array.isArray(change[1]) ? btoa(String.fromCharCode.apply(null, change[1])) : btoa(change[1]),
|
||||||
ColumnName: change[2],
|
ColumnName: change[2],
|
||||||
Value: Array.isArray(change[3]) ? btoa(String.fromCharCode.apply(null, change[3])) : btoa(change[3]),
|
Value: Array.isArray(change[3]) ? btoa(String.fromCharCode.apply(null, change[3])) : change[3],
|
||||||
ColVersion: Number(change[4]),
|
ColVersion: Number(change[4]),
|
||||||
DBVersion: Number(change[5]),
|
DBVersion: Number(change[5]),
|
||||||
SiteID: Array.isArray(change[6]) ? btoa(new TextDecoder('utf8').decode(change[6])) : btoa(change[6]),
|
SiteID: Array.isArray(change[6]) ? btoa(new TextDecoder('utf8').decode(change[6])) : btoa(change[6]),
|
||||||
|
|
3
main.go
3
main.go
|
@ -76,7 +76,6 @@ type CRSQLChange struct {
|
||||||
Seq int64
|
Seq int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (m *model) addTodo(todo string) {
|
func (m *model) addTodo(todo string) {
|
||||||
id, err := uuid.NewV7()
|
id, err := uuid.NewV7()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -114,7 +113,6 @@ func (m *model) addTodo(todo string) {
|
||||||
change.Value = base64.StdEncoding.EncodeToString(valueRaw)
|
change.Value = base64.StdEncoding.EncodeToString(valueRaw)
|
||||||
change.SiteID = base64.StdEncoding.EncodeToString(siteIDRaw)
|
change.SiteID = base64.StdEncoding.EncodeToString(siteIDRaw)
|
||||||
|
|
||||||
|
|
||||||
changes = append(changes, change)
|
changes = append(changes, change)
|
||||||
fmt.Println(change)
|
fmt.Println(change)
|
||||||
}
|
}
|
||||||
|
@ -142,7 +140,6 @@ func (m *model) sendToWebSocket(changes []CRSQLChange) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (m *model) loadTodos() {
|
func (m *model) loadTodos() {
|
||||||
rows, err := m.db.Query("SELECT content FROM todos")
|
rows, err := m.db.Query("SELECT content FROM todos")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -78,7 +78,7 @@ type CRSQLChange struct {
|
||||||
TableName string
|
TableName string
|
||||||
PK []byte
|
PK []byte
|
||||||
ColumnName string
|
ColumnName string
|
||||||
Value []byte
|
Value any
|
||||||
ColVersion int64
|
ColVersion int64
|
||||||
DBVersion int64
|
DBVersion int64
|
||||||
SiteID []byte
|
SiteID []byte
|
||||||
|
@ -97,15 +97,12 @@ func mapToCRSQLChange(m map[string]interface{}) (CRSQLChange, error) {
|
||||||
change.CL = int64(m["CL"].(float64))
|
change.CL = int64(m["CL"].(float64))
|
||||||
change.Seq = int64(m["Seq"].(float64))
|
change.Seq = int64(m["Seq"].(float64))
|
||||||
|
|
||||||
|
// Still unsure about this, it can encode anything
|
||||||
|
if m["Value"] != nil {
|
||||||
|
change.Value = m["Value"]
|
||||||
|
}
|
||||||
// Handle []byte fields
|
// Handle []byte fields
|
||||||
change.PK, err = decodeBase64ToByteArray(m["PK"].(string))
|
change.PK, err = decodeBase64ToByteArray(m["PK"].(string))
|
||||||
if err != nil {
|
|
||||||
return change, err
|
|
||||||
}
|
|
||||||
//fmt.Println(m["PK"])
|
|
||||||
//fmt.Println(m["PK"].(string))
|
|
||||||
//fmt.Println(base64.URLEncoding.DecodeString(m["PK"].(string)))
|
|
||||||
change.Value, err = decodeBase64ToByteArray(m["Value"].(string))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return change, err
|
return change, err
|
||||||
}
|
}
|
||||||
|
@ -118,7 +115,6 @@ func mapToCRSQLChange(m map[string]interface{}) (CRSQLChange, error) {
|
||||||
return change, nil
|
return change, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func decodeBase64ToByteArray(encodedString string) ([]uint8, error) {
|
func decodeBase64ToByteArray(encodedString string) ([]uint8, error) {
|
||||||
// Decode the base64 string
|
// Decode the base64 string
|
||||||
decodedBytes, err := base64.StdEncoding.DecodeString(encodedString)
|
decodedBytes, err := base64.StdEncoding.DecodeString(encodedString)
|
||||||
|
@ -145,7 +141,6 @@ func decodeBase64ToByteArray(encodedString string) ([]uint8, error) {
|
||||||
return intSlice, nil
|
return intSlice, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func applyChanges(changes []interface{}) {
|
func applyChanges(changes []interface{}) {
|
||||||
tx, _ := db.Begin()
|
tx, _ := db.Begin()
|
||||||
for i, change := range changes {
|
for i, change := range changes {
|
||||||
|
@ -174,7 +169,6 @@ func applyChanges(changes []interface{}) {
|
||||||
tx.Commit()
|
tx.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func broadcastChanges(sender *websocket.Conn, changes interface{}) {
|
func broadcastChanges(sender *websocket.Conn, changes interface{}) {
|
||||||
for client := range clients {
|
for client := range clients {
|
||||||
if client != sender {
|
if client != sender {
|
||||||
|
@ -184,13 +178,9 @@ func broadcastChanges(sender *websocket.Conn, changes interface{}) {
|
||||||
}
|
}
|
||||||
func main() {
|
func main() {
|
||||||
var err error
|
var err error
|
||||||
val, err := base64.StdEncoding.DecodeString("MTY3LDU0LDQ5LDY3LDExMyw2MCw3NiwxMjAsMTU2LDExMCwyMjcsMTc2LDE4NCwyMjksMjQxLDE0Mw==")
|
|
||||||
decoded, _ := base64.StdEncoding.DecodeString("MTY3LDU0LDQ5LDY3LDExMyw2MCw3NiwxMjAsMTU2LDExMCwyMjcsMTc2LDE4NCwyMjksMjQxLDE0Mw==")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("error:", err)
|
log.Fatal("error:", err)
|
||||||
}
|
}
|
||||||
fmt.Printf("%q\n", val)
|
|
||||||
fmt.Printf("%q\n", decoded)
|
|
||||||
sql.Register("sqlite3_with_extensions",
|
sql.Register("sqlite3_with_extensions",
|
||||||
&sqlite3.SQLiteDriver{
|
&sqlite3.SQLiteDriver{
|
||||||
Extensions: []string{
|
Extensions: []string{
|
||||||
|
@ -208,4 +198,3 @@ func main() {
|
||||||
http.HandleFunc("/sync", handleWebSocket)
|
http.HandleFunc("/sync", handleWebSocket)
|
||||||
log.Fatal(http.ListenAndServe(":8080", nil))
|
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue