Pārlūkot izejas kodu

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.
Tienson Qin 4 gadi atpakaļ
vecāks
revīzija
8e6fb5613d

+ 1 - 0
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": {

+ 30 - 13
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"

+ 2 - 2
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)

+ 5 - 0
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==
 
[email protected]:
+  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"