mirror of
https://github.com/penpot/penpot.git
synced 2026-08-25 14:18:40 +00:00
112 lines
4.5 KiB
Clojure
112 lines
4.5 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/.
|
|
;;
|
|
;; Copyright (c) KALEIDOS SUBSIDIARY SL
|
|
|
|
(ns app.media
|
|
"Media & Font postprocessing.
|
|
|
|
This namespace is the dispatch layer only. Processing implementations
|
|
live in two separate namespaces, each owning their own defmulti:
|
|
|
|
app.media.local — shell/ImageMagick/FontForge implementations
|
|
app.media.remote — HTTP delegation to media-processor service
|
|
|
|
Validation and schemas live in app.media.validation (leaf namespace,
|
|
no circular dep). When adding a new :cmd type, add defmethods in
|
|
BOTH local and remote."
|
|
(:require
|
|
[app.common.data :as d]
|
|
[app.common.exceptions :as ex]
|
|
[app.config :as cf]
|
|
[app.db :as-alias db]
|
|
[app.http.client :as http]
|
|
[app.media.local :as media.local]
|
|
[app.media.remote :as media.remote]
|
|
[app.media.sanitize :as sanitize]
|
|
[app.media.validation :as validation]
|
|
[app.storage :as-alias sto]
|
|
[app.storage.tmp :as tmp]
|
|
[cuerdas.core :as str]
|
|
[datoteka.io :as io]))
|
|
|
|
(defn run
|
|
[system params]
|
|
(if (contains? cf/flags :remote-media-processing)
|
|
(media.remote/process system params)
|
|
(media.local/process system params)))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; IMAGE HELPERS
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(defn download-image
|
|
"Download an image from the provided URI and return the media input object"
|
|
[{:keys [::http/client]} uri]
|
|
(letfn [(parse-and-validate [{:keys [status headers] :as response}]
|
|
(let [size (some-> (get headers "content-length") d/parse-integer)
|
|
mtype (get headers "content-type")]
|
|
|
|
(when-not (<= 200 status 299)
|
|
(ex/raise :type :validation
|
|
:code :unable-to-download-image
|
|
:hint (str/ffmt "unable to download image from '%': unexpected status code %" uri status)))
|
|
|
|
(when-not size
|
|
(ex/raise :type :validation
|
|
:code :unknown-size
|
|
:hint "seems like the url points to resource with unknown size"))
|
|
|
|
(-> {:size size :mtype mtype}
|
|
(validation/validate-media-type!)
|
|
(validation/validate-media-size!))))]
|
|
|
|
(let [{:keys [body] :as response}
|
|
(try
|
|
(http/req-with-redirects
|
|
client
|
|
{:method :get :uri uri}
|
|
{:response-type :input-stream
|
|
:max-redirects 3})
|
|
(catch java.net.ConnectException cause
|
|
(ex/raise :type :validation
|
|
:code :unable-to-download-image
|
|
:hint (str/ffmt "unable to download image from '%': connection refused or host unreachable" uri)
|
|
:cause cause))
|
|
(catch java.net.http.HttpConnectTimeoutException cause
|
|
(ex/raise :type :validation
|
|
:code :unable-to-download-image
|
|
:hint (str/ffmt "unable to download image from '%': connection timeout" uri)
|
|
:cause cause))
|
|
(catch java.net.http.HttpTimeoutException cause
|
|
(ex/raise :type :validation
|
|
:code :unable-to-download-image
|
|
:hint (str/ffmt "unable to download image from '%': request timeout" uri)
|
|
:cause cause))
|
|
(catch java.io.IOException cause
|
|
(ex/raise :type :validation
|
|
:code :unable-to-download-image
|
|
:hint (str/ffmt "unable to download image from '%': I/O error" uri)
|
|
:cause cause)))]
|
|
|
|
(if body
|
|
(with-open [body body]
|
|
(let [{:keys [size mtype]} (parse-and-validate response)
|
|
path (tmp/tempfile :prefix "penpot.media.download.")
|
|
written (io/write* path body :size size)]
|
|
|
|
(when (not= written size)
|
|
(ex/raise :type :internal
|
|
:code :mismatch-write-size
|
|
:hint "unexpected state: unable to write to file"))
|
|
|
|
;; Sanitize: strip trailing data after image EOF markers
|
|
(let [new-size (sanitize/truncate-after-eof path mtype)]
|
|
{:path path
|
|
:mtype mtype
|
|
:size new-size})))
|
|
|
|
;; No body - validation will raise appropriate error
|
|
(parse-and-validate response)))))
|