diff --git a/backend/src/app/config.clj b/backend/src/app/config.clj index 6f69047382..3a332bffb5 100644 --- a/backend/src/app/config.clj +++ b/backend/src/app/config.clj @@ -377,6 +377,20 @@ (or (c/get config :file-clean-delay) (ct/duration {:days 2}))) +(defn join-uri + "Join path segments onto a base URI, preserving a potential subpath + (same semantics as the frontend config). The base is normalized with + a trailing slash; segments must not start with `/` (a leading slash + would resolve against the host root and drop the subpath)." + [base & segments] + (str (apply u/join (u/ensure-path-slash base) segments))) + +(defn get-public-uri + "Canonical public URI builder: `join-uri` over the configured + :public-uri. With no segments, returns the normalized base." + [& segments] + (apply join-uri (c/get config :public-uri) segments)) + (defn get "A configuration getter. Helps code be more testable." ([key] diff --git a/backend/src/app/http/link_preview.clj b/backend/src/app/http/link_preview.clj index c415b2a99b..c024f6d651 100644 --- a/backend/src/app/http/link_preview.clj +++ b/backend/src/app/http/link_preview.clj @@ -35,14 +35,6 @@ ORDER BY ft.revn DESC NULLS LAST LIMIT 1") -(defn- resolve-image-uri - [media-id] - (str (cf/get :public-uri) "/assets/by-id/" media-id)) - -(defn- resolve-default-image-uri - [] - (str (cf/get :public-uri) "/images/penpot-link-preview.png")) - (defn- get-file-context "Return the link preview context for a file link: the file name as title and, when available, the last dashboard thumbnail as image." @@ -50,7 +42,7 @@ (when-let [{:keys [name media-id]} (db/exec-one! pool [sql:get-file file-id])] (cond-> (assoc default-context :title (str name " | Penpot")) (some? media-id) - (assoc :image (resolve-image-uri media-id))))) + (assoc :image (cf/get-public-uri (str "assets/by-id/" media-id)))))) (defn- get-context [pool params] @@ -71,7 +63,7 @@ (let [context (when (contains? cf/flags :link-preview) (get-context pool (:query-params request))) context (-> (or context default-context) - (update :image #(or % (resolve-default-image-uri))))] + (update :image #(or % (cf/get-public-uri "images/penpot-link-preview.png"))))] {::yres/status 200 ::yres/headers {"content-type" "text/html; charset=utf-8" "cache-control" "no-store, no-cache, max-age=0"} diff --git a/backend/test/backend_tests/config_test.clj b/backend/test/backend_tests/config_test.clj new file mode 100644 index 0000000000..ce070ec20b --- /dev/null +++ b/backend/test/backend_tests/config_test.clj @@ -0,0 +1,35 @@ +;; 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 backend-tests.config-test + (:require + [app.config :as cf] + [clojure.test :as t])) + +(t/deftest get-public-uri-normalizes-base + (t/testing "trailing slash is ensured with and without subpath" + (doseq [[base expected] [["http://localhost:3449" "http://localhost:3449/"] + ["http://localhost:3449/" "http://localhost:3449/"] + ["https://example.com/penpot" "https://example.com/penpot/"] + ["https://example.com/penpot/" "https://example.com/penpot/"]]] + (t/testing (str "base " base) + (with-redefs [cf/config (assoc cf/config :public-uri base)] + (t/is (= expected (cf/get-public-uri)))))))) + +(t/deftest get-public-uri-preserves-subpath + (t/testing "joined segments keep the subpath" + (with-redefs [cf/config (assoc cf/config :public-uri "https://example.com/penpot")] + (t/is (= "https://example.com/penpot/assets/by-id/123" + (cf/get-public-uri "assets/by-id/123"))) + (t/is (= "https://example.com/penpot/api/main/doc" + (cf/get-public-uri "api/main/doc")))))) + +(t/deftest join-uri-joins-arbitrary-base + (t/testing "segments join onto any base with trailing slash normalization" + (t/is (= "https://nitrate.example.com/api/teams/123" + (cf/join-uri "https://nitrate.example.com" "api/teams/123"))) + (t/is (= "https://nitrate.example.com/api/teams/123" + (cf/join-uri "https://nitrate.example.com/" "api/teams/123")))))