mirror of
https://github.com/penpot/penpot.git
synced 2025-02-08 08:09:14 -05:00
✨ Add usage quotes for snapshots
This commit is contained in:
parent
5b35cf7456
commit
9409078069
3 changed files with 139 additions and 73 deletions
|
@ -142,6 +142,8 @@
|
||||||
[:quotes-font-variants-per-team {:optional true} ::sm/int]
|
[:quotes-font-variants-per-team {:optional true} ::sm/int]
|
||||||
[:quotes-comment-threads-per-file {:optional true} ::sm/int]
|
[:quotes-comment-threads-per-file {:optional true} ::sm/int]
|
||||||
[:quotes-comments-per-file {:optional true} ::sm/int]
|
[:quotes-comments-per-file {:optional true} ::sm/int]
|
||||||
|
[:quotes-snapshots-per-file {:optional true} ::sm/int]
|
||||||
|
[:quotes-snapshots-per-team {:optional true} ::sm/int]
|
||||||
|
|
||||||
[:auth-data-cookie-domain {:optional true} :string]
|
[:auth-data-cookie-domain {:optional true} :string]
|
||||||
[:auth-token-cookie-name {:optional true} :string]
|
[:auth-token-cookie-name {:optional true} :string]
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
[app.rpc :as-alias rpc]
|
[app.rpc :as-alias rpc]
|
||||||
[app.rpc.commands.files :as files]
|
[app.rpc.commands.files :as files]
|
||||||
[app.rpc.doc :as-alias doc]
|
[app.rpc.doc :as-alias doc]
|
||||||
|
[app.rpc.quotes :as quotes]
|
||||||
[app.storage :as sto]
|
[app.storage :as sto]
|
||||||
[app.util.blob :as blob]
|
[app.util.blob :as blob]
|
||||||
[app.util.pointer-map :as pmap]
|
[app.util.pointer-map :as pmap]
|
||||||
|
@ -51,15 +52,24 @@
|
||||||
(files/check-read-permissions! conn profile-id file-id)
|
(files/check-read-permissions! conn profile-id file-id)
|
||||||
(get-file-snapshots conn file-id))))
|
(get-file-snapshots conn file-id))))
|
||||||
|
|
||||||
|
(def ^:private sql:get-file
|
||||||
|
"SELECT f.*,
|
||||||
|
p.id AS project_id,
|
||||||
|
p.team_id AS team_id
|
||||||
|
FROM file AS f
|
||||||
|
INNER JOIN project AS p ON (p.id = f.project_id)
|
||||||
|
WHERE f.id = ?")
|
||||||
|
|
||||||
(defn- get-file
|
(defn- get-file
|
||||||
[cfg file-id]
|
[cfg file-id]
|
||||||
(let [file (->> (db/get cfg :file {:id file-id})
|
(let [file (->> (db/exec-one! cfg [sql:get-file file-id])
|
||||||
(feat.fdata/resolve-file-data cfg))]
|
(feat.fdata/resolve-file-data cfg))]
|
||||||
(binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg file-id)]
|
(binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg file-id)]
|
||||||
(-> file
|
(-> file
|
||||||
(update :data blob/decode)
|
(update :data blob/decode)
|
||||||
(update :data feat.fdata/process-pointers deref)
|
(update :data feat.fdata/process-pointers deref)
|
||||||
(update :data feat.fdata/process-objects (partial into {}))
|
(update :data feat.fdata/process-objects (partial into {}))
|
||||||
|
(update :data assoc ::id file-id)
|
||||||
(update :data blob/encode)))))
|
(update :data blob/encode)))))
|
||||||
|
|
||||||
(defn- generate-snapshot-label
|
(defn- generate-snapshot-label
|
||||||
|
@ -72,12 +82,7 @@
|
||||||
|
|
||||||
(defn create-file-snapshot!
|
(defn create-file-snapshot!
|
||||||
[cfg profile-id file-id label]
|
[cfg profile-id file-id label]
|
||||||
(let [file (-> (get-file cfg file-id)
|
(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`
|
;; NOTE: final user never can provide label as `:system`
|
||||||
;; keyword because the validator implies label always as
|
;; keyword because the validator implies label always as
|
||||||
|
@ -98,13 +103,15 @@
|
||||||
(or label (generate-snapshot-label)))
|
(or label (generate-snapshot-label)))
|
||||||
|
|
||||||
snapshot-id
|
snapshot-id
|
||||||
(uuid/next)
|
(uuid/next)]
|
||||||
|
|
||||||
snapshot-data
|
(-> cfg
|
||||||
(-> (:data file)
|
(assoc ::quotes/profile-id profile-id)
|
||||||
(feat.fdata/process-pointers deref)
|
(assoc ::quotes/project-id (:project-id file))
|
||||||
(feat.fdata/process-objects (partial into {}))
|
(assoc ::quotes/team-id (:team-id file))
|
||||||
(blob/encode))]
|
(assoc ::quotes/file-id (:id file))
|
||||||
|
(quotes/check! {::quotes/id ::quotes/snapshots-per-file}
|
||||||
|
{::quotes/id ::quotes/snapshots-per-team}))
|
||||||
|
|
||||||
(l/debug :hint "creating file snapshot"
|
(l/debug :hint "creating file snapshot"
|
||||||
:file-id (str file-id)
|
:file-id (str file-id)
|
||||||
|
@ -114,7 +121,7 @@
|
||||||
(db/insert! cfg :file-change
|
(db/insert! cfg :file-change
|
||||||
{:id snapshot-id
|
{:id snapshot-id
|
||||||
:revn (:revn file)
|
:revn (:revn file)
|
||||||
:data snapshot-data
|
:data (:data file)
|
||||||
:version (:version file)
|
:version (:version file)
|
||||||
:features (:features file)
|
:features (:features file)
|
||||||
:profile-id profile-id
|
:profile-id profile-id
|
||||||
|
@ -145,10 +152,11 @@
|
||||||
(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 (some->> (db/get* conn :file-change
|
||||||
{:file-id file-id
|
{:file-id file-id
|
||||||
:id snapshot-id}
|
:id snapshot-id}
|
||||||
{::db/for-share true})]
|
{::db/for-share true})
|
||||||
|
(feat.fdata/resolve-file-data cfg))]
|
||||||
|
|
||||||
(when-not snapshot
|
(when-not snapshot
|
||||||
(ex/raise :type :not-found
|
(ex/raise :type :not-found
|
||||||
|
@ -157,67 +165,59 @@
|
||||||
:snapshot-id snapshot-id
|
:snapshot-id snapshot-id
|
||||||
:file-id file-id))
|
:file-id file-id))
|
||||||
|
|
||||||
;; (when (= (:revn snapshot) (:revn file))
|
(when-not (:data snapshot)
|
||||||
;; (ex/raise :type :validation
|
(ex/raise :type :validation
|
||||||
;; :code :snapshot-identical-to-file
|
:code :snapshot-without-data
|
||||||
;; :hint "you can't restore a snapshot that is identical to a file"
|
:hint "snapshot has no data"
|
||||||
;; :snapshot-id snapshot-id
|
:label (:label snapshot)
|
||||||
;; :file-id file-id))
|
:file-id file-id))
|
||||||
|
|
||||||
(let [snapshot (feat.fdata/resolve-file-data cfg snapshot)]
|
(l/dbg :hint "restoring snapshot"
|
||||||
(when-not (:data snapshot)
|
:file-id (str file-id)
|
||||||
(ex/raise :type :validation
|
:label (:label snapshot)
|
||||||
:code :snapshot-without-data
|
:snapshot-id (str (:id snapshot)))
|
||||||
:hint "snapshot has no data"
|
|
||||||
:label (:label snapshot)
|
|
||||||
:file-id file-id))
|
|
||||||
|
|
||||||
(l/dbg :hint "restoring snapshot"
|
;; If the file was already offloaded, on restring the snapshot
|
||||||
:file-id (str file-id)
|
;; we are going to replace the file data, so we need to touch
|
||||||
:label (:label snapshot)
|
;; the old referenced storage object and avoid possible leaks
|
||||||
:snapshot-id (str (:id snapshot)))
|
(when (feat.fdata/offloaded? file)
|
||||||
|
(sto/touch-object! storage (:data-ref-id file)))
|
||||||
|
|
||||||
;; If the file was already offloaded, on restring the snapshot
|
(db/update! conn :file
|
||||||
;; we are going to replace the file data, so we need to touch
|
{:data (:data snapshot)
|
||||||
;; the old referenced storage object and avoid possible leaks
|
:revn (inc (:revn file))
|
||||||
(when (feat.fdata/offloaded? file)
|
:vern vern
|
||||||
(sto/touch-object! storage (:data-ref-id file)))
|
:version (:version snapshot)
|
||||||
|
:data-backend nil
|
||||||
|
:data-ref-id nil
|
||||||
|
:has-media-trimmed false
|
||||||
|
:features (:features snapshot)}
|
||||||
|
{:id file-id})
|
||||||
|
|
||||||
(db/update! conn :file
|
;; clean object thumbnails
|
||||||
{:data (:data snapshot)
|
(let [sql (str "update file_tagged_object_thumbnail "
|
||||||
:revn (inc (:revn file))
|
" set deleted_at = now() "
|
||||||
:vern vern
|
" where file_id=? returning media_id")
|
||||||
:version (:version snapshot)
|
res (db/exec! conn [sql file-id])]
|
||||||
:data-backend nil
|
(doseq [media-id (into #{} (keep :media-id) res)]
|
||||||
:data-ref-id nil
|
(sto/touch-object! storage media-id)))
|
||||||
:has-media-trimmed false
|
|
||||||
:features (:features snapshot)}
|
|
||||||
{:id file-id})
|
|
||||||
|
|
||||||
;; clean object thumbnails
|
;; clean file thumbnails
|
||||||
(let [sql (str "update file_tagged_object_thumbnail "
|
(let [sql (str "update file_thumbnail "
|
||||||
" set deleted_at = now() "
|
" set deleted_at = now() "
|
||||||
" where file_id=? returning media_id")
|
" where file_id=? returning media_id")
|
||||||
res (db/exec! conn [sql file-id])]
|
res (db/exec! conn [sql file-id])]
|
||||||
(doseq [media-id (into #{} (keep :media-id) res)]
|
(doseq [media-id (into #{} (keep :media-id) res)]
|
||||||
(sto/touch-object! storage media-id)))
|
(sto/touch-object! storage media-id)))
|
||||||
|
|
||||||
;; clean file thumbnails
|
;; Send to the clients a notification to reload the file
|
||||||
(let [sql (str "update file_thumbnail "
|
(mbus/pub! msgbus
|
||||||
" set deleted_at = now() "
|
:topic (:id file)
|
||||||
" where file_id=? returning media_id")
|
:message {:type :file-restore
|
||||||
res (db/exec! conn [sql file-id])]
|
:file-id (:id file)
|
||||||
(doseq [media-id (into #{} (keep :media-id) res)]
|
:vern vern})
|
||||||
(sto/touch-object! storage media-id)))
|
{:id (:id snapshot)
|
||||||
|
:label (:label snapshot)}))
|
||||||
;; Send to the clients a notification to reload the file
|
|
||||||
(mbus/pub! msgbus
|
|
||||||
:topic (:id file)
|
|
||||||
:message {:type :file-restore
|
|
||||||
:file-id (:id file)
|
|
||||||
:vern vern})
|
|
||||||
{:id (:id snapshot)
|
|
||||||
:label (:label snapshot)})))
|
|
||||||
|
|
||||||
(def ^:private schema:restore-file-snapshot
|
(def ^:private schema:restore-file-snapshot
|
||||||
[:map {:title "restore-file-snapshot"}
|
[:map {:title "restore-file-snapshot"}
|
||||||
|
|
|
@ -408,6 +408,70 @@
|
||||||
(assoc ::count-sql [sql:get-comments-per-file file-id])
|
(assoc ::count-sql [sql:get-comments-per-file file-id])
|
||||||
(generic-check!)))
|
(generic-check!)))
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; QUOTE: SNAPSHOTS-PER-FILE
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(def ^:private schema:snapshots-per-file
|
||||||
|
[:map
|
||||||
|
[::profile-id ::sm/uuid]
|
||||||
|
[::project-id ::sm/uuid]
|
||||||
|
[::team-id ::sm/uuid]
|
||||||
|
[::file-id ::sm/uuid]])
|
||||||
|
|
||||||
|
(def ^:private valid-snapshots-per-file-quote?
|
||||||
|
(sm/lazy-validator schema:snapshots-per-file))
|
||||||
|
|
||||||
|
(def ^:private sql:get-snapshots-per-file
|
||||||
|
"SELECT count(*) AS total
|
||||||
|
FROM file_change AS fc
|
||||||
|
WHERE fc.file_id = ?
|
||||||
|
AND fc.created_by = 'user'
|
||||||
|
AND fc.deleted_at IS NULL
|
||||||
|
AND fc.data IS NOT NULL")
|
||||||
|
|
||||||
|
(defmethod check-quote ::snapshots-per-file
|
||||||
|
[{:keys [::profile-id ::file-id ::team-id ::project-id ::target] :as quote}]
|
||||||
|
(assert (valid-snapshots-per-file-quote? quote) "invalid quote parameters")
|
||||||
|
(-> quote
|
||||||
|
(assoc ::default (cf/get :quotes-snapshots-per-file Integer/MAX_VALUE))
|
||||||
|
(assoc ::quote-sql [sql:get-quotes-4 target file-id profile-id project-id
|
||||||
|
profile-id team-id profile-id profile-id])
|
||||||
|
(assoc ::count-sql [sql:get-snapshots-per-file file-id])
|
||||||
|
(generic-check!)))
|
||||||
|
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; QUOTE: SNAPSHOTS-PER-FILE
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(def ^:private schema:snapshots-per-team
|
||||||
|
[:map
|
||||||
|
[::profile-id ::sm/uuid]
|
||||||
|
[::team-id ::sm/uuid]])
|
||||||
|
|
||||||
|
(def ^:private valid-snapshots-per-team-quote?
|
||||||
|
(sm/lazy-validator schema:snapshots-per-team))
|
||||||
|
|
||||||
|
(def ^:private sql:get-snapshots-per-team
|
||||||
|
"SELECT count(*) AS total
|
||||||
|
FROM file_change AS fc
|
||||||
|
JOIN file AS f ON (f.id = fc.file_id)
|
||||||
|
JOIN project AS p ON (p.id = f.project_id)
|
||||||
|
WHERE p.team_id = ?
|
||||||
|
AND fc.created_by = 'user'
|
||||||
|
AND fc.deleted_at IS NULL
|
||||||
|
AND fc.data IS NOT NULL")
|
||||||
|
|
||||||
|
(defmethod check-quote ::snapshots-per-team
|
||||||
|
[{:keys [::profile-id ::team-id ::target] :as quote}]
|
||||||
|
(assert (valid-snapshots-per-team-quote? quote) "invalid quote parameters")
|
||||||
|
(-> quote
|
||||||
|
(assoc ::default (cf/get :quotes-snapshots-per-team Integer/MAX_VALUE))
|
||||||
|
(assoc ::quote-sql [sql:get-quotes-2 target team-id profile-id profile-id])
|
||||||
|
(assoc ::count-sql [sql:get-snapshots-per-team team-id])
|
||||||
|
(generic-check!)))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; QUOTE: DEFAULT
|
;; QUOTE: DEFAULT
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
Loading…
Add table
Reference in a new issue