file.cljs 10 KB

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