0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-23 15:26:29 -05:00

🐛 Fix many issues related to exportation process

This commit is contained in:
Andrey Antukh 2022-03-23 13:19:35 +01:00
parent 8fa708d573
commit b3d70f2556
8 changed files with 77 additions and 49 deletions

View file

@ -7,8 +7,10 @@
(ns app.browser (ns app.browser
(:require (:require
["generic-pool" :as gp] ["generic-pool" :as gp]
["generic-pool/lib/errors" :as gpe]
["playwright" :as pw] ["playwright" :as pw]
[app.common.data :as d] [app.common.data :as d]
[app.common.exceptions :as ex]
[app.common.logging :as l] [app.common.logging :as l]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.config :as cf] [app.config :as cf]
@ -17,6 +19,8 @@
(l/set-level! :trace) (l/set-level! :trace)
(def TimeoutError gpe/TimeoutError)
;; --- BROWSER API ;; --- BROWSER API
(def default-timeout 30000) (def default-timeout 30000)
@ -117,21 +121,23 @@
(defn init (defn init
[] []
(l/info :msg "initializing browser pool") (let [opts #js {:max (cf/get :browser-pool-max 5)
(let [opts #js {:max (cf/get :exporter-browser-pool-max 5) :min (cf/get :browser-pool-min 0)
:min (cf/get :exporter-browser-pool-min 0)
:testOnBorrow true :testOnBorrow true
:evictionRunIntervalMillis 5000 :evictionRunIntervalMillis 5000
:numTestsPerEvictionRun 5 :numTestsPerEvictionRun 5
:acquireTimeoutMillis 120000 ; 2min ;; :acquireTimeoutMillis 120000 ; 2min
:acquireTimeoutMillis 10000 ; 10 s
:idleTimeoutMillis 10000}] :idleTimeoutMillis 10000}]
(l/info :hint "initializing browser pool" :opts opts)
(reset! pool (gp/createPool browser-pool-factory opts)) (reset! pool (gp/createPool browser-pool-factory opts))
(p/resolved nil))) (p/resolved nil)))
(defn stop (defn stop
[] []
(when-let [pool (deref pool)] (when-let [pool (deref pool)]
(l/info :msg "finalizing browser pool") (l/info :hint "finalizing browser pool")
(p/do! (p/do!
(.drain ^js pool) (.drain ^js pool)
(.clear ^js pool)))) (.clear ^js pool))))
@ -140,6 +146,15 @@
[p] [p]
(p/handle p (constantly nil))) (p/handle p (constantly nil)))
(defn- translate-browser-errors
[cause]
(if (instance? TimeoutError cause)
(ex/raise :type :internal
:code :timeout
:hint (ex-message cause)
:cause cause)
(p/rejected cause)))
(defn exec! (defn exec!
[config handle] [config handle]
(letfn [(handle-browser [browser] (letfn [(handle-browser [browser]
@ -166,5 +181,4 @@
(when-let [pool (deref pool)] (when-let [pool (deref pool)]
(-> (p/do! (.acquire ^js pool)) (-> (p/do! (.acquire ^js pool))
(p/then (partial on-acquire pool)) (p/then (partial on-acquire pool))
(p/catch (fn [cause] (p/catch translate-browser-errors)))))
(p/rejected cause)))))))

View file

@ -26,17 +26,16 @@
:http-server-port 6061 :http-server-port 6061
:http-server-host "localhost" :http-server-host "localhost"
:redis-uri "redis://redis/0" :redis-uri "redis://redis/0"
:exporter-domain-whitelist #{"localhost2:3449"}}) :exporter-domain-whitelist #{"localhost:3449"}})
(s/def ::http-server-port ::us/integer) (s/def ::http-server-port ::us/integer)
(s/def ::http-server-host ::us/string) (s/def ::http-server-host ::us/string)
(s/def ::public-uri ::us/uri) (s/def ::public-uri ::us/uri)
(s/def ::tenant ::us/string) (s/def ::tenant ::us/string)
(s/def ::host ::us/string) (s/def ::host ::us/string)
(s/def ::domain-white-list ::us/set-of-str)
(s/def ::exporter-domain-whitelist ::us/set-of-str) (s/def ::browser-pool-max ::us/integer)
(s/def ::exporter-browser-pool-max ::us/integer) (s/def ::browser-pool-min ::us/integer)
(s/def ::exporter-browser-pool-min ::us/integer)
(s/def ::config (s/def ::config
(s/keys :opt-un [::public-uri (s/keys :opt-un [::public-uri
@ -44,9 +43,9 @@
::tenant ::tenant
::http-server-port ::http-server-port
::http-server-host ::http-server-host
::exporter-browser-pool-max ::browser-pool-max
::exporter-browser-pool-min ::browser-pool-min
::exporter-domain-whitelist])) ::domain-whitelist]))
(defn- read-env (defn- read-env
[prefix] [prefix]

View file

@ -21,7 +21,7 @@
[promesa.core :as p] [promesa.core :as p]
[reitit.core :as r])) [reitit.core :as r]))
(l/set-level! :info) (l/set-level! :debug)
(defn on-error (defn on-error
[error exchange] [error exchange]
@ -52,8 +52,10 @@
(assoc :response/headers {"content-type" "application/transit+json"})) (assoc :response/headers {"content-type" "application/transit+json"}))
:else :else
(do (let [data {:type :server-error
(l/error :msg "Unexpected error" :cause error) :hint (ex-message error)
:data data}]
(l/error :hint "unexpected internal error" :cause error)
(-> exchange (-> exchange
(assoc :response/status 500) (assoc :response/status 500)
(assoc :response/body (t/encode data)) (assoc :response/body (t/encode data))
@ -77,7 +79,7 @@
(defn validate-uri! (defn validate-uri!
[uri] [uri]
(let [white-list (cf/get :exporter-domain-whitelist #{}) (let [white-list (cf/get :domain-white-list #{})
default (cf/get :public-uri)] default (cf/get :public-uri)]
(when-not (or (contains? white-list (u/get-domain uri)) (when-not (or (contains? white-list (u/get-domain uri))
(= (u/get-domain default) (u/get-domain uri))) (= (u/get-domain default) (u/get-domain uri)))
@ -88,6 +90,7 @@
(defn handler (defn handler
[{:keys [:request/params] :as exchange}] [{:keys [:request/params] :as exchange}]
(let [{:keys [cmd uri] :as params} (us/conform ::params params)] (let [{:keys [cmd uri] :as params} (us/conform ::params params)]
(l/debug :hint "process-request" :cmd cmd)
(some-> uri validate-uri!) (some-> uri validate-uri!)
(case cmd (case cmd
:get-resource (resources/handler exchange) :get-resource (resources/handler exchange)

View file

@ -159,10 +159,10 @@
server (create-server handler) server (create-server handler)
port (cf/get :http-server-port 6061)] port (cf/get :http-server-port 6061)]
(.listen server port) (.listen server port)
(l/info :msg "welcome to penpot" (l/info :hint "welcome to penpot"
:module "exporter" :module "exporter"
:version (:full @cf/version)) :version (:full @cf/version))
(l/info :msg "starting http server" :port port) (l/info :hint "starting http server" :port port)
(reset! instance server))) (reset! instance server)))
(defn stop (defn stop
@ -170,6 +170,6 @@
(if-let [server @instance] (if-let [server @instance]
(p/create (fn [resolve] (p/create (fn [resolve]
(.close server (fn [] (.close server (fn []
(l/info :msg "shutdown http server") (l/info :hint "shutdown http server")
(resolve))))) (resolve)))))
(p/resolved nil))) (p/resolved nil)))

View file

@ -58,23 +58,15 @@
(s/def ::object-id ::us/uuid) (s/def ::object-id ::us/uuid)
(s/def ::scale ::us/number) (s/def ::scale ::us/number)
(s/def ::token ::us/string) (s/def ::token ::us/string)
(s/def ::origin ::us/string)
(s/def ::uri ::us/uri) (s/def ::uri ::us/uri)
(s/def ::params (s/def ::params
(s/keys :req-un [::name ::suffix ::type ::object-id ::page-id ::scale ::token ::file-id] (s/keys :req-un [::name ::suffix ::type ::object-id ::page-id ::scale ::token ::file-id]
:opt-un [::origin ::uri])) :opt-un [::uri]))
(defn render (defn render
[params] [params]
(us/verify ::params params) (us/verify ::params params)
(when (and (:origin params)
(not (contains? (cf/get :origin-white-list) (:origin params))))
(ex/raise :type :validation
:code :invalid-origin
:hint "invalid origin"
:origin (:origin params)))
(p/let [content (screenshot-object params)] (p/let [content (screenshot-object params)]
{:data content {:data content
:name (str (:name params) :name (str (:name params)

View file

@ -7,6 +7,7 @@
(ns app.main.data.exports (ns app.main.data.exports
(:require (:require
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.uuid :as uuid]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.data.workspace.persistence :as dwp] [app.main.data.workspace.persistence :as dwp]
[app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.state-helpers :as wsh]
@ -117,7 +118,7 @@
:filename filename}))))))) :filename filename})))))))
(defn- initialize-export-status (defn- initialize-export-status
[exports filename resource-id query] [exports filename resource-id query-name]
(ptk/reify ::initialize-export-status (ptk/reify ::initialize-export-status
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
@ -131,7 +132,7 @@
:exports exports :exports exports
:filename filename :filename filename
:last-update (dt/now) :last-update (dt/now)
:query query})))) :query-name query-name}))))
(defn- update-export-status (defn- update-export-status
[{:keys [progress status resource-id name] :as data}] [{:keys [progress status resource-id name] :as data}]
@ -163,7 +164,7 @@
(ptk/reify ::request-simple-export (ptk/reify ::request-simple-export
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(update state :export assoc :in-progress true)) (update state :export assoc :in-progress true :id uuid/zero))
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
@ -175,11 +176,17 @@
(->> (rp/query! :export-shapes-simple params) (->> (rp/query! :export-shapes-simple params)
(rx/map (fn [data] (rx/map (fn [data]
(dom/trigger-download filename data) (dom/trigger-download filename data)
(fn [state] (clear-export-state uuid/zero)))
(dissoc state :export)))))))))) (rx/catch (fn [cause]
(prn "KKKK" cause)
(rx/concat
(rx/of (clear-export-state uuid/zero))
(rx/throw cause))))))))))
(defn request-multiple-export (defn request-multiple-export
[{:keys [filename exports query] :as params}] [{:keys [filename exports query-name]
:or {query-name :export-shapes-multiple}
:as params}]
(ptk/reify ::request-multiple-export (ptk/reify ::request-multiple-export
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
@ -200,9 +207,9 @@
(rx/share)) (rx/share))
stoper stoper
(->> progress-stream
(rx/filter #(or (= "ended" (:status %)) (rx/filter #(or (= "ended" (:status %))
(= "error" (:status %)))))] (= "error" (:status %)))
progress-stream)]
(swap! st/ongoing-tasks conj :export) (swap! st/ongoing-tasks conj :export)
@ -212,11 +219,11 @@
;; Launch the exportation process and stores the resource id ;; Launch the exportation process and stores the resource id
;; locally. ;; locally.
(->> (rp/query! query params) (->> (rp/query! query-name params)
(rx/tap (fn [{:keys [id]}] (rx/tap (fn [{:keys [id]}]
(vreset! resource-id id))) (vreset! resource-id id)))
(rx/map (fn [{:keys [id]}] (rx/map (fn [{:keys [id]}]
(initialize-export-status exports filename id query)))) (initialize-export-status exports filename id query-name))))
;; We proceed to update the export state with incoming ;; We proceed to update the export state with incoming
;; progress updates. We delay the stoper for give some time ;; progress updates. We delay the stoper for give some time
@ -245,6 +252,7 @@
(ptk/reify ::retry-last-export (ptk/reify ::retry-last-export
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [{:keys [exports filename query]} (:export state)] (let [params (select-keys (:export state) [:filename :exports :query-name])]
(rx/of (request-multiple-export {:exports exports :filename filename :query query})))))) (when (seq params)
(rx/of (request-multiple-export params)))))))

