From ad1d435b5ed21532ac98a68181572874df2d321e Mon Sep 17 00:00:00 2001 From: Andrea Donetti Date: Wed, 28 May 2025 23:44:20 -0600 Subject: [PATCH] first draft --- sqlitesync-todoapp/README.md | 167 ++++++++++++++++++ sqlitesync-todoapp/db/todoapp.sqlite | Bin 0 -> 28672 bytes sqlitesync-todoapp/sql/cloud_insert_users.sql | 5 + .../sql/cloud_rls_permissions.sql | 45 +++++ .../sql/cloud_rls_todo_lists.sql | 30 ++++ sqlitesync-todoapp/sql/cloud_rls_todos.sql | 59 +++++++ sqlitesync-todoapp/sql/cloud_rls_users.sql | 12 ++ sqlitesync-todoapp/sql/edge_device_1_init.sql | 16 ++ sqlitesync-todoapp/sql/edge_device_2_init.sql | 2 + sqlitesync-todoapp/sql/edge_device_3_init.sql | 7 + sqlitesync-todoapp/sql/schema.sql | 36 ++++ 11 files changed, 379 insertions(+) create mode 100644 sqlitesync-todoapp/README.md create mode 100644 sqlitesync-todoapp/db/todoapp.sqlite create mode 100644 sqlitesync-todoapp/sql/cloud_insert_users.sql create mode 100644 sqlitesync-todoapp/sql/cloud_rls_permissions.sql create mode 100644 sqlitesync-todoapp/sql/cloud_rls_todo_lists.sql create mode 100644 sqlitesync-todoapp/sql/cloud_rls_todos.sql create mode 100644 sqlitesync-todoapp/sql/cloud_rls_users.sql create mode 100644 sqlitesync-todoapp/sql/edge_device_1_init.sql create mode 100644 sqlitesync-todoapp/sql/edge_device_2_init.sql create mode 100644 sqlitesync-todoapp/sql/edge_device_3_init.sql create mode 100644 sqlitesync-todoapp/sql/schema.sql diff --git a/sqlitesync-todoapp/README.md b/sqlitesync-todoapp/README.md new file mode 100644 index 0000000..4214d01 --- /dev/null +++ b/sqlitesync-todoapp/README.md @@ -0,0 +1,167 @@ +# SQLiteSync Demo: Multiuser To-Do App + +This repository demonstrates how to use **SQLiteSync**, our CRDT-based synchronization solution, to keep data synchronized between multiple **edge devices** and the **SQLiteCloud** service. It features: + +- Cloud-side configuration of synchronization and Row-Level Security (RLS) +- Edge device simulation using the `sqlite3` CLI +- A multitenant **To-Do application** data model + +## Overview + +**SQLiteSync** extends SQLite to enable automatic data synchronization between local databases on edge devices and a centralized **SQLiteCloud** instance. This example shows: + +- How to configure **SQLiteCloud** for **SQLiteSync** (CRDT-based synchronization) and RLS +- How to simulate edge clients using the SQLite CLI +- How RLS restricts data access based on user permissions +- How to create and interact with the schema for a collaborative to-do app + +## Requirements + +- [SQLiteCloud account](https://sqlitecloud.io) +- SQLite 3.45+ CLI with extension loading support +- [SQLiteSync](https://github.com/sqliteai/sqlite-sync) extension +- Access to this repository's `.sql` files + +## Schema + +The schema for this demo models a multitenant to-do list application with user access control. + +```sql +-- USERS +CREATE TABLE users ( + id TEXT PRIMARY KEY NOT NULL, + username TEXT NOT NULL UNIQUE DEFAULT "", + email TEXT NOT NULL UNIQUE DEFAULT "" +) WITHOUT ROWID; + +-- TODO LISTS +CREATE TABLE todo_lists ( + id TEXT PRIMARY KEY NOT NULL, + title TEXT NOT NULL DEFAULT "", + description TEXT DEFAULT NULL, + created_by TEXT, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP +) WITHOUT ROWID; + +-- PERMISSIONS +CREATE TABLE permissions ( + user_id TEXT NOT NULL, + list_id TEXT NOT NULL, + role TEXT NOT NULL DEFAULT 'viewer' CHECK (role IN ('owner', 'editor', 'viewer')), + granted_at DATETIME DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (user_id, list_id), + FOREIGN KEY (list_id) REFERENCES todo_lists(id) +) WITHOUT ROWID; + +-- TODOS +CREATE TABLE todos ( + id TEXT PRIMARY KEY NOT NULL, + list_id TEXT, + title TEXT, + is_done INTEGER NOT NULL DEFAULT 0 CHECK (is_done IN (0, 1)), + due_date DATETIME DEFAULT NULL, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (list_id) REFERENCES todo_lists(id) +) WITHOUT ROWID; +``` + +## Setup Instructions + +### 1. Configure SQLiteCloud + +#### On the Dashboard + +1. Log in to your [SQLiteCloud Dashboard](https://dashboard.sqlitecloud.io). +2. Upload the database from `./db/todoapp.sqlite` +3. Enable **SQLiteSync** (OffSync button) for each table in the database from the project's Databases page. +4. Enable **Row-Level Security (RLS)** and configure policies for the `permissions`, `todo_lists`, `todos`, and `users` tables. You can copy each statement from the `./sql/cloud_rls_.sql` files. These rules ensure that users can only view and interact with: + - Lists they created + - Lists shared with them via the `permissions` table (as owner, editor, or viewer) + - Todos belonging to those lists +5. Insert the first users of the app. From the Studio page, execute the following lines from `./sql/cloud_insert_users.sql`: + +```sql +INSERT INTO users (id, username, email) VALUES + ('018ecfc2-b2b0-7cc2-a9f0-987cef6b49f1', 'alice', 'alice@example.com'), + ('018ecfc2-b2b1-7cc3-a9f0-987cef6b49f2', 'bob', 'bob@example.com'), + ('018ecfc2-b2b2-7cc4-a9f0-987cef6b49f3', 'carol', 'carol@example.com'), + ('018ecfc2-b2b3-7cc5-a9f0-987cef6b49f4', 'dan', 'dan@example.com'); +``` + +6. Create a token for each user (`alice`, `bob`, `carol`, and `dan`) using a REST API call. For example, you can try the REST API with `curl` from a terminal and extract the `token` from the response. + + ``` + curl -X "POST" "https:///v2/tokens" \ + -H 'Authorization: Bearer ' \ + -H 'Content-Type: application/json; charset=utf-8' \ + -d $'{ + "name": "alice", + "userId": "018ecfc2-b2b0-7cc2-a9f0-987cef6b49f1" + }' + ``` + +### 2. Edge Devices Simulation + +Each edge device simulates a different user using the `sqlite3` CLI. Alternatively, you can execute the same SQL queries programmatically using your preferred programming language and SQLite driver. + +#### Example Edge Device Setup + +```bash +sqlite3 todoapp_edge_device_1.sqlite +``` + +Then, inside the CLI: + +```sql +-- Load the extension +.load /path/to/extension/cloudsync.dylib + +-- Load the schema +.read sql/schema.sql + +-- Init cloudsync for all the tables +SELECT cloudsync_init('*'); + +-- Load initial sync state if needed +.read sql/edge_device_1_init.sql + +-- Connect to the cloud +SELECT cloudsync_network_init('sqlitecloud:///todoapp.sqlite'); +SELECT cloudsync_set_token(''); + +-- Start the synchronization process: +-- 1. Retrieve changes by periodically invoking cloudsync_network_check_changes: +SELECT cloudsync_network_check_changes(); +-- ... +SELECT cloudsync_network_check_changes(); + +-- 2. Send local changes: +SELECT cloudsync_network_send_changes(); + +-- Run your application queries here +-- for example: +-- SELECT * FROM users; +-- SELECT * FROM todos JOIN todo_lists ON todos.list_id = todo_lists.id + +-- Before closing the db connection, close cloudsync +SELECT cloudsync_terminate() +``` + +Repeat similar steps for `edge_device_2.db` and `edge_device_3.db`, using their respective `.sql` files and tokens for users `bob` and `carol`. + +## Using the Demo + +After setup: + +- Any `INSERT`, `UPDATE`, or `DELETE` operation on a local database will be propagated to the cloud and synchronized across all clients using `SELECT cloudsync_network_send_changes();`, subject to RLS rules. +- Retrieve remote changes by periodically calling `SELECT cloudsync_network_check_changes();`. +- The app can query the local database for immediate responses, avoiding network latency. +- Changes made on one edge device will eventually propagate to others, in accordance with the CRDT model. + +## License + +This demo is provided under the MIT License. See [LICENSE](LICENSE) for more details. + +## Contact + +For support or more information, visit [sqlitecloud.io](https://sqlitecloud.io) or contact us at support@sqlitecloud.io. diff --git a/sqlitesync-todoapp/db/todoapp.sqlite b/sqlitesync-todoapp/db/todoapp.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..016dde2a9894ec60996e856c32d3ca7a0712dd65 GIT binary patch literal 28672 zcmeI#QBTuQ6bJAY#%53ipZa=$hqcLKP=aqp=eR(_=%DL@JT--0O_OcKwut%$q94TX z;|DSE1Ni9eZMzPZC?vkP{BP3k_V#qS=ht(W-R?mTSR)ZzFGbN5k>L(DP&(RABxS=ZjC3nzp>Gett13+w7&Jg_`x|t|^z} zx31!{W+z2gR`M$3@ktb%CgKfei=wFC$s#=dI9C66s?3{3cJWM=YxZ(iCZ7t&@-%ZT zWjS=~Y}4qWH^>%gnQ+fC4|?U=D)Qz7FX(3(7fYWeLh0tjjoGU9TvL@`=<^TpyS~`2 zyWUw6%4=87*Ikz%6=UY_p-*M#toW(Dm;T(&r>CX#ga!c!KmY;|fB*y_009U<00Izz zz_bcr|39s77I{Gc0uX=z1Rwwb2tWV=5P$##N&)QuOAGWM009U<00Izz00bZa0SG_< z0@Esh{r|MSS>y!)2tWV=5P$##AOHafKmY;|CJAOHafKmY;|fB*y_009U