Add more adaptations to make app run in a prefixed path.

This commit is contained in:
Andrey Antukh 2021-04-19 18:35:36 +02:00 committed by Andrés Moya
parent 2828ccda7f
commit 55ea84a056
14 changed files with 115 additions and 98 deletions

View file

@ -0,0 +1,34 @@
;; 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) UXBOX Labs SL
(ns app.common.uri
(:require
[app.common.data :as d]
[lambdaisland.uri :as u]
[lambdaisland.uri.normalize :as un]))
(d/export u/uri)
(d/export u/join)
(d/export un/percent-encode)
(defn query-string->map
[s]
(u/query-string->map s))
(defn default-encode-value
[v]
(if (keyword? v) (name v) v))
(defn map->query-string
([params] (map->query-string params nil))
([params {:keys [value-fn key-fn]
:or {value-fn default-encode-value
key-fn identity}}]
(->> params
(into {} (comp
(remove #(nil? (second %)))
(map (fn [[k v]] [(key-fn k) (value-fn v)]))))
(u/map->query-string))))

View file

@ -68,23 +68,23 @@ function readManifest() {
const content = JSON.parse(fs.readFileSync(path, {encoding: "utf8"})); const content = JSON.parse(fs.readFileSync(path, {encoding: "utf8"}));
const index = { const index = {
"config": "/js/config.js?ts=" + Date.now(), "config": "js/config.js?ts=" + Date.now(),
"polyfills": "js/polyfills.js?ts=" + Date.now(), "polyfills": "js/polyfills.js?ts=" + Date.now(),
}; };
for (let item of content) { for (let item of content) {
index[item.name] = "/js/" + item["output-name"]; index[item.name] = "js/" + item["output-name"];
}; };
return index; return index;
} catch (e) { } catch (e) {
console.error("Error on reading manifest, using default."); console.error("Error on reading manifest, using default.");
return { return {
"config": "/js/config.js", "config": "js/config.js",
"polyfills": "js/polyfills.js", "polyfills": "js/polyfills.js",
"main": "/js/main.js", "main": "js/main.js",
"shared": "/js/shared.js", "shared": "js/shared.js",
"worker": "/js/worker.js" "worker": "js/worker.js"
}; };
} }
} }

View file

@ -15,10 +15,9 @@
<meta name="twitter:image" content="https://penpot.app/images/workspace-ui.jpg"> <meta name="twitter:image" content="https://penpot.app/images/workspace-ui.jpg">
<meta name="twitter:site" content="@penpotapp"> <meta name="twitter:site" content="@penpotapp">
<meta name="twitter:creator" content="@penpotapp"> <meta name="twitter:creator" content="@penpotapp">
<link id="theme" href="/css/main-{{& th}}.css?ts={{& ts}}" <link id="theme" href="css/main-{{& th}}.css?ts={{& ts}}" rel="stylesheet" type="text/css" />
rel="stylesheet" type="text/css" />
<link rel="icon" href="/images/favicon.png" /> <link rel="icon" href="images/favicon.png" />
<script> <script>
window.penpotTranslations = JSON.parse({{& translations}}); window.penpotTranslations = JSON.parse({{& translations}});

View file

