Persist binfile manifest metadata in file_data on import

Store the generated-by and referer fields from the v3 binfile
manifest into the existing file_data.metadata JSONB column during
import. This preserves the export source information for diagnostics
and analytics.

The metadata schema in fdata.clj is extended with optional
:generated-by and :referer string fields. The storage backend
persistence now merges metadata instead of overwriting it, so
manifest fields coexist with :storage-ref-id.

Closes #11106

AI-assisted-by: mimo-v2.5-pro
This commit is contained in:
Andrey Antukh 2026-08-06 10:38:16 +00:00
parent 03668f6819
commit 8b902e38d2
6 changed files with 25 additions and 16 deletions

View File

@ -723,6 +723,7 @@
(-> (select-keys file file-attrs)
(assoc :data nil)
(dissoc :team-id)
(dissoc :metadata)
(dissoc :migrations)))
(defn- file->file-data-params

View File

@ -392,7 +392,7 @@
params {:type "penpot/export-files"
:version 1
:generated-by (str "penpot/" (:full cf/version))
:refer "penpot"
:referer "penpot"
:files (vec (vals files))
:relations rels}]
(write-entry! output "manifest.json" params))))
@ -734,7 +734,7 @@
:plugin-data plugin-data}))
(defn- import-file
[{:keys [::db/conn ::bfc/project-id] :as cfg} {file-id :id file-name :name}]
[{:keys [::db/conn ::bfc/project-id ::manifest] :as cfg} {file-id :id file-name :name}]
(let [file-id' (bfc/lookup-index file-id)
file (read-file cfg file-id)
media (read-file-media cfg file-id)
@ -801,8 +801,10 @@
(assoc :data data)
(assoc :name file-name)
(assoc :project-id project-id)
(assoc :metadata (d/without-nils
{:generated-by (get manifest :generated-by)
:referer (get manifest :referer)}))
(dissoc :options))
file (bfc/process-file cfg file)
file (ctf/check-file file)]

View File

@ -12,6 +12,7 @@
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.types.file :as ctf]
[app.common.types.objects-map :as omap]
[app.config :as cf]
[app.db :as db]
@ -130,6 +131,7 @@
metadata (some-> metadata db/json)
modified-at (or modified-at created-at)]
(db/exec-one! cfg [sql:upsert-file-data
file-id id
created-at
@ -159,15 +161,15 @@
:content-type "application/octet-stream"
:file-id file-id
:id id})
metadata {:storage-ref-id (:id sobject)}
metadata (-> (:metadata params)
(assoc :storage-ref-id (:id sobject)))
params (-> params
(assoc :metadata metadata)
(assoc :data nil))]
(upsert-in-database cfg params))
(= backend "db")
(->> (dissoc params :metadata)
(upsert-in-database cfg))
(upsert-in-database cfg params)
(= backend "legacy-db")
(cond
@ -213,18 +215,11 @@
[backend]
(or backend (cf/get :file-data-backend)))
(def ^:private schema:metadata
[:map {:title "Metadata"}
[:storage-ref-id {:optional true} ::sm/uuid]])
(def decode-metadata-with-schema
(sm/decoder schema:metadata sm/json-transformer))
(defn decode-metadata
[metadata]
(some-> metadata
(db/decode-json-pgobject)
(decode-metadata-with-schema)))
(ctf/decode-file-metadata)))
(def ^:private schema:update-params
[:map {:closed true}
@ -232,7 +227,7 @@
[:type [:enum "main" "snapshot" "fragment"]]
[:file-id ::sm/uuid]
[:backend {:optional true} [:enum "db" "legacy-db" "storage"]]
[:metadata {:optional true} [:maybe schema:metadata]]
[:metadata {:optional true} [:maybe ctf/schema:file-metadata]]
[:data {:optional true} bytes?]
[:created-at {:optional true} ::ct/inst]
[:modified-at {:optional true} [:maybe ::ct/inst]]

View File

@ -236,6 +236,7 @@
explain (sm/explainer schema)
decode (sm/decoder schema sm/json-transformer)
encode (sm/encoder schema sm/json-transformer)]
(fn [cfg params]
(let [params (decode params)]
(if (validate params)

View File

@ -118,7 +118,7 @@
(def ^:private schema:import-binfile
[:and
[:map {:title "import-binfile" :closed true}
[:map {:title "import-binfile"}
[:name [:or [:string {:max 250}]
[:map-of ::sm/uuid [:string {:max 250}]]]]
[:project-id ::sm/uuid]

View File

@ -88,6 +88,12 @@
[:plugin-data {:optional true} schema:plugin-data]
[:tokens-lib {:optional true} schema:tokens-lib]])
(def schema:file-metadata
[:map {:title "Metadata"}
[:storage-ref-id {:optional true} ::sm/uuid]
[:generated-by {:optional true} :string]
[:referer {:optional true} :string]])
(def schema:file
"A schema for validate a file data structure; data is optional
because sometimes we want to validate file without the data."
@ -106,6 +112,7 @@
[:data {:optional true} schema:data]
[:version :int]
[:features ::cfeat/features]
[:metadata {:optional true} schema:file-metadata]
[:migrations {:optional true}
[::sm/set {:ordered true} :string]]])
@ -123,6 +130,9 @@
(def check-file-media
(sm/check-fn schema:media))
(def decode-file-metadata
(sm/decoder schema:file-metadata sm/json-transformer))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; INITIALIZATION
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;