penpot/exporter/src/app/config.cljs
2026-07-23 15:21:52 +02:00

131 lines
4.0 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 INC Sucursal en España SL
(ns app.config
(:refer-clojure :exclude [get])
(:require
["node:buffer" :as buffer]
["node:crypto" :as crypto]
["node:process" :as process]
[app.common.data :as d]
[app.common.flags :as flags]
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.version :as v]
[cljs.core :as c]
[cuerdas.core :as str]))
(l/set-level! :info)
(def ^:private defaults
{:public-uri "http://localhost:3449"
;; :internal-uri nil ;; internal-uri cannot be nil
:tenant "default"
:host "localhost"
:http-server-port 6061
:http-server-host "0.0.0.0"
:tempdir "/tmp/penpot"
:redis-uri "redis://redis/0"})
(def ^:private schema:config
[:map {:title "config"}
[:secret-key :string]
[:public-uri {:optional true} ::sm/uri]
[:internal-uri {:optional true} ::sm/uri]
[:exporter-shared-key {:optional true} :string]
[:host {:optional true} :string]
[:tenant {:optional true} :string]
[:flags {:optional true} [::sm/set :keyword]]
[:redis-uri {:optional true} :string]
[:tempdir {:optional true} :string]
[:browser-pool-max {:optional true} ::sm/int]
[:browser-pool-min {:optional true} ::sm/int]
;; Headless WASM export (no browser). When true, `:is-wasm` exports render
;; via the in-process Skia/WASM pipeline. `:wasm-dir` points at the built
;; render-wasm artifact (render-wasm.js/.wasm).
[:wasm-headless {:optional true} :boolean]
[:wasm-dir {:optional true} :string]
;; Byte budget (in MB) for the WASM image cache; least-recently-used
;; images are evicted between requests once the store exceeds it.
[:wasm-image-cache-mb {:optional true} ::sm/int]
;; Expose unauthenticated /metrics (Prometheus text format) with
;; per-render instrumentation. Development/measurement aid; off by
;; default so production deployments are unaffected.
[:metrics {:optional true} :boolean]])
(def ^:private decode-config
(sm/decoder schema:config sm/string-transformer))
(def ^:private explain-config
(sm/explainer schema:config))
(def ^:private valid-config?
(sm/validator schema:config))
(defn- parse-flags
[config]
(flags/parse (:flags config)))
(defn- read-env
[prefix]
(let [env (unchecked-get process "env")
kwd (fn [s] (-> (str/kebab s) (str/keyword)))
prefix (str prefix "_")
len (count prefix)]
(reduce (fn [res key]
(let [val (unchecked-get env key)
key (str/lower key)]
(cond-> res
(str/starts-with? key prefix)
(assoc (kwd (subs key len)) val))))
{}
(js/Object.keys env))))
(defn- prepare-config
[]
(let [env (read-env "penpot")
env (d/without-nils env)
data (merge defaults env)
data (decode-config data)]
(when-not (valid-config? data)
(let [explain (explain-config data)]
(println (sm/humanize-explain explain))
(process/exit -1)))
data))
(def config
(prepare-config))
(def version
(v/parse "%version%"))
(def flags
(parse-flags config))
(defn get
"A configuration getter."
([key]
(c/get config key))
([key default]
(c/get config key default)))
(defn get-internal-uri
"Returns internal-uri if set, otherwise falls back to public-uri."
[]
(or (c/get config :internal-uri)
(c/get config :public-uri)))
(def management-key
(let [key (or (c/get config :exporter-shared-key)
(let [secret-key (c/get config :secret-key)
derived-key (crypto/hkdfSync "blake2b512" secret-key, "exporter" "" 32)]
(-> (.from buffer/Buffer derived-key)
(.toString "base64url"))))]
(l/inf :hint "exporter key initialized" :key (d/obfuscate-string key))
key))