@ -6,14 +6,15 @@
(ns app.config (ns app.config
(:require (:require
[clojure.spec.alpha :as s]
[app.common.data :as d] [app.common.data :as d]
[app.common.uri :as u]
[app.common.spec :as us] [app.common.spec :as us]
[app.common.version :as v] [app.common.version :as v]
[app.util.avatars :as avatars]
[app.util.dom :as dom]
[app.util.globals :refer [global location]] [app.util.globals :refer [global location]]
[app.util.object :as obj] [app.util.object :as obj]
[app.util.dom :as dom] [clojure.spec.alpha :as s]
[app.util.avatars :as avatars]
[cuerdas.core :as str])) [cuerdas.core :as str]))
;; --- Auxiliar Functions ;; --- Auxiliar Functions
@ -76,16 +77,24 @@
(def translations (obj/get global "penpotTranslations")) (def translations (obj/get global "penpotTranslations"))
(def themes (obj/get global "penpotThemes")) (def themes (obj/get global "penpotThemes"))
(def public-uri (or (obj/get global "penpotPublicURI") (.-origin ^js location)))
(def version (delay (parse-version global))) (def version (delay (parse-version global)))
(def target (delay (parse-target global))) (def target (delay (parse-target global)))
(def browser (delay (parse-browser))) (def browser (delay (parse-browser)))
(def platform (delay (parse-platform))) (def platform (delay (parse-platform)))
(def public-uri
(let [uri (u/uri (or (obj/get global "penpotPublicURI")
(str (.-origin ^js location)
(.-pathname ^js location))))]
;; Ensure that the path always ends with "/"; this ensures that
;; all path join operations works as expected.
(cond-> uri
(not (str/ends-with? (:path uri) "/"))
(update :path #(str % "/")))))
(when (= :browser @target) (when (= :browser @target)
(js/console.log (js/console.log
(str/format "Welcome to penpot! Version: '%s'." (:full @version)))) (str/format "Welcome to penpot! version='%s' base-uri='%s'." (:full @version) (str public-uri))))
;; --- Helper Functions ;; --- Helper Functions
@ -101,18 +110,20 @@
[{:keys [photo-id fullname name] :as profile}] [{:keys [photo-id fullname name] :as profile}]
(if (nil? photo-id) (if (nil? photo-id)
(avatars/generate {:name (or fullname name)}) (avatars/generate {:name (or fullname name)})
(str public-uri "/assets/by-id/" photo-id))) (str (u/join public-uri "assets/by-id/" photo-id))))
(defn resolve-team-photo-url (defn resolve-team-photo-url
[{:keys [photo-id name] :as team}] [{:keys [photo-id name] :as team}]
(if (nil? photo-id) (if (nil? photo-id)
(avatars/generate {:name name}) (avatars/generate {:name name})
(str public-uri "/assets/by-id/" photo-id))) (str (u/join public-uri "assets/by-id/" photo-id))))
(defn resolve-file-media (defn resolve-file-media
([media] ([media]
(resolve-file-media media false)) (resolve-file-media media false))
([{:keys [id] :as media} thumnail?] ([{:keys [id] :as media} thumnail?]
(str public-uri "/assets/by-file-media-id/" id (when thumnail? "/thumbnail")))) (str (cond-> (u/join public-uri "assets/by-file-media-id/")
(true? thumnail?) (u/join (str id "/thumbnail"))
(false? thumnail?) (u/join (str id))))))

View file

@ -7,21 +7,22 @@
(ns app.main.data.workspace.notifications (ns app.main.data.workspace.notifications
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.uri :as u]
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.pages :as cp] [app.common.pages :as cp]
[app.common.spec :as us] [app.common.spec :as us]
[app.config :as cfg] [app.config :as cf]
[app.main.data.workspace.common :as dwc] [app.main.data.workspace.common :as dwc]
[app.main.data.workspace.persistence :as dwp]
[app.main.data.workspace.libraries :as dwl] [app.main.data.workspace.libraries :as dwl]
[app.main.data.workspace.persistence :as dwp]
[app.main.repo :as rp] [app.main.repo :as rp]
[app.main.store :as st] [app.main.store :as st]
[app.main.streams :as ms] [app.main.streams :as ms]
[app.util.avatars :as avatars] [app.util.avatars :as avatars]
[app.util.i18n :as i18n :refer [tr]]
[app.util.time :as dt] [app.util.time :as dt]
[app.util.transit :as t] [app.util.transit :as t]
[app.util.websockets :as ws] [app.util.websockets :as ws]
[app.util.i18n :as i18n :refer [tr]]
[beicon.core :as rx] [beicon.core :as rx]
[cljs.spec.alpha :as s] [cljs.spec.alpha :as s]
[clojure.set :as set] [clojure.set :as set]
@ -39,14 +40,24 @@
(s/def ::message (s/def ::message
(s/keys :req-un [::type])) (s/keys :req-un [::type]))
(defn prepare-uri
[params]
(let [base (-> (u/join cf/public-uri "ws/notifications")
(assoc :query (u/map->query-string params)))]
(cond-> base
(= "https" (:scheme base))
(assoc :scheme "wss")
(= "http" (:scheme base))
(assoc :scheme "ws"))))
(defn initialize (defn initialize
[file-id] [file-id]
(ptk/reify ::initialize (ptk/reify ::initialize
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(let [sid (:session-id state) (let [sid (:session-id state)
uri (ws/uri "/ws/notifications" {:file-id file-id uri (prepare-uri {:file-id file-id :session-id sid})]
:session-id sid})]
(assoc-in state [:ws file-id] (ws/open uri)))) (assoc-in state [:ws file-id] (ws/open uri))))
ptk/WatchEvent ptk/WatchEvent

View file

@ -7,13 +7,13 @@
(ns app.main.repo (ns app.main.repo
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.uri :as u]
[app.config :as cfg] [app.config :as cfg]
[app.util.http :as http] [app.util.http :as http]
[app.util.time :as dt] [app.util.time :as dt]
[app.util.transit :as t] [app.util.transit :as t]
[beicon.core :as rx] [beicon.core :as rx]
[cuerdas.core :as str] [cuerdas.core :as str]))
[lambdaisland.uri :as u]))
(defn- handle-response (defn- handle-response
[{:keys [status body] :as response}] [{:keys [status body] :as response}]
@ -43,7 +43,7 @@
:status status :status status
:data body}))) :data body})))
(def ^:private base-uri (u/uri cfg/public-uri)) (def ^:private base-uri cfg/public-uri)
(defn- send-query! (defn- send-query!
"A simple helper for send and receive transit data on the penpot "A simple helper for send and receive transit data on the penpot
@ -125,7 +125,7 @@
(defmethod mutation ::multipart-upload (defmethod mutation ::multipart-upload
[id params] [id params]
(->> (http/send! {:method :post (->> (http/send! {:method :post
:uri (u/join base-uri "/api/rpc/mutation/" (name id)) :uri (u/join base-uri "api/rpc/mutation/" (name id))
:body (http/form-data params)}) :body (http/form-data params)})
(rx/map http/conditional-decode-transit) (rx/map http/conditional-decode-transit)
(rx/mapcat handle-response))) (rx/mapcat handle-response)))

View file

@ -8,7 +8,7 @@
(:require (:require
[clojure.java.io :as io] [clojure.java.io :as io]
[cuerdas.core :as str] [cuerdas.core :as str]
[lambdaisland.uri.normalize :as uri])) [app.common.uri :as u]))
(def cursor-folder "images/cursors") (def cursor-folder "images/cursors")
@ -53,7 +53,7 @@
[id rotation x y height] [id rotation x y height]
(let [svg-path (str cursor-folder "/" (name id) ".svg") (let [svg-path (str cursor-folder "/" (name id) ".svg")
data (-> svg-path io/resource slurp parse-svg) data (-> svg-path io/resource slurp parse-svg)
data (uri/percent-encode data) data (u/percent-encode data)
data (if rotation data (if rotation
(str/fmt "%3Cg transform='rotate(%s 8,8)'%3E%s%3C/g%3E" rotation data) (str/fmt "%3Cg transform='rotate(%s 8,8)'%3E%s%3C/g%3E" rotation data)

View file

@ -28,7 +28,6 @@
[app.util.timers :as ts] [app.util.timers :as ts]
[beicon.core :as rx] [beicon.core :as rx]
[cuerdas.core :as str] [cuerdas.core :as str]
[lambdaisland.uri :as uri]
[rumext.alpha :as mf])) [rumext.alpha :as mf]))
;; --- Grid Item Thumbnail ;; --- Grid Item Thumbnail

View file

@ -67,11 +67,11 @@
:viewer :viewer
(:path-params route) (:path-params route)
{:token token :index "0"}) {:token token :index "0"})
link (str cfg/public-uri "/#" link) link (assoc cfg/public-uri :fragment link)
copy-link copy-link
(fn [event] (fn [event]
(wapi/write-to-clipboard link) (wapi/write-to-clipboard (str link))
(st/emit! (dm/show {:type :info (st/emit! (dm/show {:type :info
:content "Link copied successfuly!" :content "Link copied successfuly!"
:timeout 3000})))] :timeout 3000})))]
@ -89,7 +89,7 @@
[:div.share-link-input [:div.share-link-input
(if (string? token) (if (string? token)
[:* [:*
[:span.link link] [:span.link (str link)]
[:span.link-button {:on-click copy-link} [:span.link-button {:on-click copy-link}
(t locale "viewer.header.share.copy-link")]] (t locale "viewer.header.share.copy-link")]]
[:span.link-placeholder (t locale "viewer.header.share.placeholder")])] [:span.link-placeholder (t locale "viewer.header.share.placeholder")])]

