Add permission checking to file snapshot rpc methods

This commit is contained in:
Andrey Antukh 2024-10-29 15:39:55 +01:00 committed by Alonso Torres
parent b4f868be91
commit 5f4af76d28
9 changed files with 308 additions and 202 deletions

View file

@ -1745,8 +1745,8 @@
(fn [system] (fn [system]
(binding [*system* system] (binding [*system* system]
(when (string? label) (when (string? label)
(fsnap/take-file-snapshot! system {:file-id file-id (fsnap/create-file-snapshot! system nil file-id (str "migration/" label)))
:label (str "migration/" label)}))
(let [file (get-file system file-id) (let [file (get-file system file-id)
file (process-file! system file :validate? validate?)] file (process-file! system file :validate? validate?)]

View file

@ -29,17 +29,13 @@
"SELECT id, label, revn, created_at, created_by, profile_id "SELECT id, label, revn, created_at, created_by, profile_id
FROM file_change FROM file_change
WHERE file_id = ? WHERE file_id = ?
AND created_at < ?
AND data IS NOT NULL AND data IS NOT NULL
ORDER BY created_at DESC ORDER BY created_at DESC
LIMIT ?") LIMIT 20")
(defn get-file-snapshots (defn get-file-snapshots
[{:keys [::db/conn]} {:keys [file-id limit start-at] [conn file-id]
:or {limit Long/MAX_VALUE}}] (db/exec! conn [sql:get-file-snapshots file-id]))
(let [start-at (or start-at (dt/now))
limit (min limit 20)]
(db/exec! conn [sql:get-file-snapshots file-id start-at limit])))
(def ^:private schema:get-file-snapshots (def ^:private schema:get-file-snapshots
[:map {:title "get-file-snapshots"} [:map {:title "get-file-snapshots"}
@ -48,29 +44,121 @@
(sv/defmethod ::get-file-snapshots (sv/defmethod ::get-file-snapshots
{::doc/added "1.20" {::doc/added "1.20"
::sm/params schema:get-file-snapshots} ::sm/params schema:get-file-snapshots}
[cfg params] [cfg {:keys [::rpc/profile-id file-id] :as params}]
(db/run! cfg get-file-snapshots params)) (db/run! cfg (fn [{:keys [::db/conn]}]
(files/check-read-permissions! conn profile-id file-id)
(get-file-snapshots conn file-id))))
(defn- get-file
[cfg file-id]
(let [file (->> (db/get cfg :file {:id file-id})
(feat.fdata/resolve-file-data cfg))]
(binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg file-id)]
(-> file
(update :data blob/decode)
(update :data feat.fdata/process-pointers deref)
(update :data feat.fdata/process-objects (partial into {}))
(update :data blob/encode)))))
(defn- generate-snapshot-label
[]
(let [ts (-> (dt/now)
(dt/format-instant)
(str/replace #"[T:\.]" "-")
(str/rtrim "Z"))]
(str "snapshot-" ts)))
(defn create-file-snapshot!
[cfg profile-id file-id label]
(let [file (-> (get-file cfg file-id)
(update :data
(fn [data]
(-> data
(blob/decode)
(assoc :id file-id)))))
;; NOTE: final user never can provide label as `:system`
;; keyword because the validator implies label always as
;; string; keyword is used for signal a special case
created-by
(if (= label :system)
"system"
"user")
label
(if (= label :system)
(str "internal/snapshot/" (:revn file))
(or label (generate-snapshot-label)))
snapshot-id
(uuid/next)
snapshot-data
(-> (:data file)
(feat.fdata/process-pointers deref)
(feat.fdata/process-objects (partial into {}))
(blob/encode))]
(l/debug :hint "creating file snapshot"
:file-id (str file-id)
:id (str snapshot-id)
:label label)
(db/insert! cfg :file-change
{:id snapshot-id
:revn (:revn file)
:data snapshot-data
:version (:version file)
:features (:features file)
:profile-id profile-id
:file-id (:id file)
:label label
:created-by created-by}
{::db/return-keys false})
{:id snapshot-id :label label}))
(def ^:private schema:create-file-snapshot
[:map
[:file-id ::sm/uuid]
[:label {:optional true} :string]])
(sv/defmethod ::create-file-snapshot
{::doc/added "1.20"
::sm/params schema:create-file-snapshot}
[cfg {:keys [::rpc/profile-id file-id label]}]
(db/tx-run! cfg
(fn [{:keys [::db/conn] :as cfg}]
(files/check-edition-permissions! conn profile-id file-id)
(create-file-snapshot! cfg profile-id file-id label))))
(defn restore-file-snapshot! (defn restore-file-snapshot!
[{:keys [::db/conn ::mbus/msgbus] :as cfg} {:keys [file-id id]}] [{:keys [::db/conn ::mbus/msgbus] :as cfg} file-id snapshot-id]
(let [storage (sto/resolve cfg {::db/reuse-conn true}) (let [storage (sto/resolve cfg {::db/reuse-conn true})
file (files/get-minimal-file conn file-id {::db/for-update true}) file (files/get-minimal-file conn file-id {::db/for-update true})
vern (rand-int Integer/MAX_VALUE) vern (rand-int Integer/MAX_VALUE)
snapshot (db/get* conn :file-change snapshot (db/get* conn :file-change
{:file-id file-id {:file-id file-id
:id id} :id snapshot-id}
{::db/for-share true})] {::db/for-share true})]
(when-not snapshot (when-not snapshot
(ex/raise :type :not-found (ex/raise :type :not-found
:code :snapshot-not-found :code :snapshot-not-found
:hint "unable to find snapshot with the provided label" :hint "unable to find snapshot with the provided label"
:id id :snapshot-id snapshot-id
:file-id file-id)) :file-id file-id))
;; (when (= (:revn snapshot) (:revn file))
;; (ex/raise :type :validation
;; :code :snapshot-identical-to-file
;; :hint "you can't restore a snapshot that is identical to a file"
;; :snapshot-id snapshot-id
;; :file-id file-id))
(let [snapshot (feat.fdata/resolve-file-data cfg snapshot)] (let [snapshot (feat.fdata/resolve-file-data cfg snapshot)]
(when-not (:data snapshot) (when-not (:data snapshot)
(ex/raise :type :precondition (ex/raise :type :validation
:code :snapshot-without-data :code :snapshot-without-data
:hint "snapshot has no data" :hint "snapshot has no data"
:label (:label snapshot) :label (:label snapshot)
@ -123,125 +211,77 @@
{:id (:id snapshot) {:id (:id snapshot)
:label (:label snapshot)}))) :label (:label snapshot)})))
(def ^:private (def ^:private schema:restore-file-snapshot
schema:restore-file-snapshot [:map {:title "restore-file-snapshot"}
[:and [:file-id ::sm/uuid]
[:map [:id ::sm/uuid]])
[:file-id ::sm/uuid]
[:id {:optional true} ::sm/uuid]]
[::sm/contains-any #{:id :label}]])
(sv/defmethod ::restore-file-snapshot (sv/defmethod ::restore-file-snapshot
{::doc/added "1.20" {::doc/added "1.20"
::sm/params schema:restore-file-snapshot} ::sm/params schema:restore-file-snapshot}
[cfg params] [cfg {:keys [::rpc/profile-id file-id id] :as params}]
(db/tx-run! cfg restore-file-snapshot! params)) (db/tx-run! cfg
(fn [{:keys [::db/conn] :as cfg}]
(defn- get-file (files/check-edition-permissions! conn profile-id file-id)
[cfg file-id] (create-file-snapshot! cfg profile-id file-id :system)
(let [file (->> (db/get cfg :file {:id file-id}) (restore-file-snapshot! cfg file-id id))))
(feat.fdata/resolve-file-data cfg))]
(binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg file-id)]
(-> file
(update :data blob/decode)
(update :data feat.fdata/process-pointers deref)
(update :data feat.fdata/process-objects (partial into {}))
(update :data blob/encode)))))
(defn- generate-snapshot-label
[]
(let [ts (-> (dt/now)
(dt/format-instant)
(str/replace #"[T:\.]" "-")
(str/rtrim "Z"))]
(str "snapshot-" ts)))
(defn take-file-snapshot!
[cfg {:keys [file-id label ::rpc/profile-id]}]
(let [label (or label (generate-snapshot-label))
file (-> (get-file cfg file-id)
(update :data
(fn [data]
(-> data
(blob/decode)
(assoc :id file-id)))))
snapshot-id
(uuid/next)
snapshot-data
(-> (:data file)
(feat.fdata/process-pointers deref)
(feat.fdata/process-objects (partial into {}))
(blob/encode))]
(l/debug :hint "creating file snapshot"
:file-id (str file-id)
:id (str snapshot-id)
:label label)
(db/insert! cfg :file-change
{:id snapshot-id
:revn (:revn file)
:data snapshot-data
:version (:version file)
:features (:features file)
:profile-id profile-id
:file-id (:id file)
:label label
:created-by "user"}
{::db/return-keys false})
{:id snapshot-id :label label}))
(def ^:private schema:take-file-snapshot
[:map
[:file-id ::sm/uuid]
[:label {:optional true} :string]])
(sv/defmethod ::take-file-snapshot
{::doc/added "1.20"
::sm/params schema:take-file-snapshot}
[cfg params]
(db/tx-run! cfg take-file-snapshot! params))
(def ^:private schema:update-file-snapshot (def ^:private schema:update-file-snapshot
[:map {:title "update-file-snapshot"} [:map {:title "update-file-snapshot"}
[:id ::sm/uuid] [:id ::sm/uuid]
[:label ::sm/text]]) [:label ::sm/text]])
(defn update-file-snapshot! (defn- update-file-snapshot!
[{:keys [::db/conn] :as cfg} {:keys [id label]}] [conn snapshot-id label]
(let [result (-> (db/update! conn :file-change
(db/update! conn :file-change {:label label
{:label label :created-by "user"}
:created-by "user"} {:id snapshot-id}
{:id id} {::db/return-keys true})
{::db/return-keys true})] (dissoc :data :features)))
(select-keys result [:id :label :revn :created-at :profile-id :created-by]))) (defn- get-snapshot
"Get a minimal snapshot from database and lock for update"
[conn id]
(db/get conn :file-change
{:id id}
{::sql/columns [:id :file-id :created-by]
::db/for-update true}))
(sv/defmethod ::update-file-snapshot (sv/defmethod ::update-file-snapshot
{::doc/added "1.20" {::doc/added "1.20"
::sm/params schema:update-file-snapshot} ::sm/params schema:update-file-snapshot}
[cfg params] [cfg {:keys [::rpc/profile-id id label]}]
(db/tx-run! cfg update-file-snapshot! params)) (db/tx-run! cfg
(fn [{:keys [::db/conn]}]
(let [snapshot (get-snapshot conn id)]
(files/check-edition-permissions! conn profile-id (:file-id snapshot))
(update-file-snapshot! conn id label)))))
(def ^:private schema:remove-file-snapshot (def ^:private schema:remove-file-snapshot
[:map {:title "remove-file-snapshot"} [:map {:title "remove-file-snapshot"}
[:id ::sm/uuid]]) [:id ::sm/uuid]])
(defn remove-file-snapshot! (defn- delete-file-snapshot!
[{:keys [::db/conn] :as cfg} {:keys [id]}] [conn snapshot-id]
(db/delete! conn :file-change (db/delete! conn :file-change
{:id id :created-by "user"} {:id snapshot-id}
{::db/return-keys false}) {::db/return-keys false})
nil) nil)
(sv/defmethod ::remove-file-snapshot (sv/defmethod ::delete-file-snapshot
{::doc/added "1.20" {::doc/added "1.20"
::sm/params schema:remove-file-snapshot} ::sm/params schema:remove-file-snapshot}
[cfg params] [cfg {:keys [::rpc/profile-id id]}]
(db/tx-run! cfg remove-file-snapshot! params)) (db/tx-run! cfg
(fn [{:keys [::db/conn]}]
(let [snapshot (get-snapshot conn id)]
(files/check-edition-permissions! conn profile-id (:file-id snapshot))
(when (not= (:created-by snapshot) "user")
(ex/raise :type :validation
:code :system-snapshots-cant-be-deleted
:snapshot-id id
:profile-id profile-id))
(delete-file-snapshot! conn id)))))

View file

@ -122,22 +122,19 @@
WHERE file_id = ANY(?) WHERE file_id = ANY(?)
AND id IS NOT NULL") AND id IS NOT NULL")
(defn get-file-snapshots (defn search-file-snapshots
"Get a seq parirs of file-id and snapshot-id for a set of files "Get a seq parirs of file-id and snapshot-id for a set of files
and specified label" and specified label"
[conn label ids] [conn file-ids label]
(db/exec! conn [sql:snapshots-with-file label (db/exec! conn [sql:snapshots-with-file label
(db/create-array conn "uuid" ids)])) (db/create-array conn "uuid" file-ids)]))
(defn take-team-snapshot! (defn take-team-snapshot!
[system team-id label] [system team-id label]
(let [conn (db/get-connection system)] (let [conn (db/get-connection system)]
(->> (feat.comp-v2/get-and-lock-team-files conn team-id) (->> (feat.comp-v2/get-and-lock-team-files conn team-id)
(map (fn [file-id] (reduce (fn [result file-id]
{:file-id file-id (fsnap/create-file-snapshot! system nil file-id label)
:label label}))
(reduce (fn [result params]
(fsnap/take-file-snapshot! conn params)
(inc result)) (inc result))
0)))) 0))))
@ -147,7 +144,7 @@
ids (->> (feat.comp-v2/get-and-lock-team-files conn team-id) ids (->> (feat.comp-v2/get-and-lock-team-files conn team-id)
(into #{})) (into #{}))
snap (get-file-snapshots conn label ids) snap (search-file-snapshots conn ids label)
ids' (into #{} (map :file-id) snap) ids' (into #{} (map :file-id) snap)
team (-> (feat.comp-v2/get-team conn team-id) team (-> (feat.comp-v2/get-team conn team-id)
@ -157,8 +154,8 @@
(throw (RuntimeException. "no uniform snapshot available"))) (throw (RuntimeException. "no uniform snapshot available")))
(feat.comp-v2/update-team! conn team) (feat.comp-v2/update-team! conn team)
(reduce (fn [result params] (reduce (fn [result {:keys [file-id id]}]
(fsnap/restore-file-snapshot! conn params) (fsnap/restore-file-snapshot! system file-id id)
(inc result)) (inc result))
0 0
snap))) snap)))
@ -167,7 +164,7 @@
[system file-id update-fn & {:keys [label validate? with-libraries?] :or {validate? true} :as opts}] [system file-id update-fn & {:keys [label validate? with-libraries?] :or {validate? true} :as opts}]
(when (string? label) (when (string? label)
(fsnap/take-file-snapshot! system {:file-id file-id :label label})) (fsnap/create-file-snapshot! system nil file-id label))
(let [conn (db/get-connection system) (let [conn (db/get-connection system)
file (get-file system file-id opts) file (get-file system file-id opts)

View file

@ -311,28 +311,25 @@
collectable file-changes entry." collectable file-changes entry."
[& {:keys [file-id label]}] [& {:keys [file-id label]}]
(let [file-id (h/parse-uuid file-id)] (let [file-id (h/parse-uuid file-id)]
(db/tx-run! main/system fsnap/take-file-snapshot! {:file-id file-id :label label}))) (db/tx-run! main/system fsnap/create-file-snapshot! {:file-id file-id :label label})))
(defn restore-file-snapshot! (defn restore-file-snapshot!
[file-id label] [file-id label]
(let [file-id (h/parse-uuid file-id)] (let [file-id (h/parse-uuid file-id)]
(db/tx-run! main/system (db/tx-run! main/system
(fn [{:keys [::db/conn] :as system}] (fn [{:keys [::db/conn] :as system}]
(when-let [snapshot (->> (h/get-file-snapshots conn label #{file-id}) (when-let [snapshot (->> (h/search-file-snapshots conn #{file-id} label)
(map :id) (map :id)
(first))] (first))]
(fsnap/restore-file-snapshot! system (fsnap/restore-file-snapshot! system file-id (:id snapshot)))))))
{:id (:id snapshot)
:file-id file-id}))))))
(defn list-file-snapshots! (defn list-file-snapshots!
[file-id & {:keys [limit]}] [file-id & {:as _}]
(let [file-id (h/parse-uuid file-id)] (let [file-id (h/parse-uuid file-id)]
(db/tx-run! main/system (db/tx-run! main/system
(fn [system] (fn [{:keys [::db/conn]}]
(let [params {:file-id file-id :limit limit}] (->> (fsnap/get-file-snapshots conn file-id)
(->> (fsnap/get-file-snapshots system (d/without-nils params)) (print-table [:label :id :revn :created-at]))))))
(print-table [:label :id :revn :created-at])))))))
(defn take-team-snapshot! (defn take-team-snapshot!
[team-id & {:keys [label rollback?] :or {rollback? true}}] [team-id & {:keys [label rollback?] :or {rollback? true}}]

View file

@ -0,0 +1,134 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns backend-tests.rpc-file-snapshot-test
(:require
[app.common.features :as cfeat]
[app.common.pprint :as pp]
[app.common.pprint :as pp]
[app.common.thumbnails :as thc]
[app.common.types.shape :as cts]
[app.common.uuid :as uuid]
[app.db :as db]
[app.db.sql :as sql]
[app.http :as http]
[app.rpc :as-alias rpc]
[app.storage :as sto]
[app.util.time :as dt]
[backend-tests.helpers :as th]
[clojure.test :as t]
[cuerdas.core :as str]))
(t/use-fixtures :once th/state-init)
(t/use-fixtures :each th/database-reset)
(defn- update-file!
[& {:keys [profile-id file-id changes revn] :or {revn 0}}]
(let [params {::th/type :update-file
::rpc/profile-id profile-id
:id file-id
:session-id (uuid/random)
:revn revn
:vern 0
:features cfeat/supported-features
:changes changes}
out (th/command! params)]
;; (th/print-result! out)
(t/is (nil? (:error out)))
(:result out)))
(t/deftest generic-ops
(let [profile (th/create-profile* 1 {:is-active true})
team-id (:default-team-id profile)
proj-id (:default-project-id profile)
file (th/create-file* 1 {:profile-id (:id profile)
:project-id proj-id
:is-shared false})
snapshot-id (volatile! nil)]
(t/testing "create snapshot"
(let [params {::th/type :create-file-snapshot
::rpc/profile-id (:id profile)
:file-id (:id file)
:label "label1"}
out (th/command! params)]
;; (th/print-result! out)
(t/is (nil? (:error out)))
(let [result (:result out)]
(t/is (= "label1" (:label result)))
(t/is (uuid? (:id result)))
(vswap! snapshot-id (constantly (:id result))))))
(t/testing "list snapshots"
(let [params {::th/type :get-file-snapshots
::rpc/profile-id (:id profile)
:file-id (:id file)}
out (th/command! params)]
;; (th/print-result! out)
(t/is (nil? (:error out)))
(let [[row :as result] (:result out)]
(t/is (= 1 (count result)))
(t/is (= "label1" (:label row)))
(t/is (uuid? (:id row)))
(t/is (= @snapshot-id (:id row)))
(t/is (= 0 (:revn row)))
(t/is (= (:id profile) (:profile-id row))))))
(t/testing "restore snapshot"
(let [params {::th/type :restore-file-snapshot
::rpc/profile-id (:id profile)
:file-id (:id file)
:id @snapshot-id}
out (th/command! params)]
;; (th/print-result! out)
(t/is (nil? (:error out)))
(let [result (:result out)]
(t/is (= "label1" (:label result)))
(t/is (uuid? (:id result)))))
(let [[row1 row2 :as rows]
(th/db-query :file-change
{:file-id (:id file)}
{:order-by [:created-at]})]
(t/is (= 2 (count rows)))
(t/is (= "user" (:created-by row1)))
(t/is (= "system" (:created-by row2)))))
(t/testing "delete snapshot"
(let [[row1 row2 :as rows]
(th/db-query :file-change
{:file-id (:id file)}
{:order-by [:created-at]})]
(t/testing "delete user created snapshot"
(let [params {::th/type :delete-file-snapshot
::rpc/profile-id (:id profile)
:file-id (:id file)
:id (:id row1)}
out (th/command! params)]
;; (th/print-result! out)
(t/is (nil? (:error out)))
(t/is (nil? (:result out)))))
(t/testing "delete system created snapshot"
(let [params {::th/type :delete-file-snapshot
::rpc/profile-id (:id profile)
:file-id (:id file)
:id (:id row2)}
out (th/command! params)]
;; (th/print-result! out)
(let [error (:error out)
data (ex-data error)]
(t/is (th/ex-info? error))
(t/is (= (:type data) :validation))
(t/is (= (:code data) :system-snapshots-cant-be-deleted)))))))))

View file

@ -33,7 +33,7 @@ test("Save and restore version", async ({ page }) => {
await page.getByLabel("History (Alt+H)").click(); await page.getByLabel("History (Alt+H)").click();
await workspacePage.mockRPC( await workspacePage.mockRPC(
"take-file-snapshot", "create-file-snapshot",
"workspace/versions-take-snapshot-1.json", "workspace/versions-take-snapshot-1.json",
); );
@ -57,7 +57,11 @@ test("Save and restore version", async ({ page }) => {
await page.getByRole("textbox").fill("INIT"); await page.getByRole("textbox").fill("INIT");
await page.getByRole("textbox").press("Enter"); await page.getByRole("textbox").press("Enter");
await page.getByTestId("version(INIT)").getByRole("button").click(); await page
.locator("li")
.filter({ hasText: "INIT" })
.getByRole("button")
.click();
await page.getByRole("button", { name: "Restore" }).click(); await page.getByRole("button", { name: "Restore" }).click();
await workspacePage.mockRPC( await workspacePage.mockRPC(

View file

@ -64,7 +64,7 @@
(->> (rx/from-atom refs/persistence-state {:emit-current-value? true}) (->> (rx/from-atom refs/persistence-state {:emit-current-value? true})
(rx/filter #(or (nil? %) (= :saved %))) (rx/filter #(or (nil? %) (= :saved %)))
(rx/take 1) (rx/take 1)
(rx/mapcat #(rp/cmd! :take-file-snapshot {:file-id file-id :label label})) (rx/mapcat #(rp/cmd! :create-file-snapshot {:file-id file-id :label label}))
(rx/mapcat (rx/mapcat
(fn [{:keys [id]}] (fn [{:keys [id]}]
(rx/of (rx/of
@ -101,7 +101,6 @@
(->> (rx/from-atom refs/persistence-state {:emit-current-value? true}) (->> (rx/from-atom refs/persistence-state {:emit-current-value? true})
(rx/filter #(or (nil? %) (= :saved %))) (rx/filter #(or (nil? %) (= :saved %)))
(rx/take 1) (rx/take 1)
(rx/mapcat #(rp/cmd! :take-file-snapshot {:file-id file-id :created-by "system" :label (dt/format (dt/now) :date-full)}))
(rx/mapcat #(rp/cmd! :restore-file-snapshot {:file-id file-id :id id})) (rx/mapcat #(rp/cmd! :restore-file-snapshot {:file-id file-id :id id}))
(rx/map #(dw/initialize-file project-id file-id))) (rx/map #(dw/initialize-file project-id file-id)))
(rx/of (ptk/event ::ev/event {::ev/name "restore-version"})))))) (rx/of (ptk/event ::ev/event {::ev/name "restore-version"}))))))
@ -114,7 +113,7 @@
(ptk/reify ::delete-version (ptk/reify ::delete-version
ptk/WatchEvent ptk/WatchEvent
(watch [_ _ _] (watch [_ _ _]
(->> (rp/cmd! :remove-file-snapshot {:id id}) (->> (rp/cmd! :delete-file-snapshot {:id id})
(rx/map #(fetch-versions file-id)))))) (rx/map #(fetch-versions file-id))))))
(defn pin-version (defn pin-version

View file

@ -109,8 +109,7 @@
(kbd/esc? event) (kbd/esc? event)
(st/emit! (dwv/update-version-state {:editing nil})))))] (st/emit! (dwv/update-version-state {:editing nil})))))]
[:li {:data-testid (dm/str "version(" (:label entry) ")") [:li {:class (stl/css :version-entry-wrap)}
:class (stl/css :version-entry-wrap)}
[:div {:class (stl/css :version-entry :is-snapshot)} [:div {:class (stl/css :version-entry :is-snapshot)}
[:img {:class (stl/css :version-entry-avatar) [:img {:class (stl/css :version-entry-avatar)
:alt (:fullname profile) :alt (:fullname profile)

View file

@ -12,12 +12,9 @@
[app.common.files.validate :as cfv] [app.common.files.validate :as cfv]
[app.common.json :as json] [app.common.json :as json]
[app.common.logging :as l] [app.common.logging :as l]
[app.common.schema :as sm]
[app.common.transit :as t] [app.common.transit :as t]
[app.common.types.file :as ctf] [app.common.types.file :as ctf]
[app.common.uri :as u]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.config :as cf]
[app.main.data.changes :as dwc] [app.main.data.changes :as dwc]
[app.main.data.dashboard.shortcuts] [app.main.data.dashboard.shortcuts]
[app.main.data.preview :as dp] [app.main.data.preview :as dp]
@ -33,7 +30,6 @@
[app.main.store :as st] [app.main.store :as st]
[app.util.debug :as dbg] [app.util.debug :as dbg]
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.http :as http]
[app.util.object :as obj] [app.util.object :as obj]
[app.util.timers :as timers] [app.util.timers :as timers]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
@ -454,66 +450,6 @@
[id shape-ref] [id shape-ref]
(st/emit! (dw/set-shape-ref id shape-ref))) (st/emit! (dw/set-shape-ref id shape-ref)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; SNAPSHOTS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn ^:export list-available-snapshots
[file-id]
(let [file-id (or (d/parse-uuid file-id)
(:current-file-id @st/state))]
(->> (http/send! {:method :get
:uri (u/join cf/public-uri "api/rpc/command/get-file-snapshots")
:query {:file-id file-id}})
(rx/map http/conditional-decode-transit)
(rx/mapcat rp/handle-response)
(rx/subs! (fn [result]
(let [result (map (fn [row]
(update row :id str))
result)]
(js/console.table (json/->js result))))
(fn [cause]
(js/console.log "EE:" cause))))
nil))
(defn ^:export take-snapshot
[label file-id]
(when-let [file-id (or (d/parse-uuid file-id)
(:current-file-id @st/state))]
(->> (http/send! {:method :post
:uri (u/join cf/public-uri "api/rpc/command/take-file-snapshot")
:body (http/transit-data {:file-id file-id :label label})})
(rx/map http/conditional-decode-transit)
(rx/mapcat rp/handle-response)
(rx/subs! (fn [{:keys [id]}]
(println "Snapshot saved:" (str id) label))
(fn [cause]
(js/console.log "EE:" cause))))))
(defn ^:export restore-snapshot
[label file-id]
(when-let [file-id (or (d/parse-uuid file-id)
(:current-file-id @st/state))]
(let [snapshot-id (sm/parse-uuid label)
label (if snapshot-id nil label)
params (cond-> {:file-id file-id}
(uuid? snapshot-id)
(assoc :id snapshot-id)
(string? label)
(assoc :label label))]
(->> (http/send! {:method :post
:uri (u/join cf/public-uri "api/rpc/command/restore-file-snapshot")
:body (http/transit-data params)})
(rx/map http/conditional-decode-transit)
(rx/mapcat rp/handle-response)
(rx/subs! (fn [_]
(println "Snapshot restored " (or snapshot-id label)))
#_(.reload js/location)
(fn [cause]
(js/console.log "EE:" cause)))))))
(defn ^:export enable-text-v2 (defn ^:export enable-text-v2
[] []
(st/emit! (features/enable-feature "text-editor/v2"))) (st/emit! (features/enable-feature "text-editor/v2")))