mirror of
https://github.com/penpot/penpot.git
synced 2026-08-27 15:19:49 +00:00
🎉 Allow to validate and repair files from /dbg (#11335)
This commit is contained in:
parent
b4dc8207ff
commit
582187dd03
@ -190,6 +190,20 @@ Debug Main Page
|
||||
</div>
|
||||
</form>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>Validate file:</legend>
|
||||
<desc>Given an FILE-ID, check the referential integrity.</desc>
|
||||
<form method="get" action="/dbg/actions/file-validate">
|
||||
<div class="row">
|
||||
<input type="text" style="width:300px" name="file-id" placeholder="file-id" />
|
||||
</div>
|
||||
<div class="row">
|
||||
<input type="submit" name="validate" value="Validate" />
|
||||
</div>
|
||||
</form>
|
||||
</fieldset>
|
||||
|
||||
</section>
|
||||
<section class="widget">
|
||||
<fieldset>
|
||||
@ -222,6 +236,7 @@ Debug Main Page
|
||||
</div>
|
||||
</form>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>Import binfile:</legend>
|
||||
<desc>Import penpot file in binary format.</desc>
|
||||
@ -236,6 +251,34 @@ Debug Main Page
|
||||
</div>
|
||||
</form>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>Repair file:</legend>
|
||||
<desc>Given an FILE-ID, repair the referential integrity errors.
|
||||
<br/>
|
||||
<br/>
|
||||
<b>WARNING: the reparation is not guaranteed and may cause loss of data!</b>
|
||||
<br/>
|
||||
<br/>
|
||||
You may need to give several repair rounds until all errors are cleared.
|
||||
</desc>
|
||||
<form method="get" action="/dbg/actions/file-repair">
|
||||
<div class="row">
|
||||
<input type="text" style="width:300px" name="file-id" placeholder="file-id" />
|
||||
</div>
|
||||
<div class="row">
|
||||
<label for="check-snapshot">Skip snapshot</label>
|
||||
<input id="check-snapshot" type="checkbox" name="skip-snapshot" />
|
||||
<br />
|
||||
<small>
|
||||
A snapshot is made just before the validation, unless skipped.
|
||||
</small>
|
||||
</div>
|
||||
<div class="row">
|
||||
<input type="submit" name="repair" value="Repair" />
|
||||
</div>
|
||||
</form>
|
||||
</fieldset>
|
||||
</section>
|
||||
</main>
|
||||
{% endblock %}
|
||||
|
||||
@ -13,6 +13,9 @@
|
||||
[app.common.data :as d]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.features :as cfeat]
|
||||
[app.common.files.changes :as cfc]
|
||||
[app.common.files.repair :as cfr]
|
||||
[app.common.files.validate :as cfv]
|
||||
[app.common.logging :as l]
|
||||
[app.common.pprint :as pp]
|
||||
[app.common.time :as ct]
|
||||
@ -28,6 +31,7 @@
|
||||
[app.rpc.commands.teams :as teams]
|
||||
[app.setup :as-alias setup]
|
||||
[app.setup.clock :as clock]
|
||||
[app.srepl.helpers :as h]
|
||||
[app.srepl.main :as srepl]
|
||||
[app.storage :as-alias sto]
|
||||
[app.storage.tmp :as tmp]
|
||||
@ -130,7 +134,7 @@
|
||||
:hint "invalid button"))
|
||||
|
||||
(ex/raise :type :not-found
|
||||
:code :enpty-data
|
||||
:code :empty-data
|
||||
:hint "empty response"))))
|
||||
|
||||
(defn- is-file-exists?
|
||||
@ -484,6 +488,89 @@
|
||||
{::yres/status 302
|
||||
::yres/headers {"location" "/dbg"}}))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; VALIDATE / REPAIR
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn- validate-file
|
||||
[cfg {:keys [params] :as request}]
|
||||
(let [file-id (some-> params :file-id parse-uuid)]
|
||||
|
||||
(when-not file-id
|
||||
(ex/raise :type :validation
|
||||
:code :missing-arguments))
|
||||
|
||||
(db/tx-run! (assoc cfg ::db/rollback true)
|
||||
(fn [cfg]
|
||||
(let [file (bfc/get-file cfg file-id)
|
||||
libs (bfc/get-resolved-file-libraries cfg file-id)]
|
||||
(if file
|
||||
(let [errors (cfv/validate-file file libs)]
|
||||
{::yres/status 200
|
||||
::yres/headers {"content-type" "text/plain"}
|
||||
::yres/body (if (empty? errors)
|
||||
"NO VALIDATION ERRORS FOUND"
|
||||
(pp/pprint-str errors))})
|
||||
(ex/raise :type :not-found
|
||||
:code :empty-data
|
||||
:hint "empty response")))))))
|
||||
|
||||
(defn- repair-file
|
||||
[cfg {:keys [params] :as request}]
|
||||
(let [file-id (some-> params :file-id parse-uuid)
|
||||
skip-snapshot? (contains? params :skip-snapshot)
|
||||
profile-id (:app.http.session/profile-id request)]
|
||||
|
||||
(when-not file-id
|
||||
(ex/raise :type :validation
|
||||
:code :missing-arguments))
|
||||
|
||||
(let [output (StringBuilder.)
|
||||
|
||||
repair-file
|
||||
(fn [file libs _]
|
||||
(let [errors (cfv/validate-file file libs)]
|
||||
(.append output (if (empty? errors)
|
||||
"NO VALIDATION ERRORS FOUND\n"
|
||||
(str "VALIDATION ERRORS FOUND:\n"
|
||||
(pp/pprint-str errors) "\n")))
|
||||
(if (empty? errors)
|
||||
file
|
||||
(let [changes (cfr/repair-file file libs errors)]
|
||||
(-> file
|
||||
(update :revn inc)
|
||||
(update :data cfc/process-changes changes))))))]
|
||||
|
||||
(add-watch l/log-record ::repair-watcher
|
||||
(fn [_ _ _ record]
|
||||
(when (= "app.common.files.repair" (::l/logger record))
|
||||
(let [props (::l/props record)
|
||||
hint (get props :hint "")
|
||||
args (dissoc props :hint)
|
||||
message (str hint " "
|
||||
(when-not (empty? args)
|
||||
args)
|
||||
"\n")]
|
||||
(.append output message)))))
|
||||
(try
|
||||
(db/tx-run! cfg
|
||||
h/process-file!
|
||||
file-id
|
||||
repair-file
|
||||
{::h/with-libraries? true
|
||||
::h/validate? false
|
||||
::h/profile-id profile-id
|
||||
::h/snapshot-label (when-not skip-snapshot? "repair")})
|
||||
|
||||
(.append output "\nREPAIR FINISHED")
|
||||
|
||||
{::yres/status 200
|
||||
::yres/headers {"content-type" "text/plain"}
|
||||
::yres/body (.toString output)}
|
||||
|
||||
(finally
|
||||
(remove-watch l/log-record ::repair-watcher))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; OTHER SMALL VIEWS/HANDLERS
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
@ -578,5 +665,7 @@
|
||||
{:handler (partial handle-team-features cfg)}]
|
||||
["/file-export" {:handler (partial export-handler cfg)}]
|
||||
["/file-import" {:handler (partial import-handler cfg)}]
|
||||
["/file-raw-export-import" {:handler (partial raw-export-import-handler cfg)}]]]])
|
||||
["/file-raw-export-import" {:handler (partial raw-export-import-handler cfg)}]
|
||||
["/file-validate" {:handler (partial validate-file cfg)}]
|
||||
["/file-repair" {:handler (partial repair-file cfg)}]]]])
|
||||
|
||||
|
||||
@ -153,7 +153,7 @@
|
||||
|
||||
(defn process-file!
|
||||
[system file-id update-fn
|
||||
& {:keys [::snapshot-label ::validate? ::with-libraries?]
|
||||
& {:keys [::profile-id ::snapshot-label ::validate? ::with-libraries?]
|
||||
:or {validate? true} :as opts}]
|
||||
(let [file (bfc/get-file system file-id
|
||||
:lock-for-update? true
|
||||
@ -177,8 +177,9 @@
|
||||
(when (string? snapshot-label)
|
||||
(fsnap/create! system file
|
||||
{:label snapshot-label
|
||||
:profile-id profile-id
|
||||
:deleted-at (ct/in-future {:days 30})
|
||||
:created-by "admin"}))
|
||||
:created-by "system"}))
|
||||
|
||||
(let [file' (update file' :revn inc)]
|
||||
(bfc/update-file! system file' opts)
|
||||
|
||||
@ -398,10 +398,6 @@
|
||||
(println (sm/humanize-explain explain))
|
||||
(ex/print-throwable cause))))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; PROCESSING
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn repair-file!
|
||||
"Repair the list of errors detected by validation."
|
||||
[file-id & {:keys [rollback?] :or {rollback? true} :as options}]
|
||||
@ -410,6 +406,10 @@
|
||||
options (assoc options ::h/with-libraries? true)]
|
||||
(db/tx-run! system h/process-file! file-id procs.file-repair/repair-file options)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; PROCESSING
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn update-file!
|
||||
"Apply a function to the file. Optionally save the changes or not.
|
||||
The function receives the decoded and migrated file data."
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user