mirror of
https://github.com/penpot/penpot.git
synced 2025-06-03 01:01:37 +02:00
- Migrate from puppeteer to playwright - Fix many lifecycle and resource usage issues - Add redis integration - Enable multiple exportation - Enable asynchronos exportation (with progress reporting)
130 lines
4.1 KiB
Clojure
130 lines
4.1 KiB
Clojure
;; 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) UXBOX Labs SL
|
|
|
|
(ns app.handlers.resources
|
|
"Temporal resouces management."
|
|
(:require
|
|
["archiver" :as arc]
|
|
["fs" :as fs]
|
|
["os" :as os]
|
|
["path" :as path]
|
|
[app.common.data :as d]
|
|
[app.common.data.macros :as dm]
|
|
[app.common.exceptions :as ex]
|
|
[app.common.uuid :as uuid]
|
|
[app.util.shell :as sh]
|
|
[cljs.core :as c]
|
|
[cuerdas.core :as str]
|
|
[promesa.core :as p]))
|
|
|
|
(defn- get-path
|
|
[type id]
|
|
(path/join (os/tmpdir) (dm/str "exporter." (d/name type) "." id)))
|
|
|
|
(defn- get-mtype
|
|
[type]
|
|
(case (d/name type)
|
|
"zip" "application/zip"
|
|
"jpeg" "image/jpeg"
|
|
"png" "image/png"
|
|
"pdf" "application/pdf"))
|
|
|
|
(defn create
|
|
"Generates ephimeral resource object."
|
|
[type name]
|
|
(let [task-id (uuid/next)]
|
|
{:path (get-path type task-id)
|
|
:mtype (get-mtype type)
|
|
:name name
|
|
:id (dm/str (c/name type) "." task-id)}))
|
|
|
|
(defn- write-as-zip!
|
|
[{:keys [id path]} items on-progress]
|
|
(let [^js zip (arc/create "zip")
|
|
^js out (fs/createWriteStream path)
|
|
append! (fn [{:keys [data name] :as result}]
|
|
(.append zip data #js {:name name}))
|
|
progress (atom 0)]
|
|
(p/create
|
|
(fn [resolve reject]
|
|
(.on zip "error" #(reject %))
|
|
(.on zip "end" resolve)
|
|
(.on zip "entry" (fn [data]
|
|
(let [name (unchecked-get data "name")
|
|
num (swap! progress inc)]
|
|
#_(when (= 2 num)
|
|
(.abort ^js zip)
|
|
(reject (js/Error. "unable to create zip file")))
|
|
(on-progress
|
|
{:total (count items)
|
|
:done num}))))
|
|
(.pipe zip out)
|
|
(-> (reduce (fn [res export-fn]
|
|
(p/then res (fn [_] (-> (export-fn) (p/then append!)))))
|
|
(p/resolved 1)
|
|
items)
|
|
(p/then #(.finalize zip))
|
|
(p/catch reject))))))
|
|
|
|
(defn create-simple
|
|
[& {:keys [task resource on-progress on-complete on-error]
|
|
:or {on-progress identity
|
|
on-complete identity
|
|
on-error identity}
|
|
:as params}]
|
|
(let [path (:path resource)]
|
|
(-> (task)
|
|
(p/then (fn [{:keys [data name]}]
|
|
(on-progress {:total 1 :done 1 :name name})
|
|
(.writeFile fs/promises path data)))
|
|
(p/then #(sh/stat path))
|
|
(p/then #(merge resource %))
|
|
(p/finally (fn [result cause]
|
|
(if cause
|
|
(on-error cause)
|
|
(on-complete result)))))))
|
|
|
|
(defn create-zip
|
|
"Creates a resource with multiple files merget into a single zip file."
|
|
[& {:keys [resource tasks on-error on-progress on-complete]
|
|
:or {on-error identity
|
|
on-progress identity
|
|
on-complete identity}}]
|
|
(let [{:keys [path id] :as resource} resource]
|
|
(-> (write-as-zip! resource tasks on-progress)
|
|
(p/then #(sh/stat path))
|
|
(p/then #(merge resource %))
|
|
(p/finally (fn [result cause]
|
|
(if cause
|
|
(on-error cause)
|
|
(on-complete result)))))))
|
|
|
|
(defn- lookup
|
|
[id]
|
|
(p/let [[type task-id] (str/split id "." 2)
|
|
path (get-path type task-id)
|
|
mtype (get-mtype type)
|
|
stat (sh/stat path)]
|
|
|
|
(when-not stat
|
|
(ex/raise :type :not-found))
|
|
|
|
{:stream (fs/createReadStream path)
|
|
:headers {"content-type" mtype
|
|
"content-length" (:size stat)}}))
|
|
|
|
(defn handler
|
|
[{:keys [:request/params response] :as exchange}]
|
|
(when-not (contains? params :id)
|
|
(ex/raise :type :validation
|
|
:code :missing-id))
|
|
|
|
(-> (lookup (get params :id))
|
|
(p/then (fn [{:keys [stream headers] :as resource}]
|
|
(-> exchange
|
|
(assoc :response/status 200)
|
|
(assoc :response/body stream)
|
|
(assoc :response/headers headers))))))
|