file_sync.cljs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. (ns frontend.handler.file-sync
  2. (:require ["path" :as path]
  3. [cljs-time.coerce :as tc]
  4. [cljs-time.format :as tf]
  5. [cljs.core.async :as async :refer [go <!]]
  6. [cljs.core.async.interop :refer [p->c]]
  7. [clojure.string :as string]
  8. [clojure.set :as set]
  9. [frontend.config :as config]
  10. [frontend.db :as db]
  11. [frontend.fs.sync :as sync]
  12. [frontend.handler.notification :as notification]
  13. [frontend.state :as state]
  14. [frontend.util :as util]
  15. [frontend.handler.user :as user]
  16. [frontend.fs :as fs]))
  17. (def hiding-login&file-sync (not config/dev?))
  18. (def refresh-file-sync-component (atom false))
  19. (defn graph-txid-exists?
  20. []
  21. (let [[_user-uuid graph-uuid _txid] @sync/graphs-txid]
  22. (some? graph-uuid)))
  23. (defn create-graph
  24. [name]
  25. (go
  26. (let [r* (<! (sync/create-graph sync/remoteapi name))
  27. r (if (instance? ExceptionInfo r*) r* (:GraphUUID r*))]
  28. (if (and (not (instance? ExceptionInfo r))
  29. (string? r))
  30. (let [tx-info [0 r (user/user-uuid) (state/get-current-repo)]]
  31. (apply sync/update-graphs-txid! tx-info)
  32. (swap! refresh-file-sync-component not) tx-info)
  33. (if (= 404 (get-in (ex-data r) [:err :status]))
  34. (notification/show! (str "Create graph failed: already existed graph: " name) :warning)
  35. (notification/show! (str "Create graph failed: " r) :warning))))))
  36. (defn delete-graph
  37. [graph-uuid]
  38. (sync/sync-stop)
  39. (go
  40. (let [r (<! (sync/delete-graph sync/remoteapi graph-uuid))]
  41. (if (instance? ExceptionInfo r)
  42. (notification/show! (str "Delete graph failed: " graph-uuid) :warning)
  43. (let [[_ local-graph-uuid _] @sync/graphs-txid]
  44. (when (= graph-uuid local-graph-uuid)
  45. (sync/clear-graphs-txid! (state/get-current-repo))
  46. (swap! refresh-file-sync-component not))
  47. (notification/show! (str "Graph deleted") :success))))))
  48. (defn list-graphs
  49. []
  50. (go (:Graphs (<! (sync/list-remote-graphs sync/remoteapi)))))
  51. (defn download-all-files
  52. [repo graph-uuid user-uuid base-path]
  53. (go
  54. (<! (sync/ensure-pwd-exists! repo graph-uuid))
  55. (state/reset-file-sync-download-init-state!)
  56. (state/set-file-sync-download-init-state! {:total :unknown :finished 0 :downloading? true})
  57. (let [remote-all-files-meta (<! (sync/get-remote-all-files-meta sync/remoteapi graph-uuid))
  58. local-all-files-meta (<! (sync/get-local-all-files-meta sync/rsapi graph-uuid base-path))
  59. diff-remote-files (set/difference remote-all-files-meta local-all-files-meta)
  60. latest-txid (:TXId (<! (sync/get-remote-graph sync/remoteapi nil graph-uuid)))
  61. partitioned-filetxns
  62. (sequence (sync/filepaths->partitioned-filetxns 10 graph-uuid user-uuid)
  63. (map sync/relative-path diff-remote-files))]
  64. (state/set-file-sync-download-init-state! {:total (count diff-remote-files) :finished 0})
  65. (let [r (<! (sync/apply-filetxns-partitions
  66. nil user-uuid graph-uuid base-path partitioned-filetxns repo nil (atom false)
  67. (fn [filetxns]
  68. (state/set-file-sync-download-init-state!
  69. {:downloading-files (mapv sync/relative-path filetxns)}))
  70. (fn [filetxns]
  71. (state/set-file-sync-download-init-state!
  72. {:finished (+ (count filetxns)
  73. (or (:finished (state/get-file-sync-download-init-state)) 0))}))))]
  74. (if (instance? ExceptionInfo r)
  75. ;; TODO: add re-download button
  76. (notification/show! (str "Download graph failed: " (ex-cause r)) :warning)
  77. (do (state/reset-file-sync-download-init-state!)
  78. (<! (sync/update-graphs-txid! latest-txid graph-uuid user-uuid repo))))))))
  79. (defn load-session-graphs
  80. []
  81. (when-not (state/sub [:file-sync/remote-graphs :loading])
  82. (go (state/set-state! [:file-sync/remote-graphs :loading] true)
  83. (let [graphs (<! (list-graphs))]
  84. (state/set-state! :file-sync/remote-graphs {:loading false :graphs graphs})))))
  85. (defn reset-session-graphs
  86. []
  87. (state/set-state! :file-sync/remote-graphs {:loading false :graphs nil}))
  88. (defn init-graph [graph-uuid]
  89. (let [repo (state/get-current-repo)
  90. base-path (config/get-repo-dir repo)
  91. user-uuid (user/user-uuid)]
  92. ;; FIXME: when switching graph, sync-start is not called. set-env is not called as well.
  93. (sync/set-env sync/rsapi config/FILE-SYNC-PROD?
  94. "AGE-SECRET-KEY-1RRP2D43M00FTPARY5MJNN0Z4D6K8NDWC9ME5P60ZE59EDKMXP9PQK0P6YA"
  95. "age1sk2zx4lxcy47tjcgmfdz65sxcpw92k8fjpdencmcgyncxtexfupsz38tcg")
  96. (sync/update-graphs-txid! 0 graph-uuid user-uuid repo)
  97. (go (sync/sync-stop)
  98. (<! (download-all-files repo graph-uuid user-uuid base-path))
  99. (println :debug sync/graphs-txid)
  100. (swap! refresh-file-sync-component not)
  101. (state/pub-event! [:graph/switch repo {:persist? false}]))))
  102. (defn- download-version-file [graph-uuid file-uuid version-uuid]
  103. (go
  104. (let [key (path/join "version-files" file-uuid version-uuid)
  105. r (<! (sync/update-local-files
  106. sync/rsapi graph-uuid (config/get-repo-dir (state/get-current-repo)) [key]))]
  107. (if (instance? ExceptionInfo r)
  108. (notification/show! (ex-cause r) :error)
  109. (notification/show! [:div
  110. [:div "Downloaded version file at: "]
  111. [:div key]] :success false))
  112. (when-not (instance? ExceptionInfo r)
  113. (path/join "logseq" key)))))
  114. (defn- list-file-local-versions
  115. [page]
  116. (go
  117. (when-let [path (-> page :block/file :file/path)]
  118. (let [base-path (config/get-repo-dir (state/get-current-repo))
  119. rel-path (string/replace-first path base-path "")
  120. version-files-dir (->> (path/join "logseq/version-files/local" rel-path)
  121. path/parse
  122. (#(js->clj % :keywordize-keys true))
  123. ((juxt :dir :name))
  124. (apply path/join base-path))
  125. version-file-paths* (<! (p->c (fs/readdir version-files-dir)))]
  126. (when-not (instance? ExceptionInfo version-file-paths*)
  127. (let [version-file-paths
  128. (filterv
  129. ;; filter dir
  130. (fn [dir-or-file]
  131. (-> (path/parse dir-or-file)
  132. (js->clj :keywordize-keys true)
  133. :ext
  134. seq))
  135. (js->clj (<! (p->c (fs/readdir version-files-dir)))))]
  136. (mapv
  137. (fn [path]
  138. (let [create-time
  139. (-> (path/parse path)
  140. (js->clj :keywordize-keys true)
  141. :name
  142. (#(tf/parse (tf/formatter "yyyy-MM-dd'T'HH_mm_ss.SSSZZ") %)))]
  143. {:create-time create-time :path path :relative-path (string/replace-first path base-path "")}))
  144. version-file-paths)))))))
  145. (defn list-file-versions [graph-uuid page]
  146. (let [file-id (:db/id (:block/file page))]
  147. (when-let [path (:file/path (db/entity file-id))]
  148. (let [base-path (config/get-repo-dir (state/get-current-repo))
  149. path* (string/replace-first path base-path "")]
  150. (go
  151. (let [version-list (:VersionList
  152. (<! (sync/get-remote-file-versions sync/remoteapi graph-uuid path*)))
  153. local-version-list (<! (list-file-local-versions page))
  154. all-version-list (->> (concat version-list local-version-list)
  155. (sort-by #(or (tc/from-string (:CreateTime %))
  156. (:create-time %))
  157. >))]
  158. (notification/show! [:div
  159. [:div.font-bold "File history - " path*]
  160. [:hr.my-2]
  161. (for [version all-version-list]
  162. (let [version-uuid (or (:VersionUUID version) (:relative-path version))
  163. local? (some? (:relative-path version))]
  164. [:div.my-4 {:key version-uuid}
  165. [:div
  166. [:a.text-xs.inline
  167. {:on-click #(if local?
  168. (js/window.apis.openPath (:path version))
  169. (go
  170. (when-let [relative-path
  171. (<! (download-version-file graph-uuid
  172. (:FileUUID version)
  173. (:VersionUUID version)))]
  174. (js/window.apis.openPath (path/join base-path relative-path)))))}
  175. version-uuid]
  176. (when-not local?
  177. [:div.opacity-70 (str "Size: " (:Size version))])]
  178. [:div.opacity-50
  179. (util/time-ago (or (tc/from-string (:CreateTime version))
  180. (:create-time version)))]]))]
  181. :success false)))))))
  182. (defn get-current-graph-uuid [] (second @sync/graphs-txid))
  183. (def *wait-syncing-graph (atom nil))
  184. (defn set-wait-syncing-graph
  185. [graph]
  186. (reset! *wait-syncing-graph graph))
  187. (defn init-remote-graph
  188. [local]
  189. (when-let [graph (and local @*wait-syncing-graph)]
  190. (notification/show!
  191. (str "Start to sync <" (:GraphName graph) "> to <" local ">")
  192. :warning)
  193. (init-graph (:GraphUUID graph))
  194. (state/close-modal!)))