From cd6865f54b175317faa1f46e99eee5115ed6c5f7 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Mon, 3 Nov 2025 19:54:02 +0100 Subject: [PATCH 1/3] :arrow_up: Update yetti dependency Bugfixes --- backend/deps.edn | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/deps.edn b/backend/deps.edn index a6f6fcfecd..31b5e48096 100644 --- a/backend/deps.edn +++ b/backend/deps.edn @@ -28,8 +28,8 @@ com.google.guava/guava {:mvn/version "33.4.8-jre"} funcool/yetti - {:git/tag "v11.6" - :git/sha "94dc017" + {:git/tag "v11.8" + :git/sha "1d1b33f" :git/url "https://github.com/funcool/yetti.git" :exclusions [org.slf4j/slf4j-api]} From 8307b699bf536099c24e72261dbe4b3fc90992d4 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Mon, 3 Nov 2025 19:55:37 +0100 Subject: [PATCH 2/3] :bug: Remove a race condition on file export Caused when file is deleted in the middle of an exportation. The current export process is not transactional, and on file deletion several queries can start return not-found exception because of concurrent file deletion. With the changes on this PR we allow query deleted files internally on the exportation process and make it resilent to possible concurrent deletion. --- backend/src/app/binfile/common.clj | 2 +- backend/src/app/binfile/v3.clj | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/backend/src/app/binfile/common.clj b/backend/src/app/binfile/common.clj index 1f6ab99cf2..cf8508caa0 100644 --- a/backend/src/app/binfile/common.clj +++ b/backend/src/app/binfile/common.clj @@ -550,7 +550,7 @@ [cfg data file-id] (let [library-ids (get-libraries cfg [file-id])] (reduce (fn [data library-id] - (if-let [library (get-file cfg library-id)] + (if-let [library (get-file cfg library-id :include-deleted? true)] (ctf/absorb-assets data (:data library)) data)) data diff --git a/backend/src/app/binfile/v3.clj b/backend/src/app/binfile/v3.clj index 682ddc74de..cf88f71551 100644 --- a/backend/src/app/binfile/v3.clj +++ b/backend/src/app/binfile/v3.clj @@ -228,6 +228,7 @@ (db/tx-run! cfg (fn [cfg] (cond-> (bfc/get-file cfg file-id {:realize? true + :include-deleted? true :lock-for-update? true}) detach? (-> (ctf/detach-external-references file-id) @@ -285,14 +286,12 @@ (let [file (cond-> (select-keys file bfc/file-attrs) (:options data) - (assoc :options (:options data)) + (assoc :options (:options data))) - :always - (dissoc :data)) - - file (cond-> file - :always - (encode-file)) + file (-> file + (dissoc :data) + (dissoc :deleted-at) + (encode-file)) path (str "files/" file-id ".json")] (write-entry! output path file)) From cbae3dca3474fb8fb236298abd8b1210767460ff Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Mon, 3 Nov 2025 19:58:27 +0100 Subject: [PATCH 3/3] :sparkles: Simplify the approach for return streamable body Removing unnecesary syntax overhead with simplier abstraction --- backend/src/app/rpc.clj | 10 +++++++--- backend/src/app/rpc/commands/binfile.clj | 23 +++++++++-------------- backend/src/app/rpc/helpers.clj | 7 ++++++- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/backend/src/app/rpc.clj b/backend/src/app/rpc.clj index 63d778a89c..02e290bade 100644 --- a/backend/src/app/rpc.clj +++ b/backend/src/app/rpc.clj @@ -64,9 +64,13 @@ (let [mdata (meta result) response (if (fn? result) (result request) - (let [result (rph/unwrap result)] - {::yres/status (::http/status mdata 200) - ::yres/headers (::http/headers mdata {}) + (let [result (rph/unwrap result) + status (::http/status mdata 200) + headers (cond-> (::http/headers mdata {}) + (yres/stream-body? result) + (assoc "content-type" "application/octet-stream"))] + {::yres/status status + ::yres/headers headers ::yres/body result}))] (-> response (handle-response-transformation request mdata) diff --git a/backend/src/app/rpc/commands/binfile.clj b/backend/src/app/rpc/commands/binfile.clj index f0fc211e46..98b2c04193 100644 --- a/backend/src/app/rpc/commands/binfile.clj +++ b/backend/src/app/rpc/commands/binfile.clj @@ -25,10 +25,10 @@ [app.rpc.commands.projects :as projects] [app.rpc.commands.teams :as teams] [app.rpc.doc :as-alias doc] + [app.rpc.helpers :as rph] [app.tasks.file-gc] [app.util.services :as sv] - [app.worker :as-alias wrk] - [yetti.response :as yres])) + [app.worker :as-alias wrk])) (set! *warn-on-reflection* true) @@ -44,7 +44,7 @@ (defn stream-export-v1 [cfg {:keys [file-id include-libraries embed-assets] :as params}] - (yres/stream-body + (rph/stream (fn [_ output-stream] (try (-> cfg @@ -59,7 +59,7 @@ (defn stream-export-v3 [cfg {:keys [file-id include-libraries embed-assets] :as params}] - (yres/stream-body + (rph/stream (fn [_ output-stream] (try (-> cfg @@ -79,16 +79,11 @@ ::sm/params schema:export-binfile} [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id version file-id] :as params}] (files/check-read-permissions! pool profile-id file-id) - (fn [_] - (let [version (or version 1) - body (case (int version) - 1 (stream-export-v1 cfg params) - 2 (throw (ex-info "not-implemented" {})) - 3 (stream-export-v3 cfg params))] - - {::yres/status 200 - ::yres/headers {"content-type" "application/octet-stream"} - ::yres/body body}))) + (let [version (or version 1)] + (case (int version) + 1 (stream-export-v1 cfg params) + 2 (throw (ex-info "not-implemented" {})) + 3 (stream-export-v3 cfg params)))) ;; --- Command: import-binfile diff --git a/backend/src/app/rpc/helpers.clj b/backend/src/app/rpc/helpers.clj index a424b1f8f6..5b117b82fd 100644 --- a/backend/src/app/rpc/helpers.clj +++ b/backend/src/app/rpc/helpers.clj @@ -11,7 +11,7 @@ [app.common.data.macros :as dm] [app.http :as-alias http] [app.rpc :as-alias rpc] - [yetti.response :as-alias yres])) + [yetti.response :as yres])) ;; A utilty wrapper object for wrap service responses that does not ;; implements the IObj interface that make possible attach metadata to @@ -78,3 +78,8 @@ (let [exp (if (integer? max-age) max-age (inst-ms max-age)) val (dm/fmt "max-age=%" (int (/ exp 1000.0)))] (update response ::yres/headers assoc "cache-control" val))))) + +(defn stream + "A convenience allias for yetti.response/stream-body" + [f] + (yres/stream-body f))