file.cljs 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. (ns frontend.handler.file
  2. (:refer-clojure :exclude [load-file])
  3. (:require [frontend.config :as config]
  4. [frontend.db :as db]
  5. [frontend.fs :as fs]
  6. [frontend.fs.nfs :as nfs]
  7. [frontend.fs.capacitor-fs :as capacitor-fs]
  8. [frontend.handler.common.file :as file-common-handler]
  9. [frontend.handler.repo-config :as repo-config-handler]
  10. [frontend.handler.global-config :as global-config-handler]
  11. [frontend.handler.ui :as ui-handler]
  12. [frontend.state :as state]
  13. [frontend.util :as util]
  14. [logseq.graph-parser.util :as gp-util]
  15. [electron.ipc :as ipc]
  16. [lambdaisland.glogi :as log]
  17. [promesa.core :as p]
  18. [frontend.mobile.util :as mobile-util]
  19. [logseq.graph-parser.config :as gp-config]
  20. ["path" :as path]))
  21. ;; TODO: extract all git ops using a channel
  22. (defn load-file
  23. [repo-url path]
  24. (->
  25. (p/let [content (fs/read-file (config/get-repo-dir repo-url) path)]
  26. content)
  27. (p/catch
  28. (fn [e]
  29. (println "Load file failed: " path)
  30. (js/console.error e)))))
  31. (defn load-multiple-files
  32. [repo-url paths]
  33. (doall
  34. (mapv #(load-file repo-url %) paths)))
  35. (defn- keep-formats
  36. [files formats]
  37. (filter
  38. (fn [file]
  39. (let [format (gp-util/get-format file)]
  40. (contains? formats format)))
  41. files))
  42. (defn- only-text-formats
  43. [files]
  44. (keep-formats files (gp-config/text-formats)))
  45. (defn- only-image-formats
  46. [files]
  47. (keep-formats files (gp-config/img-formats)))
  48. (defn load-files-contents!
  49. [repo-url files ok-handler]
  50. (let [images (only-image-formats files)
  51. files (only-text-formats files)]
  52. (-> (p/all (load-multiple-files repo-url files))
  53. (p/then (fn [contents]
  54. (let [file-contents (cond->
  55. (zipmap files contents)
  56. (seq images)
  57. (merge (zipmap images (repeat (count images) ""))))
  58. file-contents (for [[file content] file-contents]
  59. {:file/path (gp-util/path-normalize file)
  60. :file/content content})]
  61. (ok-handler file-contents))))
  62. (p/catch (fn [error]
  63. (log/error :nfs/load-files-error repo-url)
  64. (log/error :exception error))))))
  65. (defn backup-file!
  66. "Backup db content to bak directory"
  67. [repo-url path db-content content]
  68. (cond
  69. (util/electron?)
  70. (ipc/ipc "backupDbFile" repo-url path db-content content)
  71. (mobile-util/native-platform?)
  72. (capacitor-fs/backup-file-handle-changed! repo-url path db-content)
  73. :else
  74. nil))
  75. ;; TODO: Remove this function in favor of `alter-files`
  76. (defn alter-file
  77. [repo path content {:keys [reset? re-render-root? from-disk? skip-compare? new-graph? verbose]
  78. :or {reset? true
  79. re-render-root? false
  80. from-disk? false
  81. skip-compare? false}}]
  82. (let [original-content (db/get-file repo path)
  83. write-file! (if from-disk?
  84. #(p/resolved nil)
  85. #(let [path-dir (if (= (path/dirname path) (global-config-handler/global-config-dir))
  86. (global-config-handler/global-config-dir)
  87. (config/get-repo-dir repo))]
  88. (fs/write-file! repo path-dir path content
  89. (assoc (when original-content {:old-content original-content})
  90. :skip-compare? skip-compare?))))
  91. opts {:new-graph? new-graph?
  92. :from-disk? from-disk?}]
  93. (if reset?
  94. (do
  95. (when-let [page-id (db/get-file-page-id path)]
  96. (db/transact! repo
  97. [[:db/retract page-id :block/alias]
  98. [:db/retract page-id :block/tags]]
  99. opts))
  100. (file-common-handler/reset-file! repo path content (merge opts
  101. (when (some? verbose) {:verbose verbose}))))
  102. (db/set-file-content! repo path content opts))
  103. (util/p-handle (write-file!)
  104. (fn [_]
  105. (cond
  106. (= path (config/get-repo-config-path repo))
  107. (repo-config-handler/restore-repo-config! repo)
  108. (= path (global-config-handler/global-config-path))
  109. (global-config-handler/restore-global-config! repo)
  110. (= path (config/get-custom-css-path repo))
  111. (ui-handler/add-style-if-exists!))
  112. (when re-render-root? (ui-handler/re-render-root!)))
  113. (fn [error]
  114. (when (= path (global-config-handler/global-config-path))
  115. (state/pub-event! [:notification/show
  116. {:content (str "Failed to write to file " path)
  117. :status :error}]))
  118. (println "Write file failed, path: " path ", content: " content)
  119. (log/error :write/failed error)))))
  120. (defn set-file-content!
  121. [repo path new-content]
  122. (alter-file repo path new-content {:reset? false
  123. :re-render-root? false}))
  124. (defn alter-files-handler!
  125. [repo files {:keys [finish-handler]} file->content]
  126. (let [write-file-f (fn [[path content]]
  127. (when path
  128. (let [original-content (get file->content path)]
  129. (-> (p/let [_ (or
  130. (util/electron?)
  131. (nfs/check-directory-permission! repo))]
  132. (fs/write-file! repo (config/get-repo-dir repo) path content
  133. {:old-content original-content}))
  134. (p/catch (fn [error]
  135. (state/pub-event! [:notification/show
  136. {:content (str "Failed to save the file " path ". Error: "
  137. (str error))
  138. :status :error
  139. :clear? false}])
  140. (state/pub-event! [:instrument {:type :write-file/failed
  141. :payload {:path path
  142. :content-length (count content)
  143. :error-str (str error)
  144. :error error}}])
  145. (log/error :write-file/failed {:path path
  146. :content content
  147. :error error})))))))
  148. finish-handler (fn []
  149. (when finish-handler
  150. (finish-handler)))]
  151. (-> (p/all (map write-file-f files))
  152. (p/then (fn []
  153. (finish-handler)))
  154. (p/catch (fn [error]
  155. (println "Alter files failed:")
  156. (js/console.error error))))))
  157. (defn alter-files
  158. [repo files {:keys [reset? update-db?]
  159. :or {reset? false
  160. update-db? true}
  161. :as opts}]
  162. ;; old file content
  163. (let [file->content (let [paths (map first files)]
  164. (zipmap paths
  165. (map (fn [path] (db/get-file repo path)) paths)))]
  166. ;; update db
  167. (when update-db?
  168. (doseq [[path content] files]
  169. (if reset?
  170. (file-common-handler/reset-file! repo path content)
  171. (db/set-file-content! repo path content))))
  172. (alter-files-handler! repo files opts file->content)))
  173. (defn watch-for-current-graph-dir!
  174. []
  175. (when-let [repo (state/get-current-repo)]
  176. (when-let [dir (config/get-repo-dir repo)]
  177. ;; An unwatch shouldn't be needed on startup. However not having this
  178. ;; after an app refresh can cause stale page data to load
  179. (fs/unwatch-dir! dir)
  180. (fs/watch-dir! dir))))
  181. (defn create-metadata-file
  182. [repo-url encrypted?]
  183. (let [repo-dir (config/get-repo-dir repo-url)
  184. path (str config/app-name "/" config/metadata-file)
  185. file-path (str "/" path)
  186. default-content (if encrypted? "{:db/encrypted? true}" "{}")]
  187. (p/let [_ (fs/mkdir-if-not-exists (util/safe-path-join repo-dir config/app-name))
  188. file-exists? (fs/create-if-not-exists repo-url repo-dir file-path default-content)]
  189. (when-not file-exists?
  190. (file-common-handler/reset-file! repo-url path default-content)))))
  191. (defn create-pages-metadata-file
  192. [repo-url]
  193. (let [repo-dir (config/get-repo-dir repo-url)
  194. path (str config/app-name "/" config/pages-metadata-file)
  195. file-path (str "/" path)
  196. default-content "{}"]
  197. (p/let [_ (fs/mkdir-if-not-exists (util/safe-path-join repo-dir config/app-name))
  198. file-exists? (fs/create-if-not-exists repo-url repo-dir file-path default-content)]
  199. (when-not file-exists?
  200. (file-common-handler/reset-file! repo-url path default-content)))))