♻️ Remove koa dependency from exporter.

Replaced it with a direct usage of node http
server api and some external helpers for body
and cookies parsing.
This commit is contained in:
Andrey Antukh 2021-09-21 15:12:46 +02:00 committed by Andrés Moya
parent d246db7be8
commit bee47d7fda
6 changed files with 203 additions and 351 deletions

View file

@ -7,9 +7,12 @@
(ns app.config
(:refer-clojure :exclude [get])
(:require
["fs" :as fs]
["process" :as process]
[app.common.exceptions :as ex]
[app.common.data :as d]
[app.common.spec :as us]
[app.common.version :as v]
[cljs.core :as c]
[cljs.pprint]
[cljs.spec.alpha :as s]
@ -22,11 +25,11 @@
:browser-concurrency 5
:browser-strategy :incognito})
(s/def ::browser-executable-path ::us/string)
(s/def ::public-uri ::us/string)
(s/def ::http-server-port ::us/integer)
(s/def ::browser-concurrency ::us/integer)
(s/def ::browser-executable-path ::us/string)
(s/def ::browser-strategy ::us/keyword)
(s/def ::http-server-port ::us/integer)
(s/def ::public-uri ::us/string)
(s/def ::config
(s/keys :opt-un [::public-uri
@ -60,6 +63,11 @@
(atom (prepare-config)))
(def version
(v/parse (or (some-> (ex/ignoring (fs/readFileSync "version.txt"))
(str/trim))
"%version%")))
(defn get
"A configuration getter."
([key]

View file

@ -10,6 +10,8 @@
[app.http.export :refer [export-handler]]
[app.http.export-frames :refer [export-frames-handler]]
[app.http.impl :as impl]
[app.util.transit :as t]
[cuerdas.core :as str]
[lambdaisland.glogi :as log]
[promesa.core :as p]
[reitit.core :as r]))
@ -20,13 +22,45 @@
(def instance (atom nil))
(defn- on-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 {"content-type" "text/html"}
:body (str "<pre style='font-size:16px'>" (:explain data) "</pre>\n")}))
(and (= :internal type)
(= :browser-not-ready code))
{:status 503
:headers {"x-error" (t/encode data)}
:body ""}
:else
(do
(log/error :msg "Unexpected error" :error error)
(js/console.error error)
{:status 500
:headers {"x-error" (t/encode data)}
:body ""}))))
(defn init
[]
(let [router (r/router routes)
handler (impl/handler router)
server (impl/server handler)
handler (impl/router-handler router)
server (impl/server handler on-error)
port (cf/get :http-server-port 6061)]
(.listen server port)
(log/info :msg "welcome to penpot"
:module "exporter"
:version (:full cf/version))
(log/info :msg "starting http server" :port port)
(reset! instance server)))

View file

@ -7,8 +7,8 @@
(ns app.http.impl
(:require
["http" :as http]
["cookies" :as Cookies]
["inflation" :as inflate]
["koa" :as koa]
["raw-body" :as raw-body]
[app.util.transit :as t]
[cuerdas.core :as str]
@ -17,94 +17,64 @@
[promesa.core :as p]
[reitit.core :as r]))
(def methods-with-body
#{"POST" "PUT" "DELETE"})
(defn- match
[router ctx]
(let [uri (u/uri (unchecked-get ctx "originalUrl"))]
(when-let [match (r/match-by-path router (:path uri))]
(assoc match :query-params (u/query-string->map (:query uri))))))
(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 {"content-type" "text/html"}
:body (str "<pre style='font-size:16px'>" (:explain data) "</pre>\n")}))
(and (= :internal type)
(= :browser-not-ready code))
{:status 503
:headers {"x-error" (t/encode data)}
:body ""}
:else
(do
(log/error :msg "Unexpected error"
:error error)
(js/console.error error)
{:status 500
:headers {"x-error" (t/encode data)}
:body ""}))))
[router {:keys [path query] :as request}]
(when-let [match (r/match-by-path router path)]
(assoc match :query-params (u/query-string->map query))))
(defn- handle-response
[ctx {:keys [body headers status] :or {headers {} status 200}}]
(run! (fn [[k v]] (.set ^js ctx k v)) headers)
(set! (.-body ^js ctx) body)
(set! (.-status ^js ctx) status)
nil)
[req res]
(fn [{:keys [body headers status] :or {headers {} status 200}}]
(.writeHead ^js res status (clj->js headers))
(.end ^js res body)))
(defn- parse-headers
[ctx]
(let [orig (unchecked-get ctx "headers")]
[req]
(let [orig (unchecked-get req "headers")]
(persistent!
(reduce #(assoc! %1 %2 (unchecked-get orig %2))
(transient {})
(js/Object.keys orig)))))
(def parse-body? #{"POST" "PUT" "DELETE"})
(defn- parse-body
[ctx]
(let [headers (unchecked-get ctx "headers")
ctype (unchecked-get headers "content-type")]
(when (parse-body? (.-method ^js ctx))
(-> (inflate (.-req ^js ctx))
(raw-body #js {:limit "5mb" :encoding "utf8"})
[req]
(let [headers (unchecked-get req "headers")
method (unchecked-get req "method")
ctype (unchecked-get headers "content-type")
opts #js {:limit "5mb" :encoding "utf8"}]
(when (contains? methods-with-body method)
(-> (raw-body (inflate req) opts)
(p/then (fn [data]
(cond-> data
(= ctype "application/transit+json")
(t/decode))))))))
(cond-> data
(= ctype "application/transit+json")
(t/decode))))))))
(defn- wrap-handler
[f]
(fn [ctx]
(p/let [cookies (unchecked-get ctx "cookies")
headers (parse-headers ctx)
body (parse-body ctx)
request {:method (str/lower (unchecked-get ctx "method"))
:body body
:ctx ctx
:headers headers
:cookies cookies}]
(-> (p/do! (f request))
(p/then (fn [rsp]
(when (map? rsp)
(handle-response ctx rsp))))
(p/catch (fn [err]
(->> (handle-error err request)
(handle-response ctx))))))))
(defn- handler-adapter
[handler on-error]
(fn [req res]
(let [cookies (new Cookies req res)
headers (parse-headers req)
uri (u/uri (unchecked-get req "url"))
request {:method (str/lower (unchecked-get req "method"))
:path (:path uri)
:query (:query uri)
:url uri
:headers headers
:cookies cookies}]
(-> (parse-body req)
(p/then (fn [body]
(let [request (assoc request :body body)]
(handler request))))
(p/catch (fn [error] (on-error error request)))
(p/then (handle-response req res))))))
(defn- router-handler
(defn router-handler
[router]
(fn [{:keys [ctx body] :as request}]
(let [route (match router ctx)
(fn [{:keys [body] :as request}]
(let [route (match router request)
params (merge {}
(:query-params route)
(:path-params route)
@ -120,16 +90,5 @@
:body "Not found"}))))
(defn server
[handler]
(.createServer http @handler))
(defn handler
[router]
(let [instance (doto (new koa)
(.use (-> (router-handler router)
(wrap-handler))))]
(specify! instance
cljs.core/IDeref
(-deref [_]
(.callback instance)))))
[handler on-error]
(.createServer ^js http (handler-adapter handler on-error)))