diff --git a/common/uxbox/common/spec.cljc b/common/uxbox/common/spec.cljc index 187adcb37..920f8f1b8 100644 --- a/common/uxbox/common/spec.cljc +++ b/common/uxbox/common/spec.cljc @@ -9,7 +9,6 @@ (:refer-clojure :exclude [assert]) #?(:cljs (:require-macros [uxbox.common.spec :refer [assert]])) (:require - #?(:clj [datoteka.core :as fs]) #?(:clj [clojure.spec.alpha :as s] :cljs [cljs.spec.alpha :as s]) [expound.alpha :as expound] @@ -85,15 +84,6 @@ v ::s/invalid)) -#?(:clj - (defn path-conformer - [v] - (cond - (string? v) (fs/path v) - (fs/path? v) v - :else ::s/invalid))) - - ;; --- Default Specs (s/def ::inst inst?) @@ -107,7 +97,6 @@ (s/def ::not-empty-string (s/and string? #(not (str/empty? %)))) (s/def ::url string?) (s/def ::fn fn?) -#?(:clj (s/def ::path (s/conformer path-conformer str))) ;; --- Macros diff --git a/common/uxbox/common/uuid_impl.js b/common/uxbox/common/uuid_impl.js index 677a698a0..88c1db3dd 100644 --- a/common/uxbox/common/uuid_impl.js +++ b/common/uxbox/common/uuid_impl.js @@ -20,8 +20,13 @@ function fill(buf) { return buf; }; +// NOTE: NodeJS compatibility +if (typeof require === "function") { + global.crypto = require("crypto"); +} + 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) { for (let i = 0, r; i < buf.length; i++) { diff --git a/exporter/shadow-cljs.edn b/exporter/shadow-cljs.edn index 3fd9962bc..c5d4f9c60 100644 --- a/exporter/shadow-cljs.edn +++ b/exporter/shadow-cljs.edn @@ -1,9 +1,12 @@ {:dependencies [[funcool/promesa "5.1.0"] + [danlentz/clj-uuid "0.1.9"] [funcool/cuerdas "2020.03.26-3"] [lambdaisland/glogi "1.0.63"] - [metosin/reitit-core "0.5.2"]] - :source-paths ["src"] + [metosin/reitit-core "0.5.2"] + [com.cognitect/transit-cljs "0.8.264"]] + + :source-paths ["src" "../common"] :nrepl {:port 3497} :builds diff --git a/exporter/src/app/http.cljs b/exporter/src/app/http.cljs index fef416e13..ee6273342 100644 --- a/exporter/src/app/http.cljs +++ b/exporter/src/app/http.cljs @@ -4,7 +4,9 @@ [lambdaisland.glogi :as log] [app.browser :as bwr] [app.http.screenshot :refer [bitmap-handler]] + [app.util.transit :as t] [reitit.core :as r] + [cuerdas.core :as str] ["koa" :as koa] ["http" :as http]) (:import @@ -29,6 +31,31 @@ :params params :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 "
" (:explain data) "
\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 [ctx {:keys [body headers status] :or {headers {} status 200}}] (run! (fn [[k v]] (.set ^js ctx k v)) headers) @@ -36,15 +63,30 @@ (set! (.-status ^js ctx) status) 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 [f extra] (fn [ctx] (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/then (fn [rsp] - (when (map? rsp) - (handle-response ctx rsp)))))))) + (p/then (fn [rsp] + (when (map? rsp) + (handle-response ctx rsp)))) + (p/catch (fn [err] + (->> (handle-error err request) + (handle-response ctx)))))))) (def routes [["/export" diff --git a/exporter/src/app/http/screenshot.cljs b/exporter/src/app/http/screenshot.cljs index efd94b5ec..d8582f3ed 100644 --- a/exporter/src/app/http/screenshot.cljs +++ b/exporter/src/app/http/screenshot.cljs @@ -2,7 +2,10 @@ (:require [app.browser :as bwr] [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 [page url cookie] @@ -27,13 +30,17 @@ (load-and-screenshot page url cookie)))] (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 [{:keys [params browser cookies] :as request}] - (let [page-id (get-in params [:query :page-id]) - object-id (get-in params [:query :object-id]) - token (.get ^js cookies "auth-token")] - (-> (take-screenshot browser {:page-id page-id - :object-id object-id + (let [params (us/conform ::bitmap-handler-params (:query params)) + token (.get ^js cookies "auth-token")] + (-> (take-screenshot browser {:page-id (:page-id params) + :object-id (:object-id params) :token token}) (p/then (fn [result] {:status 200 diff --git a/exporter/src/app/util/transit.cljs b/exporter/src/app/util/transit.cljs new file mode 100644 index 000000000..1f3bfd9b8 --- /dev/null +++ b/exporter/src/app/util/transit.cljs @@ -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))) diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/options.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/options.cljs index afd564787..b4a1b086b 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/options.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/options.cljs @@ -14,6 +14,7 @@ [cuerdas.core :as str] [rumext.alpha :as mf] [uxbox.common.spec :as us] + [uxbox.main.data.messages :as dm] [uxbox.main.data.workspace :as udw] [uxbox.main.refs :as refs] [uxbox.main.store :as st] @@ -67,8 +68,10 @@ (swap! loading? not) (->> (request-screenshot (:id page) (:id shape)) (rx/subs - (fn [{:keys [status body]}] - (trigger-download (:name shape) body)) + (fn [{:keys [status body] :as response}] + (if (= status 200) + (trigger-download (:name shape) body) + (st/emit! (dm/error (tr "errors.unexpected-error"))))) (constantly nil) (fn [] (swap! loading? not)))))]