mirror of
https://github.com/penpot/penpot.git
synced 2025-06-06 23:21:38 +02:00
Merge pull request #5654 from penpot/niwinz-clone-template-bug
🐛 Add support for multiple file formats to clone-template
This commit is contained in:
commit
19a26e46dc
11 changed files with 140 additions and 119 deletions
|
@ -114,37 +114,13 @@ Debug Main Page
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Import binfile:</legend>
|
<legend>Import binfile:</legend>
|
||||||
<desc>Import penpot file in binary
|
<desc>Import penpot file in binary format.</desc>
|
||||||
format. If <strong>overwrite</strong> is checked, all files will
|
|
||||||
be overwritten using the same ids found in the file instead of
|
|
||||||
generating a new ones.</desc>
|
|
||||||
|
|
||||||
<form method="post" enctype="multipart/form-data" action="/dbg/file/import">
|
<form method="post" enctype="multipart/form-data" action="/dbg/file/import">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<input type="file" name="file" value="" />
|
<input type="file" name="file" value="" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<label>Overwrite?</label>
|
|
||||||
<input type="checkbox" name="overwrite" />
|
|
||||||
<br />
|
|
||||||
<small>
|
|
||||||
Instead of creating a new file with all relations remapped,
|
|
||||||
reuses all ids and updates/overwrites the objects that are
|
|
||||||
already exists on the database.
|
|
||||||
<strong>Warning, this operation should be used with caution.</strong>
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<label>Migrate?</label>
|
|
||||||
<input type="checkbox" name="migrate" />
|
|
||||||
<br />
|
|
||||||
<small>
|
|
||||||
Applies the file migrations on the importation process.
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<input type="submit" name="upload" value="Upload" />
|
<input type="submit" name="upload" value="Upload" />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -30,7 +30,9 @@
|
||||||
[app.worker :as-alias wrk]
|
[app.worker :as-alias wrk]
|
||||||
[clojure.set :as set]
|
[clojure.set :as set]
|
||||||
[clojure.walk :as walk]
|
[clojure.walk :as walk]
|
||||||
[cuerdas.core :as str]))
|
[cuerdas.core :as str]
|
||||||
|
[datoteka.fs :as fs]
|
||||||
|
[datoteka.io :as io]))
|
||||||
|
|
||||||
(set! *warn-on-reflection* true)
|
(set! *warn-on-reflection* true)
|
||||||
|
|
||||||
|
@ -52,6 +54,20 @@
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(defn parse-file-format
|
||||||
|
[template]
|
||||||
|
(assert (fs/path? template) "expected InputStream for `template`")
|
||||||
|
|
||||||
|
(with-open [^java.lang.AutoCloseable input (io/input-stream template)]
|
||||||
|
(let [buffer (byte-array 4)]
|
||||||
|
(io/read-to-buffer input buffer)
|
||||||
|
(if (and (= (aget buffer 0) 80)
|
||||||
|
(= (aget buffer 1) 75)
|
||||||
|
(= (aget buffer 2) 3)
|
||||||
|
(= (aget buffer 3) 4))
|
||||||
|
:binfile-v3
|
||||||
|
:binfile-v1))))
|
||||||
|
|
||||||
(def xf-map-id
|
(def xf-map-id
|
||||||
(map :id))
|
(map :id))
|
||||||
|
|
||||||
|
|
|
@ -298,7 +298,7 @@
|
||||||
(defmulti write-section ::section)
|
(defmulti write-section ::section)
|
||||||
|
|
||||||
(defn write-export!
|
(defn write-export!
|
||||||
[{:keys [::include-libraries ::embed-assets] :as cfg}]
|
[{:keys [::bfc/include-libraries ::bfc/embed-assets] :as cfg}]
|
||||||
(when (and include-libraries embed-assets)
|
(when (and include-libraries embed-assets)
|
||||||
(throw (IllegalArgumentException.
|
(throw (IllegalArgumentException.
|
||||||
"the `include-libraries` and `embed-assets` are mutally excluding options")))
|
"the `include-libraries` and `embed-assets` are mutally excluding options")))
|
||||||
|
@ -323,7 +323,7 @@
|
||||||
[:v1/metadata :v1/files :v1/rels :v1/sobjects]))))
|
[:v1/metadata :v1/files :v1/rels :v1/sobjects]))))
|
||||||
|
|
||||||
(defmethod write-section :v1/metadata
|
(defmethod write-section :v1/metadata
|
||||||
[{:keys [::output ::ids ::include-libraries] :as cfg}]
|
[{:keys [::output ::bfc/ids ::bfc/include-libraries] :as cfg}]
|
||||||
(if-let [fids (get-files cfg ids)]
|
(if-let [fids (get-files cfg ids)]
|
||||||
(let [lids (when include-libraries
|
(let [lids (when include-libraries
|
||||||
(bfc/get-libraries cfg ids))
|
(bfc/get-libraries cfg ids))
|
||||||
|
@ -335,7 +335,7 @@
|
||||||
:hint "unable to retrieve files for export")))
|
:hint "unable to retrieve files for export")))
|
||||||
|
|
||||||
(defmethod write-section :v1/files
|
(defmethod write-section :v1/files
|
||||||
[{:keys [::output ::embed-assets ::include-libraries] :as cfg}]
|
[{:keys [::output ::bfc/embed-assets ::bfc/include-libraries] :as cfg}]
|
||||||
|
|
||||||
;; Initialize SIDS with empty vector
|
;; Initialize SIDS with empty vector
|
||||||
(vswap! bfc/*state* assoc :sids [])
|
(vswap! bfc/*state* assoc :sids [])
|
||||||
|
@ -382,7 +382,7 @@
|
||||||
(vswap! bfc/*state* update :sids into bfc/xf-map-media-id thumbnails))))
|
(vswap! bfc/*state* update :sids into bfc/xf-map-media-id thumbnails))))
|
||||||
|
|
||||||
(defmethod write-section :v1/rels
|
(defmethod write-section :v1/rels
|
||||||
[{:keys [::output ::include-libraries] :as cfg}]
|
[{:keys [::output ::bfc/include-libraries] :as cfg}]
|
||||||
(let [ids (-> bfc/*state* deref :files set)
|
(let [ids (-> bfc/*state* deref :files set)
|
||||||
rels (when include-libraries
|
rels (when include-libraries
|
||||||
(bfc/get-files-rels cfg ids))]
|
(bfc/get-files-rels cfg ids))]
|
||||||
|
@ -421,15 +421,15 @@
|
||||||
(defmulti read-import ::version)
|
(defmulti read-import ::version)
|
||||||
(defmulti read-section ::section)
|
(defmulti read-section ::section)
|
||||||
|
|
||||||
(s/def ::profile-id ::us/uuid)
|
(s/def ::bfc/profile-id ::us/uuid)
|
||||||
(s/def ::project-id ::us/uuid)
|
(s/def ::bfc/project-id ::us/uuid)
|
||||||
(s/def ::input io/input-stream?)
|
(s/def ::bfc/input io/input-stream?)
|
||||||
(s/def ::overwrite? (s/nilable ::us/boolean))
|
(s/def ::overwrite? (s/nilable ::us/boolean))
|
||||||
(s/def ::ignore-index-errors? (s/nilable ::us/boolean))
|
(s/def ::ignore-index-errors? (s/nilable ::us/boolean))
|
||||||
|
|
||||||
;; FIXME: replace with schema
|
;; FIXME: replace with schema
|
||||||
(s/def ::read-import-options
|
(s/def ::read-import-options
|
||||||
(s/keys :req [::db/pool ::sto/storage ::project-id ::profile-id ::input]
|
(s/keys :req [::db/pool ::sto/storage ::bfc/project-id ::bfc/profile-id ::bfc/input]
|
||||||
:opt [::overwrite? ::ignore-index-errors?]))
|
:opt [::overwrite? ::ignore-index-errors?]))
|
||||||
|
|
||||||
(defn read-import!
|
(defn read-import!
|
||||||
|
@ -439,7 +439,7 @@
|
||||||
|
|
||||||
`::bfc/overwrite`: if true, instead of creating new files and remapping id references,
|
`::bfc/overwrite`: if true, instead of creating new files and remapping id references,
|
||||||
it reuses all ids and updates existing objects; defaults to `false`."
|
it reuses all ids and updates existing objects; defaults to `false`."
|
||||||
[{:keys [::input ::bfc/timestamp] :or {timestamp (dt/now)} :as options}]
|
[{:keys [::bfc/input ::bfc/timestamp] :or {timestamp (dt/now)} :as options}]
|
||||||
|
|
||||||
(dm/assert!
|
(dm/assert!
|
||||||
"expected input stream"
|
"expected input stream"
|
||||||
|
@ -453,7 +453,7 @@
|
||||||
(read-import (assoc options ::version version ::bfc/timestamp timestamp))))
|
(read-import (assoc options ::version version ::bfc/timestamp timestamp))))
|
||||||
|
|
||||||
(defn- read-import-v1
|
(defn- read-import-v1
|
||||||
[{:keys [::db/conn ::project-id ::profile-id ::input] :as cfg}]
|
[{:keys [::db/conn ::bfc/project-id ::bfc/profile-id ::bfc/input] :as cfg}]
|
||||||
|
|
||||||
(bfc/disable-database-timeouts! cfg)
|
(bfc/disable-database-timeouts! cfg)
|
||||||
|
|
||||||
|
@ -473,7 +473,7 @@
|
||||||
(let [options (-> cfg
|
(let [options (-> cfg
|
||||||
(assoc ::bfc/features features)
|
(assoc ::bfc/features features)
|
||||||
(assoc ::section section)
|
(assoc ::section section)
|
||||||
(assoc ::input input))]
|
(assoc ::bfc/input input))]
|
||||||
(binding [bfc/*options* options]
|
(binding [bfc/*options* options]
|
||||||
(events/tap :progress {:op :import :section section})
|
(events/tap :progress {:op :import :section section})
|
||||||
(read-section options))))
|
(read-section options))))
|
||||||
|
@ -491,7 +491,7 @@
|
||||||
(db/tx-run! options read-import-v1))
|
(db/tx-run! options read-import-v1))
|
||||||
|
|
||||||
(defmethod read-section :v1/metadata
|
(defmethod read-section :v1/metadata
|
||||||
[{:keys [::input]}]
|
[{:keys [::bfc/input]}]
|
||||||
(let [{:keys [version files]} (read-obj! input)]
|
(let [{:keys [version files]} (read-obj! input)]
|
||||||
(l/dbg :hint "metadata readed"
|
(l/dbg :hint "metadata readed"
|
||||||
:version (:full version)
|
:version (:full version)
|
||||||
|
@ -509,7 +509,7 @@
|
||||||
thumbnails))
|
thumbnails))
|
||||||
|
|
||||||
(defmethod read-section :v1/files
|
(defmethod read-section :v1/files
|
||||||
[{:keys [::db/conn ::input ::project-id ::bfc/overwrite ::name] :as system}]
|
[{:keys [::db/conn ::bfc/input ::bfc/project-id ::bfc/overwrite ::bfc/name] :as system}]
|
||||||
|
|
||||||
(doseq [[idx expected-file-id] (d/enumerate (-> bfc/*state* deref :files))]
|
(doseq [[idx expected-file-id] (d/enumerate (-> bfc/*state* deref :files))]
|
||||||
(let [file (read-obj! input)
|
(let [file (read-obj! input)
|
||||||
|
@ -576,7 +576,7 @@
|
||||||
file-id'))))
|
file-id'))))
|
||||||
|
|
||||||
(defmethod read-section :v1/rels
|
(defmethod read-section :v1/rels
|
||||||
[{:keys [::db/conn ::input ::bfc/timestamp]}]
|
[{:keys [::db/conn ::bfc/input ::bfc/timestamp]}]
|
||||||
(let [rels (read-obj! input)
|
(let [rels (read-obj! input)
|
||||||
ids (into #{} (-> bfc/*state* deref :files))]
|
ids (into #{} (-> bfc/*state* deref :files))]
|
||||||
;; Insert all file relations
|
;; Insert all file relations
|
||||||
|
@ -600,7 +600,7 @@
|
||||||
::l/sync? true))))))
|
::l/sync? true))))))
|
||||||
|
|
||||||
(defmethod read-section :v1/sobjects
|
(defmethod read-section :v1/sobjects
|
||||||
[{:keys [::db/conn ::input ::bfc/overwrite ::bfc/timestamp] :as cfg}]
|
[{:keys [::db/conn ::bfc/input ::bfc/overwrite ::bfc/timestamp] :as cfg}]
|
||||||
(let [storage (sto/resolve cfg)
|
(let [storage (sto/resolve cfg)
|
||||||
ids (read-obj! input)
|
ids (read-obj! input)
|
||||||
thumb? (into #{} (map :media-id) (:thumbnails @bfc/*state*))]
|
thumb? (into #{} (map :media-id) (:thumbnails @bfc/*state*))]
|
||||||
|
@ -674,17 +674,17 @@
|
||||||
"Do the exportation of a specified file in custom penpot binary
|
"Do the exportation of a specified file in custom penpot binary
|
||||||
format. There are some options available for customize the output:
|
format. There are some options available for customize the output:
|
||||||
|
|
||||||
`::include-libraries`: additionally to the specified file, all the
|
`::bfc/include-libraries`: additionally to the specified file, all the
|
||||||
linked libraries also will be included (including transitive
|
linked libraries also will be included (including transitive
|
||||||
dependencies).
|
dependencies).
|
||||||
|
|
||||||
`::embed-assets`: instead of including the libraries, embed in the
|
`::bfc/embed-assets`: instead of including the libraries, embed in the
|
||||||
same file library all assets used from external libraries."
|
same file library all assets used from external libraries."
|
||||||
|
|
||||||
[{:keys [::ids] :as cfg} output]
|
[{:keys [::bfc/ids] :as cfg} output]
|
||||||
|
|
||||||
(dm/assert!
|
(dm/assert!
|
||||||
"expected a set of uuid's for `::ids` parameter"
|
"expected a set of uuid's for `::bfc/ids` parameter"
|
||||||
(and (set? ids)
|
(and (set? ids)
|
||||||
(every? uuid? ids)))
|
(every? uuid? ids)))
|
||||||
|
|
||||||
|
@ -719,12 +719,12 @@
|
||||||
:cause @cs)))))
|
:cause @cs)))))
|
||||||
|
|
||||||
(defn import-files!
|
(defn import-files!
|
||||||
[{:keys [::input] :as cfg}]
|
[{:keys [::bfc/input] :as cfg}]
|
||||||
|
|
||||||
(dm/assert!
|
(dm/assert!
|
||||||
"expected valid profile-id and project-id on `cfg`"
|
"expected valid profile-id and project-id on `cfg`"
|
||||||
(and (uuid? (::profile-id cfg))
|
(and (uuid? (::bfc/profile-id cfg))
|
||||||
(uuid? (::project-id cfg))))
|
(uuid? (::bfc/project-id cfg))))
|
||||||
|
|
||||||
(dm/assert!
|
(dm/assert!
|
||||||
"expected instance of jio/IOFactory for `input`"
|
"expected instance of jio/IOFactory for `input`"
|
||||||
|
@ -738,7 +738,7 @@
|
||||||
(try
|
(try
|
||||||
(binding [*position* (atom 0)]
|
(binding [*position* (atom 0)]
|
||||||
(pu/with-open [input (io/input-stream input)]
|
(pu/with-open [input (io/input-stream input)]
|
||||||
(read-import! (assoc cfg ::input input))))
|
(read-import! (assoc cfg ::bfc/input input))))
|
||||||
|
|
||||||
(catch ZstdIOException cause
|
(catch ZstdIOException cause
|
||||||
(ex/raise :type :validation
|
(ex/raise :type :validation
|
||||||
|
|
|
@ -192,7 +192,7 @@
|
||||||
(.closeEntry output))
|
(.closeEntry output))
|
||||||
|
|
||||||
(defn- get-file
|
(defn- get-file
|
||||||
[{:keys [::embed-assets ::include-libraries] :as cfg} file-id]
|
[{:keys [::bfc/embed-assets ::bfc/include-libraries] :as cfg} file-id]
|
||||||
|
|
||||||
(when (and include-libraries embed-assets)
|
(when (and include-libraries embed-assets)
|
||||||
(throw (IllegalArgumentException.
|
(throw (IllegalArgumentException.
|
||||||
|
@ -330,7 +330,7 @@
|
||||||
(write-entry! output path color)))))
|
(write-entry! output path color)))))
|
||||||
|
|
||||||
(defn- export-files
|
(defn- export-files
|
||||||
[{:keys [::ids ::include-libraries ::output] :as cfg}]
|
[{:keys [::bfc/ids ::bfc/include-libraries ::output] :as cfg}]
|
||||||
(let [ids (into ids (when include-libraries (bfc/get-libraries cfg ids)))
|
(let [ids (into ids (when include-libraries (bfc/get-libraries cfg ids)))
|
||||||
rels (if include-libraries
|
rels (if include-libraries
|
||||||
(->> (bfc/get-files-rels cfg ids)
|
(->> (bfc/get-files-rels cfg ids)
|
||||||
|
@ -509,7 +509,7 @@
|
||||||
(json/read reader :key-fn json/read-kebab-key)))
|
(json/read reader :key-fn json/read-kebab-key)))
|
||||||
|
|
||||||
(defn- read-file
|
(defn- read-file
|
||||||
[{:keys [::input ::file-id]}]
|
[{:keys [::bfc/input ::file-id]}]
|
||||||
(let [path (str "files/" file-id ".json")
|
(let [path (str "files/" file-id ".json")
|
||||||
entry (get-zip-entry input path)]
|
entry (get-zip-entry input path)]
|
||||||
(-> (read-entry input entry)
|
(-> (read-entry input entry)
|
||||||
|
@ -517,7 +517,7 @@
|
||||||
(validate-file))))
|
(validate-file))))
|
||||||
|
|
||||||
(defn- read-file-plugin-data
|
(defn- read-file-plugin-data
|
||||||
[{:keys [::input ::file-id]}]
|
[{:keys [::bfc/input ::file-id]}]
|
||||||
(let [path (str "files/" file-id "/plugin-data.json")
|
(let [path (str "files/" file-id "/plugin-data.json")
|
||||||
entry (get-zip-entry* input path)]
|
entry (get-zip-entry* input path)]
|
||||||
(some->> entry
|
(some->> entry
|
||||||
|
@ -526,7 +526,7 @@
|
||||||
(validate-plugin-data))))
|
(validate-plugin-data))))
|
||||||
|
|
||||||
(defn- read-file-media
|
(defn- read-file-media
|
||||||
[{:keys [::input ::file-id ::entries]}]
|
[{:keys [::bfc/input ::file-id ::entries]}]
|
||||||
(->> (keep (match-media-entry-fn file-id) entries)
|
(->> (keep (match-media-entry-fn file-id) entries)
|
||||||
(reduce (fn [result {:keys [id entry]}]
|
(reduce (fn [result {:keys [id entry]}]
|
||||||
(let [object (->> (read-entry input entry)
|
(let [object (->> (read-entry input entry)
|
||||||
|
@ -540,7 +540,7 @@
|
||||||
(not-empty)))
|
(not-empty)))
|
||||||
|
|
||||||
(defn- read-file-colors
|
(defn- read-file-colors
|
||||||
[{:keys [::input ::file-id ::entries]}]
|
[{:keys [::bfc/input ::file-id ::entries]}]
|
||||||
(->> (keep (match-color-entry-fn file-id) entries)
|
(->> (keep (match-color-entry-fn file-id) entries)
|
||||||
(reduce (fn [result {:keys [id entry]}]
|
(reduce (fn [result {:keys [id entry]}]
|
||||||
(let [object (->> (read-entry input entry)
|
(let [object (->> (read-entry input entry)
|
||||||
|
@ -553,7 +553,7 @@
|
||||||
(not-empty)))
|
(not-empty)))
|
||||||
|
|
||||||
(defn- read-file-components
|
(defn- read-file-components
|
||||||
[{:keys [::input ::file-id ::entries]}]
|
[{:keys [::bfc/input ::file-id ::entries]}]
|
||||||
(->> (keep (match-component-entry-fn file-id) entries)
|
(->> (keep (match-component-entry-fn file-id) entries)
|
||||||
(reduce (fn [result {:keys [id entry]}]
|
(reduce (fn [result {:keys [id entry]}]
|
||||||
(let [object (->> (read-entry input entry)
|
(let [object (->> (read-entry input entry)
|
||||||
|
@ -566,7 +566,7 @@
|
||||||
(not-empty)))
|
(not-empty)))
|
||||||
|
|
||||||
(defn- read-file-typographies
|
(defn- read-file-typographies
|
||||||
[{:keys [::input ::file-id ::entries]}]
|
[{:keys [::bfc/input ::file-id ::entries]}]
|
||||||
(->> (keep (match-typography-entry-fn file-id) entries)
|
(->> (keep (match-typography-entry-fn file-id) entries)
|
||||||
(reduce (fn [result {:keys [id entry]}]
|
(reduce (fn [result {:keys [id entry]}]
|
||||||
(let [object (->> (read-entry input entry)
|
(let [object (->> (read-entry input entry)
|
||||||
|
@ -579,7 +579,7 @@
|
||||||
(not-empty)))
|
(not-empty)))
|
||||||
|
|
||||||
(defn- read-file-shapes
|
(defn- read-file-shapes
|
||||||
[{:keys [::input ::file-id ::page-id ::entries] :as cfg}]
|
[{:keys [::bfc/input ::file-id ::page-id ::entries] :as cfg}]
|
||||||
(->> (keep (match-shape-entry-fn file-id page-id) entries)
|
(->> (keep (match-shape-entry-fn file-id page-id) entries)
|
||||||
(reduce (fn [result {:keys [id entry]}]
|
(reduce (fn [result {:keys [id entry]}]
|
||||||
(let [object (->> (read-entry input entry)
|
(let [object (->> (read-entry input entry)
|
||||||
|
@ -592,7 +592,7 @@
|
||||||
(not-empty)))
|
(not-empty)))
|
||||||
|
|
||||||
(defn- read-file-pages
|
(defn- read-file-pages
|
||||||
[{:keys [::input ::file-id ::entries] :as cfg}]
|
[{:keys [::bfc/input ::file-id ::entries] :as cfg}]
|
||||||
(->> (keep (match-page-entry-fn file-id) entries)
|
(->> (keep (match-page-entry-fn file-id) entries)
|
||||||
(keep (fn [{:keys [id entry]}]
|
(keep (fn [{:keys [id entry]}]
|
||||||
(let [page (->> (read-entry input entry)
|
(let [page (->> (read-entry input entry)
|
||||||
|
@ -608,7 +608,7 @@
|
||||||
(d/ordered-map))))
|
(d/ordered-map))))
|
||||||
|
|
||||||
(defn- read-file-thumbnails
|
(defn- read-file-thumbnails
|
||||||
[{:keys [::input ::file-id ::entries] :as cfg}]
|
[{:keys [::bfc/input ::file-id ::entries] :as cfg}]
|
||||||
(->> (keep (match-thumbnail-entry-fn file-id) entries)
|
(->> (keep (match-thumbnail-entry-fn file-id) entries)
|
||||||
(reduce (fn [result {:keys [page-id frame-id tag entry]}]
|
(reduce (fn [result {:keys [page-id frame-id tag entry]}]
|
||||||
(let [object (->> (read-entry input entry)
|
(let [object (->> (read-entry input entry)
|
||||||
|
@ -638,7 +638,7 @@
|
||||||
:plugin-data plugin-data}))
|
:plugin-data plugin-data}))
|
||||||
|
|
||||||
(defn- import-file
|
(defn- import-file
|
||||||
[{:keys [::db/conn ::project-id ::file-id ::file-name] :as cfg}]
|
[{:keys [::db/conn ::bfc/project-id ::file-id ::file-name] :as cfg}]
|
||||||
(let [file-id' (bfc/lookup-index file-id)
|
(let [file-id' (bfc/lookup-index file-id)
|
||||||
file (read-file cfg)
|
file (read-file cfg)
|
||||||
media (read-file-media cfg)
|
media (read-file-media cfg)
|
||||||
|
@ -714,7 +714,7 @@
|
||||||
:library-file-id libr-id})))))
|
:library-file-id libr-id})))))
|
||||||
|
|
||||||
(defn- import-storage-objects
|
(defn- import-storage-objects
|
||||||
[{:keys [::input ::entries ::bfc/timestamp] :as cfg}]
|
[{:keys [::bfc/input ::entries ::bfc/timestamp] :as cfg}]
|
||||||
(events/tap :progress {:section :storage-objects})
|
(events/tap :progress {:section :storage-objects})
|
||||||
|
|
||||||
(let [storage (sto/resolve cfg)
|
(let [storage (sto/resolve cfg)
|
||||||
|
@ -810,7 +810,7 @@
|
||||||
{::db/on-conflict-do-nothing? (::bfc/overwrite cfg)}))))
|
{::db/on-conflict-do-nothing? (::bfc/overwrite cfg)}))))
|
||||||
|
|
||||||
(defn- import-files
|
(defn- import-files
|
||||||
[{:keys [::bfc/timestamp ::input ::name] :or {timestamp (dt/now)} :as cfg}]
|
[{:keys [::bfc/timestamp ::bfc/input ::bfc/name] :or {timestamp (dt/now)} :as cfg}]
|
||||||
|
|
||||||
(dm/assert!
|
(dm/assert!
|
||||||
"expected zip file"
|
"expected zip file"
|
||||||
|
@ -878,17 +878,17 @@
|
||||||
"Do the exportation of a specified file in custom penpot binary
|
"Do the exportation of a specified file in custom penpot binary
|
||||||
format. There are some options available for customize the output:
|
format. There are some options available for customize the output:
|
||||||
|
|
||||||
`::include-libraries`: additionally to the specified file, all the
|
`::bfc/include-libraries`: additionally to the specified file, all the
|
||||||
linked libraries also will be included (including transitive
|
linked libraries also will be included (including transitive
|
||||||
dependencies).
|
dependencies).
|
||||||
|
|
||||||
`::embed-assets`: instead of including the libraries, embed in the
|
`::bfc/embed-assets`: instead of including the libraries, embed in the
|
||||||
same file library all assets used from external libraries."
|
same file library all assets used from external libraries."
|
||||||
|
|
||||||
[{:keys [::ids] :as cfg} output]
|
[{:keys [::bfc/ids] :as cfg} output]
|
||||||
|
|
||||||
(dm/assert!
|
(dm/assert!
|
||||||
"expected a set of uuid's for `::ids` parameter"
|
"expected a set of uuid's for `::bfc/ids` parameter"
|
||||||
(and (set? ids)
|
(and (set? ids)
|
||||||
(every? uuid? ids)))
|
(every? uuid? ids)))
|
||||||
|
|
||||||
|
@ -930,14 +930,13 @@
|
||||||
:aborted @ab
|
:aborted @ab
|
||||||
:cause @cs)))))
|
:cause @cs)))))
|
||||||
|
|
||||||
|
|
||||||
(defn import-files!
|
(defn import-files!
|
||||||
[{:keys [::input] :as cfg}]
|
[{:keys [::bfc/input] :as cfg}]
|
||||||
|
|
||||||
(dm/assert!
|
(dm/assert!
|
||||||
"expected valid profile-id and project-id on `cfg`"
|
"expected valid profile-id and project-id on `cfg`"
|
||||||
(and (uuid? (::profile-id cfg))
|
(and (uuid? (::bfc/profile-id cfg))
|
||||||
(uuid? (::project-id cfg))))
|
(uuid? (::bfc/project-id cfg))))
|
||||||
|
|
||||||
(dm/assert!
|
(dm/assert!
|
||||||
"expected instance of jio/IOFactory for `input`"
|
"expected instance of jio/IOFactory for `input`"
|
||||||
|
@ -950,7 +949,7 @@
|
||||||
(l/info :hint "import: started" :id (str id))
|
(l/info :hint "import: started" :id (str id))
|
||||||
(try
|
(try
|
||||||
(with-open [input (ZipFile. (fs/file input))]
|
(with-open [input (ZipFile. (fs/file input))]
|
||||||
(import-files (assoc cfg ::input input)))
|
(import-files (assoc cfg ::bfc/input input)))
|
||||||
|
|
||||||
(catch Throwable cause
|
(catch Throwable cause
|
||||||
(vreset! cs cause)
|
(vreset! cs cause)
|
||||||
|
|
|
@ -7,7 +7,9 @@
|
||||||
(ns app.http.debug
|
(ns app.http.debug
|
||||||
(:refer-clojure :exclude [error-handler])
|
(:refer-clojure :exclude [error-handler])
|
||||||
(:require
|
(:require
|
||||||
|
[app.binfile.common :as bfc]
|
||||||
[app.binfile.v1 :as bf.v1]
|
[app.binfile.v1 :as bf.v1]
|
||||||
|
[app.binfile.v3 :as bf.v3]
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.exceptions :as ex]
|
[app.common.exceptions :as ex]
|
||||||
[app.common.logging :as l]
|
[app.common.logging :as l]
|
||||||
|
@ -280,23 +282,23 @@
|
||||||
(ex/raise :type :validation
|
(ex/raise :type :validation
|
||||||
:code :missing-arguments))
|
:code :missing-arguments))
|
||||||
|
|
||||||
(let [path (tmp/tempfile :prefix "penpot.export.")]
|
(let [path (tmp/tempfile :prefix "penpot.export." :min-age "30m")]
|
||||||
(with-open [output (io/output-stream path)]
|
(with-open [output (io/output-stream path)]
|
||||||
(-> cfg
|
(-> cfg
|
||||||
(assoc ::bf.v1/ids file-ids)
|
(assoc ::bfc/ids file-ids)
|
||||||
(assoc ::bf.v1/embed-assets embed?)
|
(assoc ::bfc/embed-assets embed?)
|
||||||
(assoc ::bf.v1/include-libraries libs?)
|
(assoc ::bfc/include-libraries libs?)
|
||||||
(bf.v1/export-files! output)))
|
(bf.v3/export-files! output)))
|
||||||
|
|
||||||
(if clone?
|
(if clone?
|
||||||
(let [profile (profile/get-profile pool profile-id)
|
(let [profile (profile/get-profile pool profile-id)
|
||||||
project-id (:default-project-id profile)
|
project-id (:default-project-id profile)
|
||||||
cfg (assoc cfg
|
cfg (assoc cfg
|
||||||
::bf.v1/overwrite false
|
::bfc/overwrite false
|
||||||
::bf.v1/profile-id profile-id
|
::bfc/profile-id profile-id
|
||||||
::bf.v1/project-id project-id
|
::bfc/project-id project-id
|
||||||
::bf.v1/input path)]
|
::bfc/input path)]
|
||||||
(bf.v1/import-files! cfg)
|
(bf.v3/import-files! cfg)
|
||||||
{::yres/status 200
|
{::yres/status 200
|
||||||
::yres/headers {"content-type" "text/plain"}
|
::yres/headers {"content-type" "text/plain"}
|
||||||
::yres/body "OK CLONED"})
|
::yres/body "OK CLONED"})
|
||||||
|
@ -315,9 +317,7 @@
|
||||||
:hint "missing upload file"))
|
:hint "missing upload file"))
|
||||||
|
|
||||||
(let [profile (profile/get-profile pool profile-id)
|
(let [profile (profile/get-profile pool profile-id)
|
||||||
project-id (:default-project-id profile)
|
project-id (:default-project-id profile)]
|
||||||
overwrite? (contains? params :overwrite)
|
|
||||||
migrate? (contains? params :migrate)]
|
|
||||||
|
|
||||||
(when-not project-id
|
(when-not project-id
|
||||||
(ex/raise :type :validation
|
(ex/raise :type :validation
|
||||||
|
@ -325,13 +325,16 @@
|
||||||
:hint "project not found"))
|
:hint "project not found"))
|
||||||
|
|
||||||
(let [path (-> params :file :path)
|
(let [path (-> params :file :path)
|
||||||
|
format (bfc/parse-file-format path)
|
||||||
cfg (assoc cfg
|
cfg (assoc cfg
|
||||||
::bf.v1/overwrite overwrite?
|
::bfc/profile-id profile-id
|
||||||
::bf.v1/migrate migrate?
|
::bfc/project-id project-id
|
||||||
::bf.v1/profile-id profile-id
|
::bfc/input path)]
|
||||||
::bf.v1/project-id project-id
|
|
||||||
::bf.v1/input path)]
|
(if (= format :binfile-v3)
|
||||||
(bf.v1/import-files! cfg)
|
(bf.v3/import-files! cfg)
|
||||||
|
(bf.v1/import-files! cfg))
|
||||||
|
|
||||||
{::yres/status 200
|
{::yres/status 200
|
||||||
::yres/headers {"content-type" "text/plain"}
|
::yres/headers {"content-type" "text/plain"}
|
||||||
::yres/body "OK"})))
|
::yres/body "OK"})))
|
||||||
|
|
|
@ -64,7 +64,8 @@
|
||||||
(catch Throwable cause
|
(catch Throwable cause
|
||||||
(events/tap :error (errors/handle' cause request))
|
(events/tap :error (errors/handle' cause request))
|
||||||
(when-not (ex/instance? java.io.EOFException cause)
|
(when-not (ex/instance? java.io.EOFException cause)
|
||||||
(l/err :hint "unexpected error on processing sse response" :cause cause)))
|
(binding [l/*context* (errors/request->context request)]
|
||||||
|
(l/err :hint "unexpected error on processing sse response" :cause cause))))
|
||||||
(finally
|
(finally
|
||||||
(sp/close! events/*channel*)
|
(sp/close! events/*channel*)
|
||||||
(px/await! listener)))))))}))
|
(px/await! listener)))))))}))
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
(ns app.rpc.commands.binfile
|
(ns app.rpc.commands.binfile
|
||||||
(:refer-clojure :exclude [assert])
|
(:refer-clojure :exclude [assert])
|
||||||
(:require
|
(:require
|
||||||
|
[app.binfile.common :as bfc]
|
||||||
[app.binfile.v1 :as bf.v1]
|
[app.binfile.v1 :as bf.v1]
|
||||||
[app.binfile.v3 :as bf.v3]
|
[app.binfile.v3 :as bf.v3]
|
||||||
[app.common.logging :as l]
|
[app.common.logging :as l]
|
||||||
|
@ -46,9 +47,9 @@
|
||||||
(fn [_ output-stream]
|
(fn [_ output-stream]
|
||||||
(try
|
(try
|
||||||
(-> cfg
|
(-> cfg
|
||||||
(assoc ::bf.v1/ids #{file-id})
|
(assoc ::bfc/ids #{file-id})
|
||||||
(assoc ::bf.v1/embed-assets embed-assets)
|
(assoc ::bfc/embed-assets embed-assets)
|
||||||
(assoc ::bf.v1/include-libraries include-libraries)
|
(assoc ::bfc/include-libraries include-libraries)
|
||||||
(bf.v1/export-files! output-stream))
|
(bf.v1/export-files! output-stream))
|
||||||
(catch Throwable cause
|
(catch Throwable cause
|
||||||
(l/err :hint "exception on exporting file"
|
(l/err :hint "exception on exporting file"
|
||||||
|
@ -61,9 +62,9 @@
|
||||||
(fn [_ output-stream]
|
(fn [_ output-stream]
|
||||||
(try
|
(try
|
||||||
(-> cfg
|
(-> cfg
|
||||||
(assoc ::bf.v3/ids #{file-id})
|
(assoc ::bfc/ids #{file-id})
|
||||||
(assoc ::bf.v3/embed-assets embed-assets)
|
(assoc ::bfc/embed-assets embed-assets)
|
||||||
(assoc ::bf.v3/include-libraries include-libraries)
|
(assoc ::bfc/include-libraries include-libraries)
|
||||||
(bf.v3/export-files! output-stream))
|
(bf.v3/export-files! output-stream))
|
||||||
(catch Throwable cause
|
(catch Throwable cause
|
||||||
(l/err :hint "exception on exporting file"
|
(l/err :hint "exception on exporting file"
|
||||||
|
@ -93,10 +94,10 @@
|
||||||
(defn- import-binfile-v1
|
(defn- import-binfile-v1
|
||||||
[{:keys [::wrk/executor] :as cfg} {:keys [project-id profile-id name file]}]
|
[{:keys [::wrk/executor] :as cfg} {:keys [project-id profile-id name file]}]
|
||||||
(let [cfg (-> cfg
|
(let [cfg (-> cfg
|
||||||
(assoc ::bf.v1/project-id project-id)
|
(assoc ::bfc/project-id project-id)
|
||||||
(assoc ::bf.v1/profile-id profile-id)
|
(assoc ::bfc/profile-id profile-id)
|
||||||
(assoc ::bf.v1/name name)
|
(assoc ::bfc/name name)
|
||||||
(assoc ::bf.v1/input (:path file)))]
|
(assoc ::bfc/input (:path file)))]
|
||||||
|
|
||||||
;; NOTE: the importation process performs some operations that are
|
;; NOTE: the importation process performs some operations that are
|
||||||
;; not very friendly with virtual threads, and for avoid
|
;; not very friendly with virtual threads, and for avoid
|
||||||
|
@ -107,10 +108,10 @@
|
||||||
(defn- import-binfile-v3
|
(defn- import-binfile-v3
|
||||||
[{:keys [::wrk/executor] :as cfg} {:keys [project-id profile-id name file]}]
|
[{:keys [::wrk/executor] :as cfg} {:keys [project-id profile-id name file]}]
|
||||||
(let [cfg (-> cfg
|
(let [cfg (-> cfg
|
||||||
(assoc ::bf.v3/project-id project-id)
|
(assoc ::bfc/project-id project-id)
|
||||||
(assoc ::bf.v3/profile-id profile-id)
|
(assoc ::bfc/profile-id profile-id)
|
||||||
(assoc ::bf.v3/name name)
|
(assoc ::bfc/name name)
|
||||||
(assoc ::bf.v3/input (:path file)))]
|
(assoc ::bfc/input (:path file)))]
|
||||||
;; NOTE: the importation process performs some operations that are
|
;; NOTE: the importation process performs some operations that are
|
||||||
;; not very friendly with virtual threads, and for avoid
|
;; not very friendly with virtual threads, and for avoid
|
||||||
;; unexpected blocking of other concurrent operations we dispatch
|
;; unexpected blocking of other concurrent operations we dispatch
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
(:require
|
(:require
|
||||||
[app.binfile.common :as bfc]
|
[app.binfile.common :as bfc]
|
||||||
[app.binfile.v1 :as bf.v1]
|
[app.binfile.v1 :as bf.v1]
|
||||||
|
[app.binfile.v3 :as bf.v3]
|
||||||
[app.common.exceptions :as ex]
|
[app.common.exceptions :as ex]
|
||||||
[app.common.features :as cfeat]
|
[app.common.features :as cfeat]
|
||||||
[app.common.schema :as sm]
|
[app.common.schema :as sm]
|
||||||
|
@ -25,6 +26,7 @@
|
||||||
[app.rpc.doc :as-alias doc]
|
[app.rpc.doc :as-alias doc]
|
||||||
[app.setup :as-alias setup]
|
[app.setup :as-alias setup]
|
||||||
[app.setup.templates :as tmpl]
|
[app.setup.templates :as tmpl]
|
||||||
|
[app.storage.tmp :as tmp]
|
||||||
[app.util.services :as sv]
|
[app.util.services :as sv]
|
||||||
[app.util.time :as dt]
|
[app.util.time :as dt]
|
||||||
[app.worker :as-alias wrk]
|
[app.worker :as-alias wrk]
|
||||||
|
@ -400,11 +402,20 @@
|
||||||
;; that are not very friendly with virtual threads, and for
|
;; that are not very friendly with virtual threads, and for
|
||||||
;; avoid unexpected blocking of other concurrent operations
|
;; avoid unexpected blocking of other concurrent operations
|
||||||
;; we dispatch that operation to a dedicated executor.
|
;; we dispatch that operation to a dedicated executor.
|
||||||
(let [cfg (-> cfg
|
(let [template (tmp/tempfile-from template
|
||||||
(assoc ::bf.v1/project-id project-id)
|
:prefix "penpot.template."
|
||||||
(assoc ::bf.v1/profile-id profile-id)
|
:suffix ""
|
||||||
(assoc ::bf.v1/input template))
|
:min-age "30m")
|
||||||
result (px/invoke! executor (partial bf.v1/import-files! cfg))]
|
format (bfc/parse-file-format template)
|
||||||
|
|
||||||
|
cfg (-> cfg
|
||||||
|
(assoc ::bfc/project-id project-id)
|
||||||
|
(assoc ::bfc/profile-id profile-id)
|
||||||
|
(assoc ::bfc/input template))
|
||||||
|
|
||||||
|
result (if (= format :binfile-v3)
|
||||||
|
(px/invoke! executor (partial bf.v3/import-files! cfg))
|
||||||
|
(px/invoke! executor (partial bf.v1/import-files! cfg)))]
|
||||||
|
|
||||||
(db/update! conn :project
|
(db/update! conn :project
|
||||||
{:modified-at (dt/now)}
|
{:modified-at (dt/now)}
|
||||||
|
|
|
@ -54,6 +54,7 @@
|
||||||
(::setup/templates cfg))]
|
(::setup/templates cfg))]
|
||||||
(let [dest (fs/join fs/*cwd* "builtin-templates")
|
(let [dest (fs/join fs/*cwd* "builtin-templates")
|
||||||
path (or (:path template) (fs/join dest template-id))]
|
path (or (:path template) (fs/join dest template-id))]
|
||||||
|
|
||||||
(if (fs/exists? path)
|
(if (fs/exists? path)
|
||||||
(io/input-stream path)
|
(io/input-stream path)
|
||||||
(let [resp (http/req! cfg
|
(let [resp (http/req! cfg
|
||||||
|
|
|
@ -16,10 +16,13 @@
|
||||||
[app.util.time :as dt]
|
[app.util.time :as dt]
|
||||||
[app.worker :as wrk]
|
[app.worker :as wrk]
|
||||||
[datoteka.fs :as fs]
|
[datoteka.fs :as fs]
|
||||||
|
[datoteka.io :as io]
|
||||||
[integrant.core :as ig]
|
[integrant.core :as ig]
|
||||||
[promesa.exec :as px]
|
[promesa.exec :as px]
|
||||||
[promesa.exec.csp :as sp])
|
[promesa.exec.csp :as sp])
|
||||||
(:import
|
(:import
|
||||||
|
java.io.InputStream
|
||||||
|
java.io.OutputStream
|
||||||
java.nio.file.Files))
|
java.nio.file.Files))
|
||||||
|
|
||||||
(def default-tmp-dir "/tmp/penpot")
|
(def default-tmp-dir "/tmp/penpot")
|
||||||
|
@ -86,3 +89,12 @@
|
||||||
(fs/delete-on-exit! path)
|
(fs/delete-on-exit! path)
|
||||||
(sp/offer! queue [path (some-> min-age dt/duration)])
|
(sp/offer! queue [path (some-> min-age dt/duration)])
|
||||||
path))
|
path))
|
||||||
|
|
||||||
|
(defn tempfile-from
|
||||||
|
"Create a new tempfile from from consuming the stream"
|
||||||
|
[input & {:as options}]
|
||||||
|
(let [path (tempfile options)]
|
||||||
|
(with-open [^InputStream input (io/input-stream input)]
|
||||||
|
(with-open [^OutputStream output (io/output-stream path)]
|
||||||
|
(io/copy input output)))
|
||||||
|
path))
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
(ns backend-tests.binfile-test
|
(ns backend-tests.binfile-test
|
||||||
"Internal binfile test, no RPC involved"
|
"Internal binfile test, no RPC involved"
|
||||||
(:require
|
(:require
|
||||||
|
[app.binfile.common :as bfc]
|
||||||
[app.binfile.v3 :as v3]
|
[app.binfile.v3 :as v3]
|
||||||
[app.common.features :as cfeat]
|
[app.common.features :as cfeat]
|
||||||
[app.common.pprint :as pp]
|
[app.common.pprint :as pp]
|
||||||
|
@ -93,15 +94,15 @@
|
||||||
|
|
||||||
(v3/export-files!
|
(v3/export-files!
|
||||||
(-> th/*system*
|
(-> th/*system*
|
||||||
(assoc ::v3/ids #{(:id file)})
|
(assoc ::bfc/ids #{(:id file)})
|
||||||
(assoc ::v3/embed-assets false)
|
(assoc ::bfc/embed-assets false)
|
||||||
(assoc ::v3/include-libraries false))
|
(assoc ::bfc/include-libraries false))
|
||||||
(io/output-stream output))
|
(io/output-stream output))
|
||||||
|
|
||||||
(let [result (-> th/*system*
|
(let [result (-> th/*system*
|
||||||
(assoc ::v3/project-id (:default-project-id profile))
|
(assoc ::bfc/project-id (:default-project-id profile))
|
||||||
(assoc ::v3/profile-id (:id profile))
|
(assoc ::bfc/profile-id (:id profile))
|
||||||
(assoc ::v3/input output)
|
(assoc ::bfc/input output)
|
||||||
(v3/import-files!))]
|
(v3/import-files!))]
|
||||||
(t/is (= (count result) 1))
|
(t/is (= (count result) 1))
|
||||||
(t/is (every? uuid? result)))))
|
(t/is (every? uuid? result)))))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue