mirror of
https://github.com/penpot/penpot.git
synced 2025-03-28 15:41:25 -05:00
✨ Add more adaptations to make app run in a prefixed path.
This commit is contained in:
parent
2828ccda7f
commit
55ea84a056
14 changed files with 115 additions and 98 deletions
34
common/app/common/uri.cljc
Normal file
34
common/app/common/uri.cljc
Normal 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))))
|
|
@ -68,23 +68,23 @@ function readManifest() {
|
|||
const content = JSON.parse(fs.readFileSync(path, {encoding: "utf8"}));
|
||||
|
||||
const index = {
|
||||
"config": "/js/config.js?ts=" + Date.now(),
|
||||
"config": "js/config.js?ts=" + Date.now(),
|
||||
"polyfills": "js/polyfills.js?ts=" + Date.now(),
|
||||
};
|
||||
|
||||
for (let item of content) {
|
||||
index[item.name] = "/js/" + item["output-name"];
|
||||
index[item.name] = "js/" + item["output-name"];
|
||||
};
|
||||
|
||||
return index;
|
||||
} catch (e) {
|
||||
console.error("Error on reading manifest, using default.");
|
||||
return {
|
||||
"config": "/js/config.js",
|
||||
"config": "js/config.js",
|
||||
"polyfills": "js/polyfills.js",
|
||||
"main": "/js/main.js",
|
||||
"shared": "/js/shared.js",
|
||||
"worker": "/js/worker.js"
|
||||
"main": "js/main.js",
|
||||
"shared": "js/shared.js",
|
||||
"worker": "js/worker.js"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,10 +15,9 @@
|
|||
<meta name="twitter:image" content="https://penpot.app/images/workspace-ui.jpg">
|
||||
<meta name="twitter:site" content="@penpotapp">
|
||||
<meta name="twitter:creator" content="@penpotapp">
|
||||
<link id="theme" href="/css/main-{{& th}}.css?ts={{& ts}}"
|
||||
rel="stylesheet" type="text/css" />
|
||||
<link id="theme" href="css/main-{{& th}}.css?ts={{& ts}}" rel="stylesheet" type="text/css" />
|
||||
|
||||
<link rel="icon" href="/images/favicon.png" />
|
||||
<link rel="icon" href="images/favicon.png" />
|
||||
|
||||
<script>
|
||||
window.penpotTranslations = JSON.parse({{& translations}});
|
||||
|
|
|
@ -6,14 +6,15 @@
|
|||
|
||||
(ns app.config
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[app.common.data :as d]
|
||||
[app.common.uri :as u]
|
||||
[app.common.spec :as us]
|
||||
[app.common.version :as v]
|
||||
[app.util.avatars :as avatars]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.globals :refer [global location]]
|
||||
[app.util.object :as obj]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.avatars :as avatars]
|
||||
[clojure.spec.alpha :as s]
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
;; --- Auxiliar Functions
|
||||
|
@ -76,16 +77,24 @@
|
|||
(def translations (obj/get global "penpotTranslations"))
|
||||
(def themes (obj/get global "penpotThemes"))
|
||||
|
||||
(def public-uri (or (obj/get global "penpotPublicURI") (.-origin ^js location)))
|
||||
|
||||
(def version (delay (parse-version global)))
|
||||
(def target (delay (parse-target global)))
|
||||
(def browser (delay (parse-browser)))
|
||||
(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)
|
||||
(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
|
||||
|
||||
|
@ -101,18 +110,20 @@
|
|||
[{:keys [photo-id fullname name] :as profile}]
|
||||
(if (nil? photo-id)
|
||||
(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
|
||||
[{:keys [photo-id name] :as team}]
|
||||
(if (nil? photo-id)
|
||||
(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
|
||||
([media]
|
||||
(resolve-file-media media false))
|
||||
([{: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))))))
|
||||
|
||||
|
||||
|
|
|
@ -7,21 +7,22 @@
|
|||
(ns app.main.data.workspace.notifications
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.uri :as u]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.pages :as cp]
|
||||
[app.common.spec :as us]
|
||||
[app.config :as cfg]
|
||||
[app.config :as cf]
|
||||
[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.persistence :as dwp]
|
||||
[app.main.repo :as rp]
|
||||
[app.main.store :as st]
|
||||
[app.main.streams :as ms]
|
||||
[app.util.avatars :as avatars]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[app.util.time :as dt]
|
||||
[app.util.transit :as t]
|
||||
[app.util.websockets :as ws]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[beicon.core :as rx]
|
||||
[cljs.spec.alpha :as s]
|
||||
[clojure.set :as set]
|
||||
|
@ -39,14 +40,24 @@
|
|||
(s/def ::message
|
||||
(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
|
||||
[file-id]
|
||||
(ptk/reify ::initialize
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [sid (:session-id state)
|
||||
uri (ws/uri "/ws/notifications" {:file-id file-id
|
||||
:session-id sid})]
|
||||
uri (prepare-uri {:file-id file-id :session-id sid})]
|
||||
(assoc-in state [:ws file-id] (ws/open uri))))
|
||||
|
||||
ptk/WatchEvent
|
||||
|
|
|
@ -7,13 +7,13 @@
|
|||
(ns app.main.repo
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.uri :as u]
|
||||
[app.config :as cfg]
|
||||
[app.util.http :as http]
|
||||
[app.util.time :as dt]
|
||||
[app.util.transit :as t]
|
||||
[beicon.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
[lambdaisland.uri :as u]))
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
(defn- handle-response
|
||||
[{:keys [status body] :as response}]
|
||||
|
@ -43,7 +43,7 @@
|
|||
:status status
|
||||
:data body})))
|
||||
|
||||
(def ^:private base-uri (u/uri cfg/public-uri))
|
||||
(def ^:private base-uri cfg/public-uri)
|
||||
|
||||
(defn- send-query!
|
||||
"A simple helper for send and receive transit data on the penpot
|
||||
|
@ -125,7 +125,7 @@
|
|||
(defmethod mutation ::multipart-upload
|
||||
[id params]
|
||||
(->> (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)})
|
||||
(rx/map http/conditional-decode-transit)
|
||||
(rx/mapcat handle-response)))
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
(:require
|
||||
[clojure.java.io :as io]
|
||||
[cuerdas.core :as str]
|
||||
[lambdaisland.uri.normalize :as uri]))
|
||||
[app.common.uri :as u]))
|
||||
|
||||
(def cursor-folder "images/cursors")
|
||||
|
||||
|
@ -53,7 +53,7 @@
|
|||
[id rotation x y height]
|
||||
(let [svg-path (str cursor-folder "/" (name id) ".svg")
|
||||
data (-> svg-path io/resource slurp parse-svg)
|
||||
data (uri/percent-encode data)
|
||||
data (u/percent-encode data)
|
||||
|
||||
data (if rotation
|
||||
(str/fmt "%3Cg transform='rotate(%s 8,8)'%3E%s%3C/g%3E" rotation data)
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
[app.util.timers :as ts]
|
||||
[beicon.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
[lambdaisland.uri :as uri]
|
||||
[rumext.alpha :as mf]))
|
||||
|
||||
;; --- Grid Item Thumbnail
|
||||
|
|
|
@ -67,11 +67,11 @@
|
|||
:viewer
|
||||
(:path-params route)
|
||||
{:token token :index "0"})
|
||||
link (str cfg/public-uri "/#" link)
|
||||
link (assoc cfg/public-uri :fragment link)
|
||||
|
||||
copy-link
|
||||
(fn [event]
|
||||
(wapi/write-to-clipboard link)
|
||||
(wapi/write-to-clipboard (str link))
|
||||
(st/emit! (dm/show {:type :info
|
||||
:content "Link copied successfuly!"
|
||||
:timeout 3000})))]
|
||||
|
@ -89,7 +89,7 @@
|
|||
[:div.share-link-input
|
||||
(if (string? token)
|
||||
[:*
|
||||
[:span.link link]
|
||||
[:span.link (str link)]
|
||||
[:span.link-button {:on-click copy-link}
|
||||
(t locale "viewer.header.share.copy-link")]]
|
||||
[:span.link-placeholder (t locale "viewer.header.share.placeholder")])]
|
||||
|
|
|
@ -8,13 +8,13 @@
|
|||
"A http client with rx streams interface."
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.uri :as u]
|
||||
[app.config :as cfg]
|
||||
[app.util.globals :as globals]
|
||||
[app.util.object :as obj]
|
||||
[app.util.transit :as t]
|
||||
[beicon.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
[lambdaisland.uri :as u]
|
||||
[promesa.core :as p]))
|
||||
|
||||
(defprotocol IBodyData
|
||||
|
|
|
@ -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)
|
||||
|
||||
(defn- log-expr [form level keyvals]
|
||||
|
|
|
@ -9,37 +9,17 @@
|
|||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.config :as cfg]
|
||||
[app.common.uri :as u]
|
||||
[app.util.browser-history :as bhistory]
|
||||
[app.util.timers :as ts]
|
||||
[beicon.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
[goog.events :as e]
|
||||
[potok.core :as ptk]
|
||||
[reitit.core :as r])
|
||||
(:import
|
||||
goog.Uri
|
||||
goog.Uri.QueryData))
|
||||
[reitit.core :as r]))
|
||||
|
||||
;; --- 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
|
||||
([router id] (resolve router id {} {}))
|
||||
([router id params] (resolve router id params {}))
|
||||
|
@ -47,12 +27,10 @@
|
|||
(when-let [match (r/match-by-name router id params)]
|
||||
(if (empty? qparams)
|
||||
(r/match->path match)
|
||||
(let [uri (.parse goog.Uri (r/match->path match))
|
||||
qdt (.createFromMap QueryData (-> qparams
|
||||
(d/without-nils)
|
||||
(clj->js)))]
|
||||
(.setQueryData ^js uri qdt)
|
||||
(.toString ^js uri))))))
|
||||
(let [query (u/map->query-string qparams)]
|
||||
(-> (u/uri (r/match->path match))
|
||||
(assoc :query query)
|
||||
(str)))))))
|
||||
|
||||
(defn create
|
||||
[routes]
|
||||
|
@ -65,26 +43,18 @@
|
|||
(update [_ state]
|
||||
(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
|
||||
"Given routing tree and current path, return match with possibly
|
||||
coerced parameters. Return nil if no match found."
|
||||
[router path]
|
||||
(let [uri (.parse ^js Uri path)]
|
||||
(when-let [match (r/match-by-path router (.getPath ^js uri))]
|
||||
(let [qparams (parse-query-data (.getQueryData ^js uri))
|
||||
params {:path (:path-params match) :query qparams}]
|
||||
(assoc match
|
||||
:params params
|
||||
:query-params qparams)))))
|
||||
(let [uri (u/uri path)]
|
||||
(when-let [match (r/match-by-path router (:path uri))]
|
||||
(let [qparams (u/query-string->map (:query uri))
|
||||
params {:path (:path-params match)
|
||||
:query qparams}]
|
||||
(-> match
|
||||
(assoc :params params)
|
||||
(assoc :query-params qparams))))))
|
||||
|
||||
;; --- Navigate (Event)
|
||||
|
||||
|
@ -119,8 +89,9 @@
|
|||
(effect [_ state stream]
|
||||
(let [router (:router state)
|
||||
path (resolve router id params qparams)
|
||||
uri (str cfg/public-uri "/#" path)]
|
||||
(js/window.open uri "_blank"))))
|
||||
uri (-> (u/uri cfg/public-uri)
|
||||
(assoc :fragment path))]
|
||||
(js/window.open (str uri) "_blank"))))
|
||||
|
||||
(defn nav-new-window
|
||||
([id] (nav-new-window id nil nil))
|
||||
|
|
|
@ -7,13 +7,13 @@
|
|||
(ns app.util.websockets
|
||||
"A interface to webworkers exposed functionality."
|
||||
(:require
|
||||
[app.common.uri :as u]
|
||||
[app.config :as cfg]
|
||||
[app.util.transit :as t]
|
||||
[beicon.core :as rx]
|
||||
[goog.events :as ev]
|
||||
[potok.core :as ptk])
|
||||
(:import
|
||||
goog.Uri
|
||||
goog.net.WebSocket
|
||||
goog.net.WebSocket.EventType))
|
||||
|
||||
|
@ -22,19 +22,6 @@
|
|||
(-send [_ message] "send a message")
|
||||
(-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
|
||||
[uri]
|
||||
(let [sb (rx/subject)
|
||||
|
@ -45,7 +32,7 @@
|
|||
#(rx/push! sb {:type :error :payload %}))
|
||||
lk3 (ev/listen ws EventType.OPENED
|
||||
#(rx/push! sb {:type :opened :payload %}))]
|
||||
(.open ws uri)
|
||||
(.open ws (str uri))
|
||||
(reify
|
||||
cljs.core/IDeref
|
||||
(-deref [_] ws)
|
||||
|
|
|
@ -19,8 +19,7 @@
|
|||
[app.worker.snaps]
|
||||
[app.util.object :as obj]
|
||||
[app.util.transit :as t]
|
||||
[app.util.worker :as w])
|
||||
(:import goog.Uri))
|
||||
[app.util.worker :as w]))
|
||||
|
||||
;; --- Messages Handling
|
||||
|
||||
|
@ -119,7 +118,7 @@
|
|||
(rx/debounce 1)
|
||||
|
||||
(rx/subs (fn [[messages dropped last]]
|
||||
;; Send back the dropped messages replies
|
||||
;; Send back the dropped messages replies
|
||||
(doseq [msg dropped]
|
||||
(drop-message msg))
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue