mirror of
https://github.com/logseq/logseq.git
synced 2026-04-24 22:25:01 +00:00
feat: add encryption back
This commit is contained in:
@@ -52,6 +52,7 @@ const portal = new MagicPortal(worker);
|
||||
<script defer src="./js/lsplugin.core.js"></script>
|
||||
<script defer src="./js/main.js"></script>
|
||||
<script defer src="./js/code-editor.js"></script>
|
||||
<script defer src="./js/age-encryption.js"></script>
|
||||
<script defer src="./js/excalidraw.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -12,12 +12,12 @@
|
||||
:js-options {:ignore-asset-requires true} ;; handle `require(xxx.css)`
|
||||
:modules {:main
|
||||
{:init-fn frontend.core/init}
|
||||
;; :graph
|
||||
;; {:entries [frontend.extensions.graph.force]
|
||||
;; :depends-on #{:main}}
|
||||
:code-editor
|
||||
{:entries [frontend.extensions.code]
|
||||
:depends-on #{:main}}
|
||||
:age-encryption
|
||||
{:entries [frontend.extensions.age-encryption]
|
||||
:depends-on #{:main}}
|
||||
:excalidraw
|
||||
{:entries [frontend.extensions.excalidraw]
|
||||
:depends-on #{:main}}}
|
||||
@@ -69,12 +69,12 @@
|
||||
:js-options {:ignore-asset-requires true}
|
||||
:modules {:main
|
||||
{:init-fn frontend.publishing/init}
|
||||
;; :graph
|
||||
;; {:entries [frontend.extensions.graph.force]
|
||||
;; :depends-on #{:main}}
|
||||
:code-editor
|
||||
{:entries [frontend.extensions.code]
|
||||
:depends-on #{:main}}
|
||||
:age-encryption
|
||||
{:entries [frontend.extensions.age-encryption]
|
||||
:depends-on #{:main}}
|
||||
:excalidraw
|
||||
{:entries [frontend.extensions.excalidraw]
|
||||
:depends-on #{:main}}}
|
||||
|
||||
@@ -127,7 +127,7 @@
|
||||
;; TODO: ugly, replace with ls-files and filter with ".map"
|
||||
_ (p/all (map (fn [file]
|
||||
(. fs removeSync (path/join static-dir "js" (str file ".map"))))
|
||||
["main.js" "code-editor.js" "excalidraw.js"]))]
|
||||
["main.js" "code-editor.js" "excalidraw.js" "age-encryption.js"]))]
|
||||
(. dialog showMessageBox (clj->js {:message (str "Export public pages and publish assets to " root-dir " successfully")})))))))
|
||||
|
||||
(defn setup-app-manager!
|
||||
|
||||
176
src/main/frontend/components/encryption.cljs
Normal file
176
src/main/frontend/components/encryption.cljs
Normal file
@@ -0,0 +1,176 @@
|
||||
(ns frontend.components.encryption
|
||||
(:require [clojure.string :as string]
|
||||
[frontend.context.i18n :refer [t]]
|
||||
[frontend.encrypt :as e]
|
||||
[frontend.handler.metadata :as metadata-handler]
|
||||
[frontend.handler.notification :as notification]
|
||||
[frontend.state :as state]
|
||||
[frontend.ui :as ui]
|
||||
[frontend.util :as util]
|
||||
[promesa.core :as p]
|
||||
[rum.core :as rum]))
|
||||
|
||||
(rum/defcs encryption-dialog-inner <
|
||||
(rum/local false ::reveal-secret-phrase?)
|
||||
[state repo-url close-fn]
|
||||
(let [reveal-secret-phrase? (get state ::reveal-secret-phrase?)
|
||||
public-key (e/get-public-key repo-url)
|
||||
private-key (e/get-secret-key repo-url)]
|
||||
[:div
|
||||
[:div.sm:flex.sm:items-start
|
||||
[:div.mt-3.text-center.sm:mt-0.sm:text-left
|
||||
[:h3#modal-headline.text-lg.leading-6.font-medium
|
||||
"This graph is encrypted with " [:a {:href "https://age-encryption.org/" :target "_blank" :rel "noopener"} "age-encryption.org/v1"]]]]
|
||||
|
||||
[:div.mt-1
|
||||
[:div.max-w-2xl.rounded-md.shadow-sm.sm:max-w-xl
|
||||
[:div.cursor-pointer.block.w-full.rounded-sm.p-2
|
||||
{:on-click (fn []
|
||||
(when (not @reveal-secret-phrase?)
|
||||
(reset! reveal-secret-phrase? true)))}
|
||||
[:div.font-medium "Public Key:"]
|
||||
[:div.font-mono.select-all.break-all public-key]
|
||||
(if @reveal-secret-phrase?
|
||||
[:div
|
||||
[:div.mt-1.font-medium "Private Key:"]
|
||||
[:div.font-mono.select-all.break-all private-key]]
|
||||
[:div.underline "click to view the private key"])]]]
|
||||
|
||||
[:div.mt-5.sm:mt-4.sm:flex.sm:flex-row-reverse
|
||||
[:span.mt-3.flex.w-full.rounded-md.shadow-sm.sm:mt-0.sm:w-auto
|
||||
[:button.inline-flex.justify-center.w-full.rounded-md.border.border-gray-300.px-4.py-2.bg-white.text-base.leading-6.font-medium.text-gray-700.shadow-sm.hover:text-gray-500.focus:outline-none.focus:border-blue-300.focus:shadow-outline-blue.transition.ease-in-out.duration-150.sm:text-sm.sm:leading-5
|
||||
{:type "button"
|
||||
:on-click close-fn}
|
||||
(t :close)]]]]))
|
||||
|
||||
(defn encryption-dialog
|
||||
[repo-url]
|
||||
(fn [close-fn]
|
||||
(encryption-dialog-inner repo-url close-fn)))
|
||||
|
||||
(rum/defcs input-password-inner <
|
||||
(rum/local "" ::password)
|
||||
(rum/local "" ::password-confirm)
|
||||
[state repo-url close-fn]
|
||||
(let [password (get state ::password)
|
||||
password-confirm (get state ::password-confirm)]
|
||||
[:div
|
||||
[:div.sm:flex.sm:items-start
|
||||
[:div.mt-3.text-center.sm:mt-0.sm:text-left
|
||||
[:h3#modal-headline.text-lg.leading-6.font-medium.font-bold
|
||||
"Enter a password"]]]
|
||||
|
||||
(ui/admonition
|
||||
:warning
|
||||
[:div.opacity-70
|
||||
"Choose a strong and hard to guess password.\nIf you lose your password, all the data can't be decrypted!! Please make sure you remember the password you have set, or you can keep a secure backup of the password."])
|
||||
[:input.form-input.block.w-full.sm:text-sm.sm:leading-5.my-2
|
||||
{:type "password"
|
||||
:placeholder "Password"
|
||||
:auto-focus true
|
||||
:on-change (fn [e]
|
||||
(reset! password (util/evalue e)))}]
|
||||
[:input.form-input.block.w-full.sm:text-sm.sm:leading-5.my-2
|
||||
{:type "password"
|
||||
:placeholder "Re-enter the password"
|
||||
:on-change (fn [e]
|
||||
(reset! password-confirm (util/evalue e)))}]
|
||||
|
||||
[:div.mt-5.sm:mt-4.sm:flex.sm:flex-row-reverse
|
||||
[:span.flex.w-full.rounded-md.shadow-sm.sm:ml-3.sm:w-auto
|
||||
[:button.inline-flex.justify-center.w-full.rounded-md.border.border-transparent.px-4.py-2.bg-indigo-600.text-base.leading-6.font-medium.text-white.shadow-sm.hover:bg-indigo-500.focus:outline-none.focus:border-indigo-700.focus:shadow-outline-indigo.transition.ease-in-out.duration-150.sm:text-sm.sm:leading-5
|
||||
{:type "button"
|
||||
:on-click (fn []
|
||||
(let [value @password]
|
||||
(cond
|
||||
(string/blank? value)
|
||||
nil
|
||||
|
||||
(not= @password @password-confirm)
|
||||
(notification/show! "The passwords are not matched." :error)
|
||||
|
||||
:else
|
||||
(p/let [keys (e/generate-key-pair-and-save! repo-url)
|
||||
db-encrypted-secret (e/encrypt-with-passphrase value keys)]
|
||||
(metadata-handler/set-db-encrypted-secret! db-encrypted-secret)
|
||||
(close-fn true)))))}
|
||||
"Submit"]]]]))
|
||||
|
||||
(defn input-password
|
||||
[repo-url close-fn]
|
||||
(fn [_close-fn]
|
||||
(input-password-inner repo-url close-fn)))
|
||||
|
||||
(rum/defcs encryption-setup-dialog-inner
|
||||
[state repo-url close-fn]
|
||||
[:div
|
||||
[:div.sm:flex.sm:items-start
|
||||
[:div.mt-3.text-center.sm:mt-0.sm:text-left
|
||||
[:h3#modal-headline.text-lg.leading-6.font-medium
|
||||
"Do you want to create an encrypted graph?"]]]
|
||||
|
||||
[:div.mt-5.sm:mt-4.sm:flex.sm:flex-row-reverse
|
||||
[:span.flex.w-full.rounded-md.shadow-sm.sm:ml-3.sm:w-auto
|
||||
[:button.inline-flex.justify-center.w-full.rounded-md.border.border-transparent.px-4.py-2.bg-indigo-600.text-base.leading-6.font-medium.text-white.shadow-sm.hover:bg-indigo-500.focus:outline-none.focus:border-indigo-700.focus:shadow-outline-indigo.transition.ease-in-out.duration-150.sm:text-sm.sm:leading-5
|
||||
{:type "button"
|
||||
:on-click (fn []
|
||||
(state/set-modal! (input-password repo-url close-fn)))}
|
||||
(t :yes)]]
|
||||
[:span.mt-3.flex.w-full.rounded-md.shadow-sm.sm:mt-0.sm:w-auto
|
||||
[:button.inline-flex.justify-center.w-full.rounded-md.border.border-gray-300.px-4.py-2.bg-white.text-base.leading-6.font-medium.text-gray-700.shadow-sm.hover:text-gray-500.focus:outline-none.focus:border-blue-300.focus:shadow-outline-blue.transition.ease-in-out.duration-150.sm:text-sm.sm:leading-5
|
||||
{:type "button"
|
||||
:on-click (fn [] (close-fn false))}
|
||||
(t :no)]]]])
|
||||
|
||||
(defn encryption-setup-dialog
|
||||
[repo-url close-fn]
|
||||
(fn [close-modal-fn]
|
||||
(let [close-fn (fn [encrypted?]
|
||||
(close-fn encrypted?)
|
||||
(close-modal-fn))]
|
||||
(encryption-setup-dialog-inner repo-url close-fn))))
|
||||
|
||||
(rum/defcs encryption-input-secret-inner <
|
||||
(rum/local "" ::secret)
|
||||
(rum/local false ::loading)
|
||||
[state _repo-url db-encrypted-secret close-fn]
|
||||
(let [secret (::secret state)
|
||||
loading (::loading state)]
|
||||
[:div
|
||||
[:div.sm:flex.sm:items-start
|
||||
[:div.mt-3.text-center.sm:mt-0.sm:text-left
|
||||
[:h3#modal-headline.text-lg.leading-6.font-medium
|
||||
"Enter your password"]]]
|
||||
|
||||
[:input.form-input.block.w-full.sm:text-sm.sm:leading-5.my-2
|
||||
{:type "password"
|
||||
:auto-focus true
|
||||
:on-change (fn [e]
|
||||
(reset! secret (util/evalue e)))}]
|
||||
|
||||
[:div.mt-5.sm:mt-4.sm:flex.sm:flex-row-reverse
|
||||
[:span.flex.w-full.rounded-md.shadow-sm.sm:ml-3.sm:w-auto
|
||||
[:button.inline-flex.justify-center.w-full.rounded-md.border.border-transparent.px-4.py-2.bg-indigo-600.text-base.leading-6.font-medium.text-white.shadow-sm.hover:bg-indigo-500.focus:outline-none.focus:border-indigo-700.focus:shadow-outline-indigo.transition.ease-in-out.duration-150.sm:text-sm.sm:leading-5
|
||||
{:type "button"
|
||||
:on-click (fn []
|
||||
(reset! loading true)
|
||||
(let [value @secret]
|
||||
(when-not (string/blank? value) ; TODO: length or other checks
|
||||
(let [repo (state/get-current-repo)]
|
||||
(p/do!
|
||||
(-> (e/decrypt-with-passphrase value db-encrypted-secret)
|
||||
(p/then (fn [keys]
|
||||
(e/save-key-pair! repo keys)
|
||||
(close-fn true)
|
||||
(state/set-state! :encryption/graph-parsing? false)))
|
||||
(p/catch #(notification/show! "The password is not matched." :warning true))
|
||||
(p/finally #(reset! loading false))))))))}
|
||||
(if @loading (ui/loading "Decrypting") "Decrypt")]]]]))
|
||||
|
||||
(defn encryption-input-secret-dialog
|
||||
[repo-url db-encrypted-secret close-fn]
|
||||
(fn [close-modal-fn]
|
||||
(let [close-fn (fn [encrypted?]
|
||||
(close-fn encrypted?)
|
||||
(close-modal-fn))]
|
||||
(encryption-input-secret-inner repo-url db-encrypted-secret close-fn))))
|
||||
@@ -17,7 +17,9 @@
|
||||
[frontend.text :as text]
|
||||
[promesa.core :as p]
|
||||
[electron.ipc :as ipc]
|
||||
[goog.object :as gobj]))
|
||||
[goog.object :as gobj]
|
||||
[frontend.components.encryption :as encryption]
|
||||
[frontend.encrypt :as e]))
|
||||
|
||||
(rum/defc add-repo
|
||||
[args]
|
||||
@@ -60,6 +62,11 @@
|
||||
:href url}
|
||||
(db/get-repo-path url)])
|
||||
[:div.controls
|
||||
(when (e/encrypted-db? url)
|
||||
[:a.control {:title "Show encryption information about this graph"
|
||||
:on-click (fn []
|
||||
(state/set-modal! (encryption/encryption-dialog url)))}
|
||||
"🔐"])
|
||||
[:a.text-gray-400.ml-4.font-medium.text-sm
|
||||
{:title "No worries, unlink this graph will clear its cache only, it does not remove your files on the disk."
|
||||
:on-click (fn []
|
||||
|
||||
@@ -390,6 +390,14 @@
|
||||
;; (let [value (not enable-block-timestamps?)]
|
||||
;; (config-handler/set-config! :feature/enable-block-timestamps? value)))))
|
||||
|
||||
(defn encryption-row [t enable-encryption?]
|
||||
(toggle "enable_encryption"
|
||||
(t :settings-page/enable-encryption)
|
||||
enable-encryption?
|
||||
#(let [value (not enable-encryption?)]
|
||||
(config-handler/set-config! :feature/enable-encryption? value))
|
||||
[:div.text-sm.opacity-50 "⚠️ This feature is experimental"]))
|
||||
|
||||
(rum/defc keyboard-shortcuts-row [t]
|
||||
(row-with-button-action
|
||||
{:left-label (t :settings-page/customize-shortcuts)
|
||||
@@ -544,6 +552,7 @@
|
||||
preferred-workflow (state/get-preferred-workflow)
|
||||
enable-timetracking? (state/enable-timetracking?)
|
||||
enable-journals? (state/enable-journals? current-repo)
|
||||
enable-encryption? (state/enable-encryption? current-repo)
|
||||
enable-all-pages-public? (state/all-pages-public?)
|
||||
logical-outdenting? (state/logical-outdenting?)
|
||||
enable-tooltip? (state/enable-tooltip?)
|
||||
@@ -578,6 +587,7 @@
|
||||
:on-key-press (fn [e]
|
||||
(when (= "Enter" (util/ekey e))
|
||||
(update-home-page e)))}]]]])
|
||||
(encryption-row t enable-encryption?)
|
||||
(enable-all-pages-public-row t enable-all-pages-public?)
|
||||
(zotero-settings-row t)
|
||||
(auto-push-row t current-repo enable-git-auto-push?)]))
|
||||
|
||||
@@ -276,6 +276,7 @@
|
||||
(def config-file "config.edn")
|
||||
(def custom-css-file "custom.css")
|
||||
(def custom-js-file "custom.js")
|
||||
(def metadata-file "metadata.edn")
|
||||
(def pages-metadata-file "pages-metadata.edn")
|
||||
|
||||
(def config-default-content (rc/inline "config.edn"))
|
||||
@@ -368,6 +369,13 @@
|
||||
(when repo
|
||||
(get-file-path repo (str app-name "/" config-file)))))
|
||||
|
||||
(defn get-metadata-path
|
||||
([]
|
||||
(get-metadata-path (state/get-current-repo)))
|
||||
([repo]
|
||||
(when repo
|
||||
(get-file-path repo (str app-name "/" metadata-file)))))
|
||||
|
||||
(defn get-pages-metadata-path
|
||||
([]
|
||||
(get-pages-metadata-path (state/get-current-repo)))
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
:ast/version {}
|
||||
:db/type {}
|
||||
:db/ident {:db/unique :db.unique/identity}
|
||||
:db/encrypted? {}
|
||||
:db/encryption-keys {}
|
||||
|
||||
:recent/pages {}
|
||||
|
||||
|
||||
@@ -162,6 +162,7 @@
|
||||
:settings-page/enable-tooltip "Tooltips"
|
||||
:settings-page/enable-journals "Journals"
|
||||
:settings-page/enable-all-pages-public "All pages public when publishing"
|
||||
:settings-page/enable-encryption "Encryption"
|
||||
:settings-page/customize-shortcuts "Keyboard shortcuts"
|
||||
:settings-page/shortcut-settings "Customize shortcuts"
|
||||
:settings-page/home-default-page "Set the default home page"
|
||||
@@ -585,6 +586,7 @@
|
||||
:settings-page/customize-shortcuts "Tastaturbefehle"
|
||||
:settings-page/disable-sentry "Nutzungs- und Diagnostik-Daten an Logseq senden"
|
||||
:settings-page/edit-custom-css "custom.css bearbeiten"
|
||||
:settings-page/enable-encryption "Verschlüsselung"
|
||||
:settings-page/enable-shortcut-tooltip "Tooltips für Verknüpfungen aktivieren"
|
||||
:settings-page/enable-tooltip "Tooltips"
|
||||
:settings-page/shortcut-settings "Verknüpfungen anpassen"
|
||||
@@ -895,6 +897,7 @@
|
||||
:settings-page/enable-tooltip "开启提示框"
|
||||
:settings-page/enable-journals "开启日记"
|
||||
:settings-page/enable-all-pages-public "发布所有页面"
|
||||
:settings-page/enable-encryption "激活加密功能"
|
||||
:settings-page/customize-shortcuts "自定义快捷键"
|
||||
:settings-page/shortcut-settings "快捷键设置"
|
||||
:settings-page/home-default-page "设置首页默认页面"
|
||||
@@ -1476,6 +1479,7 @@
|
||||
:settings-page/enable-timetracking "Habilitar rastreo de tiempo"
|
||||
:settings-page/enable-tooltip "Habilitar descripción emergente"
|
||||
:settings-page/enable-journals "Habilitar diarios"
|
||||
:settings-page/enable-encryption "Habilitar función de cifrado"
|
||||
:settings-page/home-default-page "Establecer página de inicio"
|
||||
:settings-page/enable-block-time "Habilitar marcas temporales de bloque"
|
||||
:settings-page/clear-cache "Limpiar caché"
|
||||
@@ -1706,6 +1710,7 @@
|
||||
:settings-page/preferred-workflow "Foretrukket arbeidslflyt"
|
||||
:settings-page/enable-shortcut-tooltip "Skru på tooltip for snarveier"
|
||||
:settings-page/enable-timetracking "Aktiver tidssporing"
|
||||
:settings-page/enable-encryption "Aktiver kryptering"
|
||||
:settings-page/enable-tooltip "Aktiver verktøytips"
|
||||
:settings-page/enable-journals "Aktiver dagbøker"
|
||||
:settings-page/enable-all-pages-public "Aktiver alle sider som offentlige ved publisering"
|
||||
@@ -1994,6 +1999,7 @@
|
||||
:settings-page/enable-tooltip "Ativar dicas de ferramentas"
|
||||
:settings-page/enable-journals "Ativar diários"
|
||||
:settings-page/enable-all-pages-public "Ativar todas as páginas públicas ao publicar"
|
||||
:settings-page/enable-encryption "Ativar funcionalidade de criptografia"
|
||||
:settings-page/customize-shortcuts "Atalhos de teclado"
|
||||
:settings-page/shortcut-settings "Personalizar atalhos"
|
||||
:settings-page/home-default-page "Definir a página inicial padrão"
|
||||
@@ -2315,6 +2321,7 @@
|
||||
:settings-page/enable-tooltip "Dicas de atalhos"
|
||||
:settings-page/enable-journals "Diários"
|
||||
:settings-page/enable-all-pages-public "Todas as páginas públicas ao publicar"
|
||||
:settings-page/enable-encryption "Encriptação"
|
||||
:settings-page/customize-shortcuts "Atalhos de teclado"
|
||||
:settings-page/shortcut-settings "Personalizar atalhos"
|
||||
:settings-page/home-default-page "Definir a página inicial predefinida"
|
||||
@@ -2610,6 +2617,7 @@
|
||||
:settings-page/enable-shortcut-tooltip "Всплывающие подсказки горячих клавиш"
|
||||
:settings-page/enable-journals "Включить Дневники"
|
||||
:settings-page/enable-all-pages-public "Все страницы общедоступны при публикации"
|
||||
:settings-page/enable-encryption "Функции шифрования"
|
||||
:settings-page/customize-shortcuts "Горячие клавиши"
|
||||
:settings-page/shortcut-settings "Настроить горячие клавиши"
|
||||
:settings-page/home-default-page "Установить домашнюю страницу по умолчанию"
|
||||
@@ -2897,6 +2905,7 @@
|
||||
:settings-page/enable-tooltip "ツールチップ"
|
||||
:settings-page/enable-journals "日誌"
|
||||
:settings-page/enable-all-pages-public "パブリッシュ時には全てのページを公開する"
|
||||
:settings-page/enable-encryption "暗号化"
|
||||
:settings-page/customize-shortcuts "キーボードショートカット"
|
||||
:settings-page/shortcut-settings "ショートカットをカスタマイズ"
|
||||
:settings-page/home-default-page "デフォルトのホームページを設定"
|
||||
|
||||
104
src/main/frontend/encrypt.cljs
Normal file
104
src/main/frontend/encrypt.cljs
Normal file
@@ -0,0 +1,104 @@
|
||||
(ns frontend.encrypt
|
||||
(:require [frontend.utf8 :as utf8]
|
||||
[frontend.db.utils :as db-utils]
|
||||
[frontend.db :as db]
|
||||
[promesa.core :as p]
|
||||
[frontend.state :as state]
|
||||
[clojure.string :as str]
|
||||
[cljs.reader :as reader]
|
||||
[shadow.loader :as loader]
|
||||
[lambdaisland.glogi :as log]))
|
||||
|
||||
(defonce age-pem-header-line "-----BEGIN AGE ENCRYPTED FILE-----")
|
||||
(defonce age-version-line "age-encryption.org/v1")
|
||||
|
||||
(defn content-encrypted?
|
||||
[content]
|
||||
(when content
|
||||
(or (str/starts-with? content age-pem-header-line)
|
||||
(str/starts-with? content age-version-line))))
|
||||
|
||||
(defn encrypted-db?
|
||||
[repo-url]
|
||||
(db-utils/get-key-value repo-url :db/encrypted?))
|
||||
|
||||
(defn get-key-pair
|
||||
[repo-url]
|
||||
(db-utils/get-key-value repo-url :db/encryption-keys))
|
||||
|
||||
(defn save-key-pair!
|
||||
[repo-url keys]
|
||||
(let [keys (if (string? keys) (reader/read-string keys) keys)]
|
||||
(db/set-key-value repo-url :db/encryption-keys keys)
|
||||
(db/set-key-value repo-url :db/encrypted? true)))
|
||||
|
||||
(defn generate-key-pair
|
||||
[]
|
||||
(p/let [_ (loader/load :age-encryption)
|
||||
lazy-keygen (resolve 'frontend.extensions.age-encryption/keygen)
|
||||
js-keys (lazy-keygen)]
|
||||
(array-seq js-keys)))
|
||||
|
||||
(defn generate-key-pair-and-save!
|
||||
[repo-url]
|
||||
(when-not (get-key-pair repo-url)
|
||||
(p/let [keys (generate-key-pair)]
|
||||
(save-key-pair! repo-url keys)
|
||||
(pr-str keys))))
|
||||
|
||||
(defn get-public-key
|
||||
[repo-url]
|
||||
(second (get-key-pair repo-url)))
|
||||
|
||||
(defn get-secret-key
|
||||
[repo-url]
|
||||
(first (get-key-pair repo-url)))
|
||||
|
||||
(defn encrypt
|
||||
([content]
|
||||
(encrypt (state/get-current-repo) content))
|
||||
([repo-url content]
|
||||
(cond
|
||||
(encrypted-db? repo-url)
|
||||
(p/let [_ (loader/load :age-encryption)
|
||||
lazy-encrypt-with-x25519 (resolve 'frontend.extensions.age-encryption/encrypt-with-x25519)
|
||||
content (utf8/encode content)
|
||||
public-key (get-public-key repo-url)
|
||||
encrypted (lazy-encrypt-with-x25519 public-key content true)]
|
||||
(utf8/decode encrypted))
|
||||
:else
|
||||
(p/resolved content))))
|
||||
|
||||
(defn decrypt
|
||||
([content]
|
||||
(decrypt (state/get-current-repo) content))
|
||||
([repo-url content]
|
||||
(cond
|
||||
(and (encrypted-db? repo-url)
|
||||
(content-encrypted? content))
|
||||
(let [content (utf8/encode content)]
|
||||
(if-let [secret-key (get-secret-key repo-url)]
|
||||
(p/let [_ (loader/load :age-encryption)
|
||||
lazy-decrypt-with-x25519 (resolve 'frontend.extensions.age-encryption/decrypt-with-x25519)
|
||||
decrypted (lazy-decrypt-with-x25519 secret-key content)]
|
||||
(utf8/decode decrypted))
|
||||
(log/error :encrypt/empty-secret-key (str "Can't find the secret key for repo: " repo-url))))
|
||||
:else
|
||||
(p/resolved content))))
|
||||
|
||||
(defn encrypt-with-passphrase
|
||||
[passphrase content]
|
||||
(p/let [_ (loader/load :age-encryption)
|
||||
lazy-encrypt-with-user-passphrase (resolve 'frontend.extensions.age-encryption/encrypt-with-user-passphrase)
|
||||
content (utf8/encode content)
|
||||
encrypted (@lazy-encrypt-with-user-passphrase passphrase content true)]
|
||||
(utf8/decode encrypted)))
|
||||
|
||||
;; ;; TODO: What if decryption failed
|
||||
(defn decrypt-with-passphrase
|
||||
[passphrase content]
|
||||
(p/let [_ (loader/load :age-encryption)
|
||||
lazy-decrypt-with-user-passphrase (resolve 'frontend.extensions.age-encryption/decrypt-with-user-passphrase)
|
||||
content (utf8/encode content)
|
||||
decrypted (lazy-decrypt-with-user-passphrase passphrase content)]
|
||||
(utf8/decode decrypted)))
|
||||
23
src/main/frontend/extensions/age_encryption.cljs
Normal file
23
src/main/frontend/extensions/age_encryption.cljs
Normal file
@@ -0,0 +1,23 @@
|
||||
(ns frontend.extensions.age-encryption
|
||||
(:require ["regenerator-runtime/runtime"] ;; required for async npm module
|
||||
["@kanru/rage-wasm" :as rage]))
|
||||
|
||||
(defn keygen
|
||||
[]
|
||||
(rage/keygen))
|
||||
|
||||
(defn encrypt-with-x25519
|
||||
[public-key content armor]
|
||||
(rage/encrypt_with_x25519 public-key content armor))
|
||||
|
||||
(defn decrypt-with-x25519
|
||||
[secret-key content]
|
||||
(rage/decrypt_with_x25519 secret-key content))
|
||||
|
||||
(defn encrypt-with-user-passphrase
|
||||
[passphrase content armor]
|
||||
(rage/encrypt_with_user_passphrase passphrase content armor))
|
||||
|
||||
(defn decrypt-with-user-passphrase
|
||||
[passphrase content]
|
||||
(rage/decrypt_with_user_passphrase passphrase content))
|
||||
@@ -11,7 +11,8 @@
|
||||
[lambdaisland.glogi :as log]
|
||||
[promesa.core :as p]
|
||||
[frontend.db :as db]
|
||||
[clojure.string :as string]))
|
||||
[clojure.string :as string]
|
||||
[frontend.encrypt :as encrypt]))
|
||||
|
||||
(defonce nfs-record (nfs/->Nfs))
|
||||
(defonce bfs-record (bfs/->Bfs))
|
||||
@@ -74,17 +75,20 @@
|
||||
(defn write-file!
|
||||
[repo dir path content opts]
|
||||
(when content
|
||||
(->
|
||||
(p/let [_ (protocol/write-file! (get-fs dir) repo dir path content opts)]
|
||||
(when (= bfs-record (get-fs dir))
|
||||
(db/set-file-last-modified-at! repo (config/get-file-path repo path) (js/Date.))))
|
||||
(p/catch (fn [error]
|
||||
(log/error :file/write-failed {:dir dir
|
||||
:path path
|
||||
:error error})
|
||||
;; Disable this temporarily
|
||||
;; (js/alert "Current file can't be saved! Please copy its content to your local file system and click the refresh button.")
|
||||
)))))
|
||||
(let [fs-record (get-fs dir)]
|
||||
(p/let [md-or-org? (contains? #{"md" "markdown" "org"} (util/get-file-ext path))
|
||||
content (if-not md-or-org? content (encrypt/encrypt content))]
|
||||
(->
|
||||
(p/let [_ (protocol/write-file! (get-fs dir) repo dir path content opts)]
|
||||
(when (= bfs-record fs-record)
|
||||
(db/set-file-last-modified-at! repo (config/get-file-path repo path) (js/Date.))))
|
||||
(p/catch (fn [error]
|
||||
(log/error :file/write-failed {:dir dir
|
||||
:path path
|
||||
:error error})
|
||||
;; Disable this temporarily
|
||||
;; (js/alert "Current file can't be saved! Please copy its content to your local file system and click the refresh button.")
|
||||
)))))))
|
||||
|
||||
(defn read-file
|
||||
([dir path]
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
[promesa.core :as p]
|
||||
[rum.core :as rum]
|
||||
[frontend.state :as state]
|
||||
[frontend.db :as db]))
|
||||
[frontend.db :as db]
|
||||
[frontend.encrypt :as encrypt]))
|
||||
|
||||
(when (mobile-util/native-ios?)
|
||||
(defn iOS-ensure-documents!
|
||||
@@ -101,7 +102,10 @@
|
||||
(defn- contents-matched?
|
||||
[disk-content db-content]
|
||||
(when (and (string? disk-content) (string? db-content))
|
||||
(p/resolved (= (string/trim disk-content) (string/trim db-content)))))
|
||||
(if (encrypt/encrypted-db? (state/get-current-repo))
|
||||
(p/let [decrypted-content (encrypt/decrypt disk-content)]
|
||||
(= (string/trim decrypted-content) (string/trim db-content)))
|
||||
(p/resolved (= (string/trim disk-content) (string/trim db-content))))))
|
||||
|
||||
(defn- write-file-impl!
|
||||
[_this repo _dir path content {:keys [ok-handler error-handler old-content skip-compare?]} stat]
|
||||
@@ -136,7 +140,8 @@
|
||||
(not (contains? #{"excalidraw" "edn" "css"} ext))
|
||||
(not (string/includes? path "/.recycle/"))
|
||||
(zero? pending-writes))
|
||||
(state/pub-event! [:file/not-matched-from-disk path disk-content content])
|
||||
(p/let [disk-content (encrypt/decrypt disk-content)]
|
||||
(state/pub-event! [:file/not-matched-from-disk path disk-content content]))
|
||||
|
||||
:else
|
||||
(->
|
||||
@@ -144,7 +149,10 @@
|
||||
:data content
|
||||
:encoding (.-UTF8 Encoding)
|
||||
:recursive true}))]
|
||||
(db/set-file-content! repo (js/decodeURI path) content)
|
||||
(p/let [content (if (encrypt/encrypted-db? (state/get-current-repo))
|
||||
(encrypt/decrypt content)
|
||||
content)]
|
||||
(db/set-file-content! repo (js/decodeURI path) content))
|
||||
(when ok-handler
|
||||
(ok-handler repo path result))
|
||||
result)
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
[frontend.config :as config]
|
||||
[frontend.state :as state]
|
||||
[frontend.handler.notification :as notification]
|
||||
["/frontend/utils" :as utils]))
|
||||
["/frontend/utils" :as utils]
|
||||
[frontend.encrypt :as encrypt]))
|
||||
|
||||
;; We need to cache the file handles in the memory so that
|
||||
;; the browser will not keep asking permissions.
|
||||
@@ -57,7 +58,10 @@
|
||||
(defn- contents-matched?
|
||||
[disk-content db-content]
|
||||
(when (and (string? disk-content) (string? db-content))
|
||||
(p/resolved (= (string/trim disk-content) (string/trim db-content)))))
|
||||
(if (encrypt/encrypted-db? (state/get-current-repo))
|
||||
(p/let [decrypted-content (encrypt/decrypt disk-content)]
|
||||
(= (string/trim decrypted-content) (string/trim db-content)))
|
||||
(p/resolved (= (string/trim disk-content) (string/trim db-content))))))
|
||||
|
||||
(defrecord ^:large-vars/cleanup-todo Nfs []
|
||||
protocol/Fs
|
||||
@@ -171,12 +175,16 @@
|
||||
(not (contains? #{"excalidraw" "edn" "css"} ext))
|
||||
(not (string/includes? path "/.recycle/"))
|
||||
(zero? pending-writes))
|
||||
(state/pub-event! [:file/not-matched-from-disk path local-content content])
|
||||
(p/let [local-content (encrypt/decrypt local-content)]
|
||||
(state/pub-event! [:file/not-matched-from-disk path local-content content]))
|
||||
(p/let [_ (verify-permission repo file-handle true)
|
||||
_ (utils/writeFile file-handle content)
|
||||
file (.getFile file-handle)]
|
||||
(when file
|
||||
(db/set-file-content! repo path content)
|
||||
(p/let [content (if (encrypt/encrypted-db? (state/get-current-repo))
|
||||
(encrypt/decrypt content)
|
||||
content)]
|
||||
(db/set-file-content! repo path content))
|
||||
(nfs-saved-handler repo path file))))))
|
||||
(p/catch (fn [e]
|
||||
(js/console.error e))))
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
[frontend.util :as util]
|
||||
[goog.object :as gobj]
|
||||
[lambdaisland.glogi :as log]
|
||||
[promesa.core :as p]))
|
||||
[promesa.core :as p]
|
||||
[frontend.encrypt :as encrypt]))
|
||||
|
||||
(defn concat-path
|
||||
[dir path]
|
||||
@@ -27,7 +28,10 @@
|
||||
(defn- contents-matched?
|
||||
[disk-content db-content]
|
||||
(when (and (string? disk-content) (string? db-content))
|
||||
(p/resolved (= (string/trim disk-content) (string/trim db-content)))))
|
||||
(if (encrypt/encrypted-db? (state/get-current-repo))
|
||||
(p/let [decrypted-content (encrypt/decrypt disk-content)]
|
||||
(= (string/trim decrypted-content) (string/trim db-content)))
|
||||
(p/resolved (= (string/trim disk-content) (string/trim db-content))))))
|
||||
|
||||
(defn- write-file-impl!
|
||||
[this repo dir path content {:keys [ok-handler error-handler old-content skip-compare?]} stat]
|
||||
@@ -58,14 +62,18 @@
|
||||
(not (contains? #{"excalidraw" "edn" "css"} ext))
|
||||
(not (string/includes? path "/.recycle/"))
|
||||
(zero? pending-writes))
|
||||
(state/pub-event! [:file/not-matched-from-disk path disk-content content])
|
||||
(p/let [disk-content (encrypt/decrypt disk-content)]
|
||||
(state/pub-event! [:file/not-matched-from-disk path disk-content content]))
|
||||
|
||||
:else
|
||||
(->
|
||||
(p/let [result (ipc/ipc "writeFile" repo path content)
|
||||
mtime (gobj/get result "mtime")]
|
||||
(db/set-file-last-modified-at! repo path mtime)
|
||||
(db/set-file-content! repo path content)
|
||||
(p/let [content (if (encrypt/encrypted-db? (state/get-current-repo))
|
||||
(encrypt/decrypt content)
|
||||
content)]
|
||||
(db/set-file-content! repo path content))
|
||||
(when ok-handler
|
||||
(ok-handler repo path result))
|
||||
result)
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
[lambdaisland.glogi :as log]
|
||||
[electron.ipc :as ipc]
|
||||
[promesa.core :as p]
|
||||
[frontend.state :as state]))
|
||||
[frontend.state :as state]
|
||||
[frontend.encrypt :as encrypt]))
|
||||
|
||||
;; all IPC paths must be normalized! (via gp-util/path-normalize)
|
||||
|
||||
@@ -47,7 +48,9 @@
|
||||
pages-metadata-path (config/get-pages-metadata-path)
|
||||
{:keys [mtime]} stat
|
||||
db-content (or (db/get-file repo path) "")]
|
||||
(when (or content (= type "unlink"))
|
||||
(when (and (or content (= type "unlink"))
|
||||
(not (encrypt/content-encrypted? content))
|
||||
(not (:encryption/graph-parsing? @state/state)))
|
||||
(cond
|
||||
(and (= "add" type)
|
||||
(not= (string/trim content) (string/trim db-content))
|
||||
|
||||
@@ -80,6 +80,14 @@
|
||||
(state/set-config! repo-url config)
|
||||
config)))
|
||||
|
||||
(defn read-metadata!
|
||||
[content]
|
||||
(try
|
||||
(reader/read-string content)
|
||||
(catch :default e
|
||||
(log/error :parse/metadata-failed e)
|
||||
{})))
|
||||
|
||||
(defn get-page-default-properties
|
||||
[page-name]
|
||||
{:title page-name
|
||||
|
||||
@@ -38,7 +38,9 @@
|
||||
[frontend.fs :as fs]
|
||||
[clojure.string :as string]
|
||||
[frontend.util.persist-var :as persist-var]
|
||||
[frontend.fs.sync :as sync]))
|
||||
[frontend.fs.sync :as sync]
|
||||
[frontend.components.encryption :as encryption]
|
||||
[frontend.encrypt :as encrypt]))
|
||||
|
||||
;; TODO: should we move all events here?
|
||||
|
||||
@@ -233,7 +235,8 @@
|
||||
{:label "diff__cp"}))))
|
||||
|
||||
(defmethod handle :modal/display-file-version [[_ path content hash]]
|
||||
(state/set-modal! #(git-component/file-specific-version path hash content)))
|
||||
(p/let [content (when content (encrypt/decrypt content))]
|
||||
(state/set-modal! #(git-component/file-specific-version path hash content))))
|
||||
|
||||
(defmethod handle :graph/ready [[_ repo]]
|
||||
(search-handler/rebuild-indices-when-stale! repo)
|
||||
@@ -352,6 +355,18 @@
|
||||
(defmethod handle :rebuild-slash-commands-list [[_]]
|
||||
(page-handler/rebuild-slash-commands-list!))
|
||||
|
||||
;; encryption
|
||||
(defmethod handle :modal/encryption-setup-dialog [[_ repo-url close-fn]]
|
||||
(state/set-modal!
|
||||
(encryption/encryption-setup-dialog repo-url close-fn)))
|
||||
|
||||
(defmethod handle :modal/encryption-input-secret-dialog [[_ repo-url db-encrypted-secret close-fn]]
|
||||
(state/set-modal!
|
||||
(encryption/encryption-input-secret-dialog
|
||||
repo-url
|
||||
db-encrypted-secret
|
||||
close-fn)))
|
||||
|
||||
(defn run!
|
||||
[]
|
||||
(let [chan (state/get-events-chan)]
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
[frontend.handler.ui :as ui-handler]
|
||||
[frontend.state :as state]
|
||||
[frontend.util :as util]
|
||||
[logseq.graph-parser.util :as gp-util]
|
||||
[logseq.graph-parser.util :as gp-util]
|
||||
[lambdaisland.glogi :as log]
|
||||
[promesa.core :as p]
|
||||
[frontend.mobile.util :as mobile]
|
||||
@@ -272,6 +272,17 @@
|
||||
(when-let [dir (config/get-repo-dir repo)]
|
||||
(fs/watch-dir! dir))))
|
||||
|
||||
(defn create-metadata-file
|
||||
[repo-url encrypted?]
|
||||
(let [repo-dir (config/get-repo-dir repo-url)
|
||||
path (str config/app-name "/" config/metadata-file)
|
||||
file-path (str "/" path)
|
||||
default-content (if encrypted? "{:db/encrypted? true}" "{}")]
|
||||
(p/let [_ (fs/mkdir-if-not-exists (str repo-dir "/" config/app-name))
|
||||
file-exists? (fs/create-if-not-exists repo-url repo-dir file-path default-content)]
|
||||
(when-not file-exists?
|
||||
(reset-file! repo-url path default-content)))))
|
||||
|
||||
(defn create-pages-metadata-file
|
||||
[repo-url]
|
||||
(let [repo-dir (config/get-repo-dir repo-url)
|
||||
|
||||
@@ -1,12 +1,44 @@
|
||||
(ns frontend.handler.metadata
|
||||
(:require [cljs.pprint]
|
||||
(:require [cljs.reader :as reader]
|
||||
[cljs.pprint]
|
||||
[clojure.string :as string]
|
||||
[datascript.db :as ddb]
|
||||
[frontend.config :as config]
|
||||
[frontend.db :as db]
|
||||
[frontend.fs :as fs]
|
||||
[frontend.handler.common :as common-handler]
|
||||
[frontend.handler.file :as file-handler]
|
||||
[frontend.state :as state]
|
||||
[promesa.core :as p]))
|
||||
|
||||
(def default-metadata-str "{}")
|
||||
|
||||
(defn set-metadata!
|
||||
[k v]
|
||||
(when-let [repo (state/get-current-repo)]
|
||||
(let [encrypted? (= k :db/encrypted-secret)
|
||||
path (config/get-metadata-path)
|
||||
file-content (db/get-file path)]
|
||||
(p/let [_ (file-handler/create-metadata-file repo false)]
|
||||
(let [metadata-str (or file-content default-metadata-str)
|
||||
metadata (try
|
||||
(reader/read-string metadata-str)
|
||||
(catch js/Error e
|
||||
(println "Parsing metadata.edn failed: ")
|
||||
(js/console.dir e)
|
||||
{}))
|
||||
new-metadata (cond
|
||||
(= k :block/properties)
|
||||
(update metadata :block/properties v) ; v should be a function
|
||||
:else
|
||||
(let [ks (if (vector? k) k [k])]
|
||||
(assoc-in metadata ks v)))
|
||||
new-metadata (if encrypted?
|
||||
(assoc new-metadata :db/encrypted? true)
|
||||
new-metadata)
|
||||
new-content (pr-str new-metadata)]
|
||||
(file-handler/set-file-content! repo path new-content))))))
|
||||
|
||||
(defn set-pages-metadata!
|
||||
[repo]
|
||||
(let [path (config/get-pages-metadata-path repo)
|
||||
@@ -23,3 +55,31 @@
|
||||
path
|
||||
new-content
|
||||
{})))))
|
||||
|
||||
(defn set-db-encrypted-secret!
|
||||
[encrypted-secret]
|
||||
(when-not (string/blank? encrypted-secret)
|
||||
(set-metadata! :db/encrypted-secret encrypted-secret)))
|
||||
|
||||
(defn- handler-properties!
|
||||
[all-properties properties-tx]
|
||||
(reduce
|
||||
(fn [acc datom]
|
||||
(let [v (:v datom)
|
||||
id (or (get v :id)
|
||||
(get v :title))]
|
||||
(if id
|
||||
(let [added? (ddb/datom-added datom)
|
||||
remove-all-properties? (and (not added?)
|
||||
;; only id
|
||||
(= 1 (count v)))]
|
||||
(if remove-all-properties?
|
||||
(dissoc acc id)
|
||||
(assoc acc id v)))
|
||||
acc)))
|
||||
all-properties
|
||||
properties-tx))
|
||||
|
||||
(defn update-properties!
|
||||
[properties-tx]
|
||||
(set-metadata! :block/properties #(handler-properties! % properties-tx)))
|
||||
|
||||
@@ -25,7 +25,8 @@
|
||||
[logseq.graph-parser.util :as gp-util]
|
||||
[electron.ipc :as ipc]
|
||||
[clojure.set :as set]
|
||||
[clojure.core.async :as async]))
|
||||
[clojure.core.async :as async]
|
||||
[frontend.encrypt :as encrypt]))
|
||||
|
||||
;; Project settings should be checked in two situations:
|
||||
;; 1. User changes the config.edn directly in logseq.com (fn: alter-file)
|
||||
@@ -128,16 +129,19 @@
|
||||
(ui-handler/re-render-root!)))))))
|
||||
|
||||
(defn create-default-files!
|
||||
[repo-url]
|
||||
(spec/validate :repos/url repo-url)
|
||||
(let [repo-dir (config/get-repo-dir repo-url)]
|
||||
(p/let [_ (fs/mkdir-if-not-exists (str repo-dir "/" config/app-name))
|
||||
_ (fs/mkdir-if-not-exists (str repo-dir "/" config/app-name "/" config/recycle-dir))
|
||||
_ (fs/mkdir-if-not-exists (str repo-dir "/" (config/get-journals-directory)))
|
||||
_ (create-config-file-if-not-exists repo-url)
|
||||
_ (create-contents-file repo-url)
|
||||
_ (create-custom-theme repo-url)]
|
||||
(state/pub-event! [:page/create-today-journal repo-url]))))
|
||||
([repo-url]
|
||||
(create-default-files! repo-url false))
|
||||
([repo-url encrypted?]
|
||||
(spec/validate :repos/url repo-url)
|
||||
(let [repo-dir (config/get-repo-dir repo-url)]
|
||||
(p/let [_ (fs/mkdir-if-not-exists (str repo-dir "/" config/app-name))
|
||||
_ (fs/mkdir-if-not-exists (str repo-dir "/" config/app-name "/" config/recycle-dir))
|
||||
_ (fs/mkdir-if-not-exists (str repo-dir "/" (config/get-journals-directory)))
|
||||
_ (file-handler/create-metadata-file repo-url encrypted?)
|
||||
_ (create-config-file-if-not-exists repo-url)
|
||||
_ (create-contents-file repo-url)
|
||||
_ (create-custom-theme repo-url)]
|
||||
(state/pub-event! [:page/create-today-journal repo-url])))))
|
||||
|
||||
(defn- load-pages-metadata!
|
||||
"force?: if set true, skip the metadata timestamp range check"
|
||||
@@ -197,18 +201,21 @@
|
||||
(update m :finished inc))))
|
||||
|
||||
(defn- after-parse
|
||||
[repo-url files file-paths re-render? re-render-opts opts graph-added-chan]
|
||||
[repo-url files file-paths db-encrypted? re-render? re-render-opts opts graph-added-chan]
|
||||
(load-pages-metadata! repo-url file-paths files true)
|
||||
(when (:new-graph? opts)
|
||||
(create-default-files! repo-url))
|
||||
(if (and (not db-encrypted?) (state/enable-encryption? repo-url))
|
||||
(state/pub-event! [:modal/encryption-setup-dialog repo-url
|
||||
#(create-default-files! repo-url %)])
|
||||
(create-default-files! repo-url db-encrypted?)))
|
||||
(when re-render?
|
||||
(ui-handler/re-render-root! re-render-opts))
|
||||
(state/pub-event! [:graph/added repo-url opts])
|
||||
(state/reset-parsing-state!)
|
||||
(async/offer! graph-added-chan true))
|
||||
|
||||
(defn- parse-files-and-create-default-files!
|
||||
[repo-url files delete-files delete-blocks file-paths re-render? re-render-opts opts]
|
||||
(defn- parse-files-and-create-default-files-inner!
|
||||
[repo-url files delete-files delete-blocks file-paths db-encrypted? re-render? re-render-opts opts]
|
||||
(let [support-files (filter
|
||||
(fn [file]
|
||||
(let [format (format/get-format (:file/path file))]
|
||||
@@ -234,16 +241,27 @@
|
||||
(do
|
||||
(doseq [file support-files']
|
||||
(parse-and-load-file! repo-url file new-graph?))
|
||||
(after-parse repo-url files file-paths re-render? re-render-opts opts graph-added-chan))
|
||||
(after-parse repo-url files file-paths db-encrypted? re-render? re-render-opts opts graph-added-chan))
|
||||
(async/go-loop []
|
||||
(if-let [file (async/<! chan)]
|
||||
(do
|
||||
(parse-and-load-file! repo-url file new-graph?)
|
||||
(async/<! (async/timeout 10))
|
||||
(recur))
|
||||
(after-parse repo-url files file-paths re-render? re-render-opts opts graph-added-chan))))
|
||||
(after-parse repo-url files file-paths db-encrypted? re-render? re-render-opts opts graph-added-chan))))
|
||||
graph-added-chan))
|
||||
|
||||
(defn- parse-files-and-create-default-files!
|
||||
[repo-url files delete-files delete-blocks file-paths db-encrypted? re-render? re-render-opts opts]
|
||||
(if db-encrypted?
|
||||
(p/let [files (p/all
|
||||
(map (fn [file]
|
||||
(p/let [content (encrypt/decrypt (:file/content file))]
|
||||
(assoc file :file/content content)))
|
||||
files))]
|
||||
(parse-files-and-create-default-files-inner! repo-url files delete-files delete-blocks file-paths db-encrypted? re-render? re-render-opts opts))
|
||||
(parse-files-and-create-default-files-inner! repo-url files delete-files delete-blocks file-paths db-encrypted? re-render? re-render-opts opts)))
|
||||
|
||||
(defn- update-parsing-state!
|
||||
[repo-url]
|
||||
(state/set-loading-files! repo-url false))
|
||||
@@ -253,8 +271,21 @@
|
||||
:or {re-render? true}}]
|
||||
(update-parsing-state! repo-url)
|
||||
|
||||
(let [file-paths (map :file/path files)]
|
||||
(parse-files-and-create-default-files! repo-url files delete-files delete-blocks file-paths re-render? re-render-opts opts)))
|
||||
(let [file-paths (map :file/path files)
|
||||
metadata-file (config/get-metadata-path)
|
||||
metadata-content (some #(when (= (:file/path %) metadata-file)
|
||||
(:file/content %)) files)
|
||||
metadata (when metadata-content
|
||||
(common-handler/read-metadata! metadata-content))
|
||||
db-encrypted? (:db/encrypted? metadata)
|
||||
db-encrypted-secret (if db-encrypted? (:db/encrypted-secret metadata) nil)]
|
||||
(if db-encrypted?
|
||||
(let [close-fn #(parse-files-and-create-default-files! repo-url files delete-files delete-blocks file-paths db-encrypted? re-render? re-render-opts opts)]
|
||||
(state/set-state! :encryption/graph-parsing? true)
|
||||
(state/pub-event! [:modal/encryption-input-secret-dialog repo-url
|
||||
db-encrypted-secret
|
||||
close-fn]))
|
||||
(parse-files-and-create-default-files! repo-url files delete-files delete-blocks file-paths db-encrypted? re-render? re-render-opts opts))))
|
||||
|
||||
(defn load-repo-to-db!
|
||||
[repo-url {:keys [diffs nfs-files refresh? new-graph? empty-graph?]}]
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
[frontend.mobile.util :as mobile-util]
|
||||
[logseq.graph-parser.util :as gp-util]
|
||||
[logseq.graph-parser.config :as gp-config]
|
||||
[clojure.core.async :as async]))
|
||||
[clojure.core.async :as async]
|
||||
[frontend.encrypt :as encrypt]))
|
||||
|
||||
(defn remove-ignore-files
|
||||
[files]
|
||||
@@ -168,7 +169,8 @@
|
||||
(-> (p/all (map (fn [file]
|
||||
(p/let [content (if nfs?
|
||||
(.text (:file/file file))
|
||||
(:file/content file))]
|
||||
(:file/content file))
|
||||
content (encrypt/decrypt content)]
|
||||
(assoc file :file/content content))) markup-files))
|
||||
(p/then (fn [result]
|
||||
(let [files (map #(dissoc % :file/file) result)]
|
||||
@@ -247,7 +249,8 @@
|
||||
(when-let [file (get-file-f path new-files)]
|
||||
(p/let [content (if nfs?
|
||||
(.text (:file/file file))
|
||||
(:file/content file))]
|
||||
(:file/content file))
|
||||
content (encrypt/decrypt content)]
|
||||
(assoc file :file/content content)))) added-or-modified))
|
||||
(p/then (fn [result]
|
||||
(let [files (map #(dissoc % :file/file :file/handle) result)
|
||||
|
||||
@@ -216,6 +216,8 @@
|
||||
:file-sync/sync-state nil
|
||||
:file-sync/sync-uploading-files nil
|
||||
:file-sync/sync-downloading-files nil
|
||||
|
||||
:encryption/graph-parsing? false
|
||||
})))
|
||||
|
||||
;; block uuid -> {content(String) -> ast}
|
||||
@@ -1674,3 +1676,8 @@
|
||||
(update-state! [:graph/parsing-state (get-current-repo)]
|
||||
(if (fn? m) m
|
||||
(fn [old-value] (merge old-value m)))))
|
||||
|
||||
(defn enable-encryption?
|
||||
[repo]
|
||||
(:feature/enable-encryption?
|
||||
(get (sub-config) repo)))
|
||||
|
||||
Reference in New Issue
Block a user