mirror of https://github.com/logseq/logseq
fix(sqlite): graph list and unlinking for in-browser graph
parent
bcd3e79730
commit
fd0ccf2add
|
@ -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 one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -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
|
||||
[]
|
||||
|
|
|
@ -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))))
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue