mirror of
https://github.com/penpot/penpot.git
synced 2025-03-12 07:41:43 -05:00
🎉 Add basic error handling to exporter app.
This commit is contained in:
parent
20c4b46930
commit
a1f0625bec
7 changed files with 107 additions and 26 deletions
|
@ -9,7 +9,6 @@
|
||||||
(:refer-clojure :exclude [assert])
|
(:refer-clojure :exclude [assert])
|
||||||
#?(:cljs (:require-macros [uxbox.common.spec :refer [assert]]))
|
#?(:cljs (:require-macros [uxbox.common.spec :refer [assert]]))
|
||||||
(:require
|
(:require
|
||||||
#?(:clj [datoteka.core :as fs])
|
|
||||||
#?(:clj [clojure.spec.alpha :as s]
|
#?(:clj [clojure.spec.alpha :as s]
|
||||||
:cljs [cljs.spec.alpha :as s])
|
:cljs [cljs.spec.alpha :as s])
|
||||||
[expound.alpha :as expound]
|
[expound.alpha :as expound]
|
||||||
|
@ -85,15 +84,6 @@
|
||||||
v
|
v
|
||||||
::s/invalid))
|
::s/invalid))
|
||||||
|
|
||||||
#?(:clj
|
|
||||||
(defn path-conformer
|
|
||||||
[v]
|
|
||||||
(cond
|
|
||||||
(string? v) (fs/path v)
|
|
||||||
(fs/path? v) v
|
|
||||||
:else ::s/invalid)))
|
|
||||||
|
|
||||||
|
|
||||||
;; --- Default Specs
|
;; --- Default Specs
|
||||||
|
|
||||||
(s/def ::inst inst?)
|
(s/def ::inst inst?)
|
||||||
|
@ -107,7 +97,6 @@
|
||||||
(s/def ::not-empty-string (s/and string? #(not (str/empty? %))))
|
(s/def ::not-empty-string (s/and string? #(not (str/empty? %))))
|
||||||
(s/def ::url string?)
|
(s/def ::url string?)
|
||||||
(s/def ::fn fn?)
|
(s/def ::fn fn?)
|
||||||
#?(:clj (s/def ::path (s/conformer path-conformer str)))
|
|
||||||
|
|
||||||
;; --- Macros
|
;; --- Macros
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,13 @@ function fill(buf) {
|
||||||
return buf;
|
return buf;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// NOTE: NodeJS compatibility
|
||||||
|
if (typeof require === "function") {
|
||||||
|
global.crypto = require("crypto");
|
||||||
|
}
|
||||||
|
|
||||||
if (global.crypto === undefined) {
|
if (global.crypto === undefined) {
|
||||||
console.warn("No high quality RNG available, switching back to Math.random.", platform);
|
console.warn("No high quality RNG available, switching back to Math.random.");
|
||||||
|
|
||||||
fill = function(buf) {
|
fill = function(buf) {
|
||||||
for (let i = 0, r; i < buf.length; i++) {
|
for (let i = 0, r; i < buf.length; i++) {
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
{:dependencies
|
{:dependencies
|
||||||
[[funcool/promesa "5.1.0"]
|
[[funcool/promesa "5.1.0"]
|
||||||
|
[danlentz/clj-uuid "0.1.9"]
|
||||||
[funcool/cuerdas "2020.03.26-3"]
|
[funcool/cuerdas "2020.03.26-3"]
|
||||||
[lambdaisland/glogi "1.0.63"]
|
[lambdaisland/glogi "1.0.63"]
|
||||||
[metosin/reitit-core "0.5.2"]]
|
[metosin/reitit-core "0.5.2"]
|
||||||
:source-paths ["src"]
|
[com.cognitect/transit-cljs "0.8.264"]]
|
||||||
|
|
||||||
|
:source-paths ["src" "../common"]
|
||||||
:nrepl {:port 3497}
|
:nrepl {:port 3497}
|
||||||
|
|
||||||
:builds
|
:builds
|
||||||
|
|
|
@ -4,7 +4,9 @@
|
||||||
[lambdaisland.glogi :as log]
|
[lambdaisland.glogi :as log]
|
||||||
[app.browser :as bwr]
|
[app.browser :as bwr]
|
||||||
[app.http.screenshot :refer [bitmap-handler]]
|
[app.http.screenshot :refer [bitmap-handler]]
|
||||||
|
[app.util.transit :as t]
|
||||||
[reitit.core :as r]
|
[reitit.core :as r]
|
||||||
|
[cuerdas.core :as str]
|
||||||
["koa" :as koa]
|
["koa" :as koa]
|
||||||
["http" :as http])
|
["http" :as http])
|
||||||
(:import
|
(:import
|
||||||
|
@ -29,6 +31,31 @@
|
||||||
:params params
|
:params params
|
||||||
:query-params qparams)))))
|
:query-params qparams)))))
|
||||||
|
|
||||||
|
(defn- handle-error
|
||||||
|
[error request]
|
||||||
|
(let [{:keys [type message code] :as data} (ex-data error)]
|
||||||
|
(cond
|
||||||
|
(= :validation type)
|
||||||
|
(let [header (get-in request [:headers "accept"])]
|
||||||
|
(if (and (str/starts-with? header "text/html")
|
||||||
|
(= :spec-validation (:code data)))
|
||||||
|
{:status 400
|
||||||
|
:headers {"content-type" "text/html"}
|
||||||
|
:body (str "<pre style='font-size:16px'>" (:explain data) "</pre>\n")}
|
||||||
|
{:status 400
|
||||||
|
:headers {"x-metadata" (t/encode data)}
|
||||||
|
:body ""}))
|
||||||
|
|
||||||
|
:else
|
||||||
|
(do
|
||||||
|
(log/error :msg "Unexpected error"
|
||||||
|
:error error)
|
||||||
|
{:status 500
|
||||||
|
:headers {"x-metadata" (t/encode {:type :unexpected
|
||||||
|
:message (ex-message error)})}
|
||||||
|
:body ""}))))
|
||||||
|
|
||||||
|
|
||||||
(defn- handle-response
|
(defn- handle-response
|
||||||
[ctx {:keys [body headers status] :or {headers {} status 200}}]
|
[ctx {:keys [body headers status] :or {headers {} status 200}}]
|
||||||
(run! (fn [[k v]] (.set ^js ctx k v)) headers)
|
(run! (fn [[k v]] (.set ^js ctx k v)) headers)
|
||||||
|
@ -36,15 +63,30 @@
|
||||||
(set! (.-status ^js ctx) status)
|
(set! (.-status ^js ctx) status)
|
||||||
nil)
|
nil)
|
||||||
|
|
||||||
|
(defn- parse-headers
|
||||||
|
[ctx]
|
||||||
|
(let [orig (unchecked-get ctx "headers")]
|
||||||
|
(persistent!
|
||||||
|
(reduce #(assoc! %1 %2 (unchecked-get orig %2))
|
||||||
|
(transient {})
|
||||||
|
(js/Object.keys orig)))))
|
||||||
|
|
||||||
(defn- wrap-handler
|
(defn- wrap-handler
|
||||||
[f extra]
|
[f extra]
|
||||||
(fn [ctx]
|
(fn [ctx]
|
||||||
(let [cookies (unchecked-get ctx "cookies")
|
(let [cookies (unchecked-get ctx "cookies")
|
||||||
request (assoc extra :ctx ctx :cookies cookies)]
|
headers (parse-headers ctx)
|
||||||
|
request (assoc extra
|
||||||
|
:ctx ctx
|
||||||
|
:headers headers
|
||||||
|
:cookies cookies)]
|
||||||
(-> (p/do! (f request))
|
(-> (p/do! (f request))
|
||||||
(p/then (fn [rsp]
|
(p/then (fn [rsp]
|
||||||
(when (map? rsp)
|
(when (map? rsp)
|
||||||
(handle-response ctx rsp))))))))
|
(handle-response ctx rsp))))
|
||||||
|
(p/catch (fn [err]
|
||||||
|
(->> (handle-error err request)
|
||||||
|
(handle-response ctx))))))))
|
||||||
|
|
||||||
(def routes
|
(def routes
|
||||||
[["/export"
|
[["/export"
|
||||||
|
|
|
@ -2,7 +2,10 @@
|
||||||
(:require
|
(:require
|
||||||
[app.browser :as bwr]
|
[app.browser :as bwr]
|
||||||
[app.config :as cfg]
|
[app.config :as cfg]
|
||||||
[promesa.core :as p]))
|
[cljs.spec.alpha :as s]
|
||||||
|
[promesa.core :as p]
|
||||||
|
[uxbox.common.exceptions :as exc :include-macros true]
|
||||||
|
[uxbox.common.spec :as us]))
|
||||||
|
|
||||||
(defn- load-and-screenshot
|
(defn- load-and-screenshot
|
||||||
[page url cookie]
|
[page url cookie]
|
||||||
|
@ -27,13 +30,17 @@
|
||||||
(load-and-screenshot page url cookie)))]
|
(load-and-screenshot page url cookie)))]
|
||||||
(bwr/exec! browser on-browser)))
|
(bwr/exec! browser on-browser)))
|
||||||
|
|
||||||
|
(s/def ::page-id ::us/uuid)
|
||||||
|
(s/def ::object-id ::us/uuid)
|
||||||
|
(s/def ::bitmap-handler-params
|
||||||
|
(s/keys :req-un [::page-id ::object-id]))
|
||||||
|
|
||||||
(defn bitmap-handler
|
(defn bitmap-handler
|
||||||
[{:keys [params browser cookies] :as request}]
|
[{:keys [params browser cookies] :as request}]
|
||||||
(let [page-id (get-in params [:query :page-id])
|
(let [params (us/conform ::bitmap-handler-params (:query params))
|
||||||
object-id (get-in params [:query :object-id])
|
|
||||||
token (.get ^js cookies "auth-token")]
|
token (.get ^js cookies "auth-token")]
|
||||||
(-> (take-screenshot browser {:page-id page-id
|
(-> (take-screenshot browser {:page-id (:page-id params)
|
||||||
:object-id object-id
|
:object-id (:object-id params)
|
||||||
:token token})
|
:token token})
|
||||||
(p/then (fn [result]
|
(p/then (fn [result]
|
||||||
{:status 200
|
{:status 200
|
||||||
|
|
32
exporter/src/app/util/transit.cljs
Normal file
32
exporter/src/app/util/transit.cljs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
;; 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/.
|
||||||
|
;;
|
||||||
|
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
|
;; defined by the Mozilla Public License, v. 2.0.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) 2020 UXBOX Labs SL
|
||||||
|
|
||||||
|
(ns app.util.transit
|
||||||
|
(:require
|
||||||
|
[cognitect.transit :as t]))
|
||||||
|
|
||||||
|
;; --- Transit Handlers
|
||||||
|
|
||||||
|
(def ^:private +read-handlers+
|
||||||
|
{"u" uuid})
|
||||||
|
|
||||||
|
(def ^:private +write-handlers+
|
||||||
|
{})
|
||||||
|
|
||||||
|
;; --- Public Api
|
||||||
|
|
||||||
|
(defn decode
|
||||||
|
[data]
|
||||||
|
(let [r (t/reader :json {:handlers +read-handlers+})]
|
||||||
|
(t/read r data)))
|
||||||
|
|
||||||
|
(defn encode
|
||||||
|
[data]
|
||||||
|
(let [w (t/writer :json {:handlers +write-handlers+})]
|
||||||
|
(t/write w data)))
|
|
@ -14,6 +14,7 @@
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[rumext.alpha :as mf]
|
[rumext.alpha :as mf]
|
||||||
[uxbox.common.spec :as us]
|
[uxbox.common.spec :as us]
|
||||||
|
[uxbox.main.data.messages :as dm]
|
||||||
[uxbox.main.data.workspace :as udw]
|
[uxbox.main.data.workspace :as udw]
|
||||||
[uxbox.main.refs :as refs]
|
[uxbox.main.refs :as refs]
|
||||||
[uxbox.main.store :as st]
|
[uxbox.main.store :as st]
|
||||||
|
@ -67,8 +68,10 @@
|
||||||
(swap! loading? not)
|
(swap! loading? not)
|
||||||
(->> (request-screenshot (:id page) (:id shape))
|
(->> (request-screenshot (:id page) (:id shape))
|
||||||
(rx/subs
|
(rx/subs
|
||||||
(fn [{:keys [status body]}]
|
(fn [{:keys [status body] :as response}]
|
||||||
(trigger-download (:name shape) body))
|
(if (= status 200)
|
||||||
|
(trigger-download (:name shape) body)
|
||||||
|
(st/emit! (dm/error (tr "errors.unexpected-error")))))
|
||||||
(constantly nil)
|
(constantly nil)
|
||||||
(fn []
|
(fn []
|
||||||
(swap! loading? not)))))]
|
(swap! loading? not)))))]
|
||||||
|
|
Loading…
Add table
Reference in a new issue