View file

@ -126,7 +126,7 @@
[_ params] [_ params]
(send-export-command :cmd :export-shapes :params params :blob? false)) (send-export-command :cmd :export-shapes :params params :blob? false))
(defmethod query :export-frames (defmethod query :export-frames-multiple
[_ params] [_ params]
(send-export-command :cmd :export-frames :params (assoc params :uri (str base-uri)) :blob? false)) (send-export-command :cmd :export-frames :params (assoc params :uri (str base-uri)) :blob? false))

View file

@ -22,7 +22,7 @@
[rumext.alpha :as mf])) [rumext.alpha :as mf]))
(mf/defc export-multiple-dialog (mf/defc export-multiple-dialog
[{:keys [exports filename title query no-selection]}] [{:keys [exports filename title query-name no-selection]}]
(let [lstate (mf/deref refs/export) (let [lstate (mf/deref refs/export)
in-progress? (:in-progress lstate) in-progress? (:in-progress lstate)
@ -43,7 +43,10 @@
(fn [event] (fn [event]
(dom/prevent-default event) (dom/prevent-default event)
(st/emit! (modal/hide) (st/emit! (modal/hide)
(de/request-multiple-export {:filename filename :exports enabled-exports :query query}))) (de/request-multiple-export
{:filename filename
:exports enabled-exports
:query-name query-name})))
on-toggle-enabled on-toggle-enabled
(fn [index] (fn [index]
@ -143,14 +146,23 @@
::mf/register-as :export-shapes} ::mf/register-as :export-shapes}
[{:keys [exports filename]}] [{:keys [exports filename]}]
(let [title (tr "dashboard.export-shapes.title")] (let [title (tr "dashboard.export-shapes.title")]
(export-multiple-dialog {:exports exports :filename filename :title title :query :export-shapes-multiple :no-selection shapes-no-selection}))) [:& export-multiple-dialog
{:exports exports
:filename filename
:title title
:query-name :export-shapes-multiple
:no-selection shapes-no-selection}]))
(mf/defc export-frames (mf/defc export-frames
{::mf/register modal/components {::mf/register modal/components
::mf/register-as :export-frames} ::mf/register-as :export-frames}
[{:keys [exports filename]}] [{:keys [exports filename]}]
(let [title (tr "dashboard.export-frames.title")] (let [title (tr "dashboard.export-frames.title")]
(export-multiple-dialog {:exports exports :filename filename :title title :query :export-frames}))) [:& export-multiple-dialog
{:exports exports
:filename filename
:title title
:query-name :export-frames-multiple}]))
(mf/defc export-progress-widget (mf/defc export-progress-widget
{::mf/wrap [mf/memo]} {::mf/wrap [mf/memo]}