From 8e6fb5613d176fe71b50bd358a37348b62a804f0 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Wed, 2 Mar 2022 11:58:20 +0800 Subject: [PATCH] enhance: backup files when there're differences between db and disk Previously, files are backuped to logseq/bak when logseq detects there're differences between the db and disk. But it has two problems: 1. Only a few of users know `logseq/bak`, other users think that their data has been lost. 2. Files in the logseq/bak folder are never truncated. This PR backups old file in DB with timestamp suffixes instead of logesq/bak, and only keep the latest 10 versions of any changed file. --- resources/package.json | 1 + src/electron/electron/handler.cljs | 43 ++++++++++++++++------- src/main/frontend/fs/watcher_handler.cljs | 4 +-- static/yarn.lock | 5 +++ 4 files changed, 38 insertions(+), 15 deletions(-) diff --git a/resources/package.json b/resources/package.json index 9abfcfcb5..60ce9929e 100644 --- a/resources/package.json +++ b/resources/package.json @@ -32,6 +32,7 @@ "semver": "7.3.5", "update-electron-app": "2.0.1", "extract-zip": "2.0.1", + "diff-match-patch": "1.0.5", "https-proxy-agent": "5.0.0" }, "devDependencies": { diff --git a/src/electron/electron/handler.cljs b/src/electron/electron/handler.cljs index 5f4e051d3..85dbc4a05 100644 --- a/src/electron/electron/handler.cljs +++ b/src/electron/electron/handler.cljs @@ -6,6 +6,7 @@ ["fs-extra" :as fs-extra] ["path" :as path] ["os" :as os] + ["diff-match-patch" :as google-diff] [electron.fs-watcher :as watcher] [electron.configs :as cfgs] [promesa.core :as p] @@ -16,7 +17,8 @@ [electron.search :as search] [electron.git :as git] [electron.plugin :as plugin] - [electron.window :as win])) + [electron.window :as win] + [goog.object :as gobj])) (defmulti handle (fn [_window args] (keyword (first args)))) @@ -56,21 +58,36 @@ new-path (str recycle-dir "/" file-name)] (fs/renameSync path new-path)))) +(defonce Diff (google-diff.)) +(defn string-some-deleted? + [old new] + (let [result (.diff_main Diff old new)] + (some (fn [a] (= -1 (first a))) result))) + +(defn- truncate-old-versioned-files! + [file-path] + (let [dir (path/dirname file-path) + files (fs/readdirSync dir (clj->js {:withFileTypes true})) + files (map #(.-name %) files) + prefix (str (path/basename file-path) ".") + versioned-files (filter #(string/starts-with? % prefix) files) + old-versioned-files (drop 10 (reverse (sort versioned-files)))] + (doseq [file old-versioned-files] + (fs-extra/removeSync (path/join dir file))))) + (defn backup-file [repo path content] - (let [file-name (-> (string/replace path (str repo "/") "") - (string/replace "/" "_") - (string/replace "\\" "_")) - bak-dir (str repo "/logseq/bak") - _ (fs-extra/ensureDirSync bak-dir) - new-path (str bak-dir "/" file-name "." - (string/replace (.toISOString (js/Date.)) ":" "_"))] + (let [new-path (str path "." (string/replace (.toISOString (js/Date.)) ":" "_"))] (fs/writeFileSync new-path content) (fs/statSync new-path) + (truncate-old-versioned-files! path) new-path)) -(defmethod handle :backupDbFile [_window [_ repo path db-content]] - (backup-file repo path db-content)) +(defmethod handle :backupDbFile [_window [_ repo path db-content new-content]] + (when (and (string? db-content) + (string? new-content) + (string-some-deleted? db-content new-content)) + (backup-file repo path db-content))) (defmethod handle :readFile [_window [_ path]] (utils/read-file path)) @@ -80,7 +97,7 @@ (assert (string? path)) (try (fs/accessSync path (aget fs "W_OK")) - (catch js/Error _e + (catch :default _e false))) (defmethod handle :writeFile [_window [_ repo path content]] @@ -93,10 +110,10 @@ (fs/chmodSync path "644")) (fs/writeFileSync path content) (fs/statSync path) - (catch js/Error e + (catch :default e (let [backup-path (try (backup-file repo path content) - (catch js/Error e + (catch :default e (println "Backup file failed") (js/console.dir e)))] (utils/send-to-renderer "notification" {:type "error" diff --git a/src/main/frontend/fs/watcher_handler.cljs b/src/main/frontend/fs/watcher_handler.cljs index c6822d10f..ebf29018b 100644 --- a/src/main/frontend/fs/watcher_handler.cljs +++ b/src/main/frontend/fs/watcher_handler.cljs @@ -33,8 +33,8 @@ (defn- handle-add-and-change! [repo path content db-content mtime backup?] (p/let [ - ;; save the previous content in a bak file to avoid data overwritten. - _ (when backup? (ipc/ipc "backupDbFile" (config/get-local-dir repo) path db-content)) + ;; save the previous content in a versioned bak file to avoid data overwritten. + _ (when backup? (ipc/ipc "backupDbFile" (config/get-local-dir repo) path db-content content)) _ (file-handler/alter-file repo path content {:re-render-root? true :from-disk? true})] (set-missing-block-ids! content) diff --git a/static/yarn.lock b/static/yarn.lock index bd5d78ecc..429292cff 100644 --- a/static/yarn.lock +++ b/static/yarn.lock @@ -1529,6 +1529,11 @@ detect-node@^2.0.4: resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== +diff-match-patch@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/diff-match-patch/-/diff-match-patch-1.0.5.tgz#abb584d5f10cd1196dfc55aa03701592ae3f7b37" + integrity sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw== + dir-compare@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/dir-compare/-/dir-compare-2.4.0.tgz#785c41dc5f645b34343a4eafc50b79bac7f11631"