View file

@ -8,13 +8,13 @@
"A http client with rx streams interface." "A http client with rx streams interface."
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.uri :as u]
[app.config :as cfg] [app.config :as cfg]
[app.util.globals :as globals] [app.util.globals :as globals]
[app.util.object :as obj] [app.util.object :as obj]
[app.util.transit :as t] [app.util.transit :as t]
[beicon.core :as rx] [beicon.core :as rx]
[cuerdas.core :as str] [cuerdas.core :as str]
[lambdaisland.uri :as u]
[promesa.core :as p])) [promesa.core :as p]))
(defprotocol IBodyData (defprotocol IBodyData

View file

@ -1,3 +1,9 @@
;; 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) UXBOX Labs SL
(ns app.util.logging) (ns app.util.logging)
(defn- log-expr [form level keyvals] (defn- log-expr [form level keyvals]

View file

@ -9,37 +9,17 @@
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.config :as cfg] [app.config :as cfg]
[app.common.uri :as u]
[app.util.browser-history :as bhistory] [app.util.browser-history :as bhistory]
[app.util.timers :as ts] [app.util.timers :as ts]
[beicon.core :as rx] [beicon.core :as rx]
[cuerdas.core :as str] [cuerdas.core :as str]
[goog.events :as e] [goog.events :as e]
[potok.core :as ptk] [potok.core :as ptk]
[reitit.core :as r]) [reitit.core :as r]))
(:import
goog.Uri
goog.Uri.QueryData))
;; --- Router API ;; --- Router API
(defn- parse-query-data
[^QueryData qdata]
(persistent!
(reduce (fn [acc key]
(let [values (.getValues qdata key)
rkey (str/keyword key)]
(cond
(> (alength values) 1)
(assoc! acc rkey (into [] values))
(= (alength values) 1)
(assoc! acc rkey (aget values 0))
:else
acc)))
(transient {})
(.getKeys qdata))))
(defn resolve (defn resolve
([router id] (resolve router id {} {})) ([router id] (resolve router id {} {}))
([router id params] (resolve router id params {})) ([router id params] (resolve router id params {}))
@ -47,12 +27,10 @@
(when-let [match (r/match-by-name router id params)] (when-let [match (r/match-by-name router id params)]
(if (empty? qparams) (if (empty? qparams)
(r/match->path match) (r/match->path match)
(let [uri (.parse goog.Uri (r/match->path match)) (let [query (u/map->query-string qparams)]
qdt (.createFromMap QueryData (-> qparams (-> (u/uri (r/match->path match))
(d/without-nils) (assoc :query query)
(clj->js)))] (str)))))))
(.setQueryData ^js uri qdt)
(.toString ^js uri))))))
(defn create (defn create
[routes] [routes]
@ -65,26 +43,18 @@
(update [_ state] (update [_ state]
(assoc state :router (create routes))))) (assoc state :router (create routes)))))
(defn query-params
"Given goog.Uri, read query parameters into Clojure map."
[^goog.Uri uri]
(let [^js q (.getQueryData uri)]
(->> q
(.getKeys)
(map (juxt keyword #(.get q %)))
(into {}))))
(defn match (defn match
"Given routing tree and current path, return match with possibly "Given routing tree and current path, return match with possibly
coerced parameters. Return nil if no match found." coerced parameters. Return nil if no match found."
[router path] [router path]
(let [uri (.parse ^js Uri path)] (let [uri (u/uri path)]
(when-let [match (r/match-by-path router (.getPath ^js uri))] (when-let [match (r/match-by-path router (:path uri))]
(let [qparams (parse-query-data (.getQueryData ^js uri)) (let [qparams (u/query-string->map (:query uri))
params {:path (:path-params match) :query qparams}] params {:path (:path-params match)
(assoc match :query qparams}]
:params params (-> match
:query-params qparams))))) (assoc :params params)
(assoc :query-params qparams))))))
;; --- Navigate (Event) ;; --- Navigate (Event)
@ -119,8 +89,9 @@
(effect [_ state stream] (effect [_ state stream]
(let [router (:router state) (let [router (:router state)
path (resolve router id params qparams) path (resolve router id params qparams)
uri (str cfg/public-uri "/#" path)] uri (-> (u/uri cfg/public-uri)
(js/window.open uri "_blank")))) (assoc :fragment path))]
(js/window.open (str uri) "_blank"))))
(defn nav-new-window (defn nav-new-window
([id] (nav-new-window id nil nil)) ([id] (nav-new-window id nil nil))

View file

@ -7,13 +7,13 @@
(ns app.util.websockets (ns app.util.websockets
"A interface to webworkers exposed functionality." "A interface to webworkers exposed functionality."
(:require (:require
[app.common.uri :as u]
[app.config :as cfg] [app.config :as cfg]
[app.util.transit :as t] [app.util.transit :as t]
[beicon.core :as rx] [beicon.core :as rx]
[goog.events :as ev] [goog.events :as ev]
[potok.core :as ptk]) [potok.core :as ptk])
(:import (:import
goog.Uri
goog.net.WebSocket goog.net.WebSocket
goog.net.WebSocket.EventType)) goog.net.WebSocket.EventType))
@ -22,19 +22,6 @@
(-send [_ message] "send a message") (-send [_ message] "send a message")
(-close [_] "close websocket")) (-close [_] "close websocket"))
(defn uri
([path] (uri path {}))
([path params]
(let [uri (.parse ^js Uri cfg/public-uri)]
(.setPath ^js uri path)
(if (= (.getScheme ^js uri) "http")
(.setScheme ^js uri "ws")
(.setScheme ^js uri "wss"))
(run! (fn [[k v]]
(.setParameterValue ^js uri (name k) (str v)))
params)
(.toString uri))))
(defn open (defn open
[uri] [uri]
(let [sb (rx/subject) (let [sb (rx/subject)
@ -45,7 +32,7 @@
#(rx/push! sb {:type :error :payload %})) #(rx/push! sb {:type :error :payload %}))
lk3 (ev/listen ws EventType.OPENED lk3 (ev/listen ws EventType.OPENED
#(rx/push! sb {:type :opened :payload %}))] #(rx/push! sb {:type :opened :payload %}))]
(.open ws uri) (.open ws (str uri))
(reify (reify
cljs.core/IDeref cljs.core/IDeref
(-deref [_] ws) (-deref [_] ws)

View file

@ -19,8 +19,7 @@
[app.worker.snaps] [app.worker.snaps]
[app.util.object :as obj] [app.util.object :as obj]
[app.util.transit :as t] [app.util.transit :as t]
[app.util.worker :as w]) [app.util.worker :as w]))
(:import goog.Uri))
;; --- Messages Handling ;; --- Messages Handling