penpot/exporter/src/app/http/export.cljs
2021-01-29 09:53:55 +01:00

123 lines
3.9 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/.
;;
;; 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.http.export
(:require
[app.http.export-bitmap :as bitmap]
[app.http.export-svg :as svg]
[app.zipfile :as zip]
[cljs.spec.alpha :as s]
[cuerdas.core :as str]
[lambdaisland.glogi :as log]
[promesa.core :as p]
[app.common.exceptions :as exc :include-macros true]
[app.common.spec :as us]))
(s/def ::name ::us/string)
(s/def ::page-id ::us/uuid)
(s/def ::file-id ::us/uuid)
(s/def ::object-id ::us/uuid)
(s/def ::scale ::us/number)
(s/def ::suffix ::us/string)
(s/def ::type ::us/keyword)
(s/def ::suffix string?)
(s/def ::scale number?)
(s/def ::export (s/keys :req-un [::type ::suffix ::scale]))
(s/def ::exports (s/coll-of ::export :kind vector?))
(s/def ::handler-params
(s/keys :req-un [::page-id ::file-id ::object-id ::name ::exports]))
(declare handle-single-export)
(declare handle-multiple-export)
(declare perform-export)
(declare attach-filename)
(defn export-handler
[{:keys [params browser cookies] :as request}]
(let [{:keys [exports page-id file-id object-id name]} (us/conform ::handler-params params)
token (.get ^js cookies "auth-token")]
(case (count exports)
0 (exc/raise :type :validation :code :missing-exports)
1 (handle-single-export
request
(assoc (first exports)
:name name
:token token
:file-id file-id
:page-id page-id
:object-id object-id))
(handle-multiple-export
request
(map (fn [item]
(assoc item
:name name
:token token
:file-id file-id
:page-id page-id
:object-id object-id)) exports)))))
(defn- handle-single-export
[{:keys [browser]} params]
(p/let [result (perform-export browser params)]
{:status 200
:body (:content result)
:headers {"content-type" (:mime-type result)
"content-length" (:length result)}}))
(defn- handle-multiple-export
[{:keys [browser]} exports]
(let [proms (->> exports
(attach-filename)
(map (partial perform-export browser)))]
(-> (p/all proms)
(p/then (fn [results]
(reduce #(zip/add! %1 (:filename %2) (:content %2)) (zip/create) results)))
(p/then (fn [fzip]
{:status 200
:headers {"content-type" "application/zip"}
:body (.generateNodeStream ^js fzip)})))))
(defn- perform-export
[browser params]
(case (:type params)
:png (bitmap/export browser params)
:jpeg (bitmap/export browser params)
:svg (svg/export browser params)))
(defn- find-filename-candidate
[params used]
(loop [index 0]
(let [candidate (str (:name params)
(:suffix params "")
(when (pos? index)
(str "-" (inc index)))
(case (:type params)
:png ".png"
:jpeg ".jpg"
:svg ".svg"))]
(if (contains? used candidate)
(recur (inc index))
candidate))))
(defn- attach-filename
[exports]
(loop [exports (seq exports)
used #{}
result []]
(if (nil? exports)
result
(let [export (first exports)
candidate (find-filename-candidate export used)
export (assoc export :filename candidate)]
(recur (next exports)
(conj used candidate)
(conj result export))))))