Browse Source

fix(sqlite): graph list and unlinking for in-browser graph

Andelf 2 years ago
parent
commit
fd0ccf2add

+ 56 - 5
deps/persist-db/src/index.js

@@ -8,14 +8,13 @@ import * as Comlink from 'comlink';
 
 let sqlite3;
 let dbMap = {};
-
+const idbName = "logseq-VFS";
 
 class AsyncLock {
     constructor() {
         this.disable = (_) => { }
         this.promise = Promise.resolve()
     }
-
     enable() {
         this.promise = new Promise(resolve => this.disable = resolve)
     }
@@ -42,7 +41,56 @@ const SQLiteDB = {
     inc() {
         return 233;
     },
-
+    async listDB() {
+        return new Promise((resolve, reject) => {
+            const request = indexedDB.open(idbName);
+            request.onerror = reject;
+            request.onsuccess = (ev) => {
+                const db = ev.target.result;
+                const transaction = db.transaction(["blocks"], "readonly");
+                const objectStore = transaction.objectStore("blocks");
+                const request = objectStore.getAllKeys();
+                request.onsuccess = () => {
+                    db.close();
+                    const dbNames = [];
+                    for (const key of request.result) {
+                        if (dbNames.findIndex(dbName => dbName === key[0]) === -1) {
+                            dbNames.push(key[0]);
+                        }
+                    }
+                    resolve(dbNames.map(dbName => dbName.replace(/^\//, "")));
+                };
+                request.onerror = reject;
+            };
+        });
+    },
+    async unsafeUnlinkDB(dbName) {
+        const dbKey = "/" + dbName;
+        console.log("[worker] deleting", dbName);
+        return new Promise((resolve, reject) => {
+            const request = indexedDB.open(idbName);
+            request.onerror = reject;
+            request.onsuccess = (ev) => {
+                const db = ev.target.result;
+                const transaction = db.transaction(["blocks"], "readwrite");
+                const objectStore = transaction.objectStore("blocks");
+                const request = objectStore.openCursor();
+                request.onsuccess = (event) => {
+                    const cursor = event.target.result;
+                    if (cursor) {
+                        if (cursor.key[0] === (dbKey)) {
+                            cursor.delete();
+                        }
+                        cursor.continue();
+                    } else {
+                        db.close();
+                        resolve();
+                    }
+                };
+                request.onerror = reject;
+            };
+        });
+    },
     async init() {
         console.log("[worker] calling init");
         const module = await SQLiteModuleFactory();
@@ -59,9 +107,12 @@ const SQLiteDB = {
     // :db-new
     async newDB(dbName) {
         console.log("[worker] SQLite newDB", dbName);
-        const db = await sqlite3.open_v2(dbName ?? 'demo');
+        let db = dbMap[dbName];
+        if (db) {
+            return db;
+        }
+        db = await sqlite3.open_v2(dbName ?? 'demo');
         // create-blocks-table!
-
         const str = sqlite3.str_new(db, `CREATE TABLE IF NOT EXISTS blocks (
             uuid TEXT PRIMARY KEY NOT NULL,
             type INTEGER NOT NULL,

File diff suppressed because it is too large
+ 0 - 0
resources/js/ls-wa-sqlite/persist-db-worker.js


File diff suppressed because it is too large
+ 0 - 0
resources/js/ls-wa-sqlite/persist-db-worker.js.map


+ 43 - 24
src/main/frontend/components/repo.cljs

@@ -50,7 +50,8 @@
   "Graph list in `All graphs` page"
   [repos]
   (for [{:keys [url remote? GraphUUID GraphName] :as repo} repos
-        :let [only-cloud? (and remote? (nil? url))]]
+        :let [only-cloud? (and remote? (nil? url))
+              db-based? (config/db-based-graph? url)]]
     [:div.flex.justify-between.mb-4.items-center {:key (or url GraphUUID)}
      (normalized-graph-label repo #(if only-cloud?
                                      (state/pub-event! [:graph/pull-down-remote-graph repo])
@@ -59,35 +60,53 @@
      [:div.controls
       [:div.flex.flex-row.items-center
        (ui/tippy {:html [:div.text-sm.max-w-xs
-                         (if only-cloud?
+                         (cond
+                           only-cloud?
                            "Deletes this remote graph. Note this can't be recovered."
+
+                           db-based?
+                           "Unsafe delete this DB-based graph. Note this can't be recovered."
+
+                           :else
                            "Removes Logseq's access to the local file path of your graph. It won't remove your local files.")]
                   :class "tippy-hover"
                   :interactive true}
                  [:a.text-gray-400.ml-4.font-medium.text-sm.whitespace-nowrap
                   {:on-click (fn []
-                               (if only-cloud?
-                                 (let [confirm-fn
-                                       (fn []
-                                         (ui/make-confirm-modal
-                                          {:title      [:div
-                                                        {:style {:max-width 700}}
-                                                        (str "Are you sure to permanently delete the graph \"" GraphName "\" from our server?")]
-                                           :sub-title   [:div.small.mt-1
-                                                         "Notice that we can't recover this graph after being deleted. Make sure you have backups before deleting it."]
-                                           :on-confirm (fn [_ {:keys [close-fn]}]
-                                                         (close-fn)
-
-                                                         (state/set-state! [:file-sync/remote-graphs :loading] true)
-                                                         (go (<! (file-sync/<delete-graph GraphUUID))
-                                                             (state/delete-repo! repo)
-                                                             (state/delete-remote-graph! repo)
-                                                             (state/set-state! [:file-sync/remote-graphs :loading] false)))}))]
-                                   (state/set-modal! (confirm-fn)))
-                                 (let [current-repo (state/get-current-repo)]
-                                   (repo-handler/remove-repo! repo)
-                                   (state/pub-event! [:graph/unlinked repo current-repo]))))}
-                  (if only-cloud? "Remove" "Unlink")])]]]))
+                               (let [has-prompt? (or only-cloud? db-based?)
+                                     prompt-str (cond only-cloud?
+                                                      (str "Are you sure to permanently delete the graph \"" GraphName "\" from our server?")
+                                                      db-based?
+                                                      (str "Are you sure to permanently delete the graph \"" url "\" from Logseq?")
+                                                      :else
+                                                      "")
+                                     unlink-or-remote-fn (fn []
+                                                           (let [current-repo (state/get-current-repo)]
+                                                             (repo-handler/remove-repo! repo)
+                                                             (state/pub-event! [:graph/unlinked repo current-repo])))
+                                     action-confirm-fn (if only-cloud?
+                                                         (fn []
+                                                           (state/set-state! [:file-sync/remote-graphs :loading] true)
+                                                           (go (<! (file-sync/<delete-graph GraphUUID))
+                                                               (state/delete-repo! repo)
+                                                               (state/delete-remote-graph! repo)
+                                                               (state/set-state! [:file-sync/remote-graphs :loading] false)))
+                                                         unlink-or-remote-fn)
+                                     confirm-fn
+                                     (fn []
+                                       (ui/make-confirm-modal
+                                        {:title      [:div
+                                                      {:style {:max-width 700}}
+                                                      prompt-str]
+                                         :sub-title   [:div.small.mt-1
+                                                       "Notice that we can't recover this graph after being deleted. Make sure you have backups before deleting it."]
+                                         :on-confirm (fn [_ {:keys [close-fn]}]
+                                                       (close-fn)
+                                                       (action-confirm-fn))}))]
+                                 (if has-prompt?
+                                   (state/set-modal! (confirm-fn))
+                                   (unlink-or-remote-fn))))}
+                  (if (or db-based? only-cloud?) "Remove" "Unlink")])]]]))
 
 (rum/defc repos < rum/reactive
   []

+ 6 - 2
src/main/frontend/db/persist.cljs

@@ -5,7 +5,8 @@
             [frontend.config :as config]
             [electron.ipc :as ipc]
             [frontend.db.conn :as db-conn]
-            [promesa.core :as p]))
+            [promesa.core :as p]
+            [frontend.persist-db :as persist-db]))
 
 (defn get-all-graphs
   []
@@ -15,7 +16,9 @@
             ;; backward compatibility (release <= 0.5.4)
             result (if (seq result) result (idb/get-nfs-dbs))]
       (distinct result))
-    (idb/get-nfs-dbs)))
+    (p/let [repos (idb/get-nfs-dbs)
+            db-repos (persist-db/<list-db)]
+      (concat repos db-repos))))
 
 (defn get-serialized-graph
   [graph-name]
@@ -40,6 +43,7 @@
   [graph]
   (let [key (db-conn/datascript-db graph)
         db-based? (config/db-based-graph? graph)]
+    (persist-db/<unsafe-delete graph)
     (if (util/electron?)
       (ipc/ipc "deleteGraph" graph key db-based?)
      (idb/remove-item! key))))

+ 20 - 16
src/main/frontend/persist_db.cljs

@@ -1,27 +1,31 @@
 (ns frontend.persist-db
-  "Backend of DB based graph"
-  (:require [frontend.persist-db.browser :as browser]
-            [frontend.persist-db.node :as node]
-            [frontend.persist-db.protocol :as protocol]
-            [frontend.util :as util]
-            [promesa.core :as p]))
+   "Backend of DB based graph"
+   (:require [frontend.persist-db.browser :as browser]
+             [frontend.persist-db.node :as node]
+             [frontend.persist-db.protocol :as protocol]
+             [frontend.util :as util]
+             [promesa.core :as p]))
 
 
-(defonce electron-ipc-sqlite-db (node/->ElectronIPC))
+ (defonce electron-ipc-sqlite-db (node/->ElectronIPC))
 
-(defonce opfs-db (browser/->InBrowser))
+ (defonce opfs-db (browser/->InBrowser))
 
-(defn- get-impl
-  "Get the actual implementation of PersistentDB"
-  []
-  (cond
-    (util/electron?)
-    electron-ipc-sqlite-db
+ (defn- get-impl
+   "Get the actual implementation of PersistentDB"
+   []
+   (cond
+     (util/electron?)
+     electron-ipc-sqlite-db
 
-    :else
-    opfs-db))
+     :else
+     opfs-db))
 
+ (defn <list-db []
+   (protocol/<list-db (get-impl)))
 
+ (defn <unsafe-delete [repo]
+   (protocol/<unsafe-delete (get-impl) repo))
 
 (defn <new [repo]
   (protocol/<new (get-impl) repo))

+ 33 - 16
src/main/frontend/persist_db/browser.cljs

@@ -15,22 +15,31 @@
 (defn- ensure-sqlite-init []
   (if (nil? @*worker)
     (js/Promise. (fn [resolve _reject]
-                   (let [worker (js/SharedWorker. "/static/js/ls-wa-sqlite/persist-db-worker.js")
+                   (let [worker (try
+                                  (js/SharedWorker. "/static/js/ls-wa-sqlite/persist-db-worker.js")
+                                  (catch js/Error e
+                                    (js/console.error "worker error", e)
+                                    nil))
                          _ (reset! *worker worker)
                          ^js sqlite (Comlink/wrap (.-port worker))
                          _ (reset! *sqlite sqlite)]
-                     (p/do!
-                      (.init ^js sqlite)
-                      (resolve @*sqlite)
-                      ;; start the upsert loop
-                      (go-loop []
-                        (let [[repo ret-ch deleted-uuids upsert-blocks] (<! db-upsert-chan)
-                              delete-rc (when (seq deleted-uuids)
-                                          (<! (p->c (.deleteBlocks sqlite repo (clj->js deleted-uuids)))))
-                              upsert-rc (<! (p->c (.upsertBlocks sqlite repo (clj->js upsert-blocks))))]
-                          (async/put! ret-ch [delete-rc upsert-rc])
-                          (prn :db-upsert-chan :delete delete-rc :upsert upsert-rc))
-                        (recur))))))
+                     (-> (.init sqlite)
+                         (p/then (fn []
+                                   (js/console.log "sqlite init done")
+                                   (resolve @*sqlite)))
+                         (p/then (fn []
+                                   (go-loop []
+                                     (let [[repo ret-ch deleted-uuids upsert-blocks] (<! db-upsert-chan)
+                                           delete-rc (when (seq deleted-uuids)
+                                                       (<! (p->c (.deleteBlocks sqlite repo (clj->js deleted-uuids)))))
+                                           upsert-rc (<! (p->c (.upsertBlocks sqlite repo (clj->js upsert-blocks))))]
+                                       (async/put! ret-ch [delete-rc upsert-rc])
+                                       (prn :db-upsert-chan :delete delete-rc :upsert upsert-rc))
+                                     (recur))
+                                   (prn ::done))
+                                 )
+                         (p/catch (fn [e]
+                                    (js/console.error "init error", e)))))))
     (p/resolved @*sqlite)))
 
 (defn- type-of-block
@@ -77,10 +86,18 @@
 (defrecord InBrowser []
   protocol/PersistentDB
   (<new [_this repo]
-    (prn ::repo repo)
+    (prn ::new-repo repo)
+    (p/let [^js sqlite (ensure-sqlite-init)]
+      (.newDB sqlite repo)))
+
+  (<list-db [_this]
     (p/let [^js sqlite (ensure-sqlite-init)
-            rc (.newDB ^js sqlite repo)]
-      (js/console.log "new db created rc=" rc)))
+            dbs (.listDB sqlite)]
+      (js/console.log "list DBs:" dbs)
+      dbs))
+  (<unsafe-delete [_this repo]
+    (p/let [^js sqlite (ensure-sqlite-init)]
+      (.unsafeUnlinkDB sqlite repo)))
 
   (<transact-data [_this repo upsert-blocks deleted-uuids]
     (go

+ 8 - 1
src/main/frontend/persist_db/node.cljs

@@ -1,13 +1,20 @@
 (ns frontend.persist-db.node
   "Electron ipc based persistent db"
   (:require [electron.ipc :as ipc]
-            [frontend.persist-db.protocol :as protocol]))
+            [frontend.persist-db.protocol :as protocol]
+            [promesa.core :as p]))
 
 (defrecord ElectronIPC []
   protocol/PersistentDB
   (<new [_this repo]
     (prn ::new repo)
     (ipc/ipc :db-new repo))
+  (<list-db [_this]
+    (js/console.warn "TODO: list-db for electron not implemented")
+    [])
+  (<unsafe-delete [_this _repo]
+    (js/console.warn "TODO: delete")
+    (p/resolved nil))
   (<transact-data [_this repo added-blocks deleted-block-uuids]
     ;; (prn ::transact-data repo added-blocks deleted-block-uuids)
     (ipc/ipc :db-transact-data repo

+ 2 - 0
src/main/frontend/persist_db/protocol.cljs

@@ -4,7 +4,9 @@
 
 ;; TODO: exporting, importing support
 (defprotocol PersistentDB
+  (<list-db [this])
   (<new [this repo])
+  (<unsafe-delete [this repo])
   (<transact-data [this repo added-blocks deleted-block-uuids]
     "Transact data to db
 

Some files were not shown because too many files changed in this diff