From 7f2dc66e8662c2d801fb586c0eb67730cb6df705 Mon Sep 17 00:00:00 2001 From: Gennadiy Ivashchenko <94037347+UniversalWill@users.noreply.github.com> Date: Tue, 18 Aug 2026 02:44:43 +0500 Subject: [PATCH] :bug: Preserve public URI subpath in asset download URLs (#11234) Join asset download paths relative to PENPOT_PUBLIC_URI so temporary exports and binary file downloads retain configured subpaths. Add regression coverage for both URL generation paths. AI-assisted-by: gpt-5.6-sol --- backend/src/app/rpc/commands/binfile.clj | 3 ++- backend/src/app/rpc/management/exporter.clj | 3 ++- backend/test/backend_tests/binfile_test.clj | 14 ++++++++++++++ backend/test/backend_tests/rpc_management_test.clj | 11 +++++++++-- 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/backend/src/app/rpc/commands/binfile.clj b/backend/src/app/rpc/commands/binfile.clj index 685b450ecb..95197e70b9 100644 --- a/backend/src/app/rpc/commands/binfile.clj +++ b/backend/src/app/rpc/commands/binfile.clj @@ -63,7 +63,8 @@ :bucket "tempfile"})] (-> (cf/get :public-uri) - (u/join "/assets/by-id/") + (u/ensure-path-slash) + (u/join "assets/by-id/") (u/join (str (:id object))))) (finally diff --git a/backend/src/app/rpc/management/exporter.clj b/backend/src/app/rpc/management/exporter.clj index f4b7d9547f..317850b126 100644 --- a/backend/src/app/rpc/management/exporter.clj +++ b/backend/src/app/rpc/management/exporter.clj @@ -47,5 +47,6 @@ object (sto/put-object! storage content)] {:id (:id object) :uri (-> (cf/get :public-uri) - (u/join "/assets/by-id/") + (u/ensure-path-slash) + (u/join "assets/by-id/") (u/join (str (:id object))))})) diff --git a/backend/test/backend_tests/binfile_test.clj b/backend/test/backend_tests/binfile_test.clj index 05f1525c5e..1f8a3a51d8 100644 --- a/backend/test/backend_tests/binfile_test.clj +++ b/backend/test/backend_tests/binfile_test.clj @@ -16,10 +16,12 @@ [app.common.thumbnails :as thc] [app.common.types.shape :as cts] [app.common.uuid :as uuid] + [app.config :as cf] [app.db :as db] [app.db.sql :as sql] [app.http :as http] [app.rpc :as-alias rpc] + [app.rpc.commands.binfile :as binfile] [app.storage :as sto] [app.storage.tmp :as tmp] [backend-tests.helpers :as th] @@ -207,6 +209,18 @@ (t/is (= (count result) 1)) (t/is (every? uuid? result))))) +(t/deftest export-binfile-preserves-public-uri-subpath + (let [profile (th/create-profile* 1) + file (prepare-simple-file profile) + config (assoc cf/config :public-uri "https://example.com/penpot") + params {:file-id (:id file) + :include-libraries false + :embed-assets false} + uri (binding [cf/config config] + (#'binfile/export-binfile th/*system* params))] + (t/is (str/starts-with? (str uri) + "https://example.com/penpot/assets/by-id/")))) + (t/deftest read-obj-rejects-oversized-buffer ;; N1-07: read-obj! must reject objects exceeding max-object-size ;; before attempting to allocate the buffer diff --git a/backend/test/backend_tests/rpc_management_test.clj b/backend/test/backend_tests/rpc_management_test.clj index 2dd25694a1..6191305e26 100644 --- a/backend/test/backend_tests/rpc_management_test.clj +++ b/backend/test/backend_tests/rpc_management_test.clj @@ -11,6 +11,7 @@ [app.common.pprint :as pp] [app.common.types.shape :as cts] [app.common.uuid :as uuid] + [app.config :as cf] [app.db :as db] [app.http :as http] [app.rpc :as-alias rpc] @@ -19,6 +20,7 @@ [backend-tests.storage-test :refer [configure-storage-backend]] [buddy.core.bytes :as b] [clojure.test :as t] + [cuerdas.core :as str] [datoteka.fs :as fs] [datoteka.io :as io])) @@ -50,10 +52,15 @@ :path path :mtype "image/png" :size 7}} - out1 (th/management-command! params) - out2 (th/management-command! params)] + config (assoc cf/config :public-uri "https://example.com/penpot") + out1 (binding [cf/config config] + (th/management-command! params)) + out2 (binding [cf/config config] + (th/management-command! params))] (t/is (nil? (:error out1))) (t/is (nil? (:error out2))) + (t/is (str/starts-with? (str (get-in out1 [:result :uri])) + "https://example.com/penpot/assets/by-id/")) (t/is (not= (get-in out1 [:result :id]) (get-in out2 [:result :id])))))