diff --git a/backend/src/app/srepl/fixes.clj b/backend/src/app/srepl/fixes.clj deleted file mode 100644 index aa5af7ae0..000000000 --- a/backend/src/app/srepl/fixes.clj +++ /dev/null @@ -1,402 +0,0 @@ -;; 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 - -(ns app.srepl.fixes - "A collection of adhoc fixes scripts." - (:require - [app.common.data :as d] - [app.common.files.helpers :as cfh] - [app.common.files.validate :as cfv] - [app.common.geom.shapes :as gsh] - [app.common.logging :as l] - [app.common.pprint :refer [pprint]] - [app.common.types.component :as ctk] - [app.common.types.container :as ctn] - [app.common.types.file :as ctf] - [app.common.uuid :as uuid] - [app.db :as db] - [app.rpc.commands.files :as files] - [app.srepl.helpers :as h] - [app.util.blob :as blob])) - -(defn validate-file - [file] - (let [libs (->> (files/get-file-libraries app.srepl.helpers/*conn* (:id file)) - (cons file) - (map #(files/get-file app.srepl.helpers/*conn* (:id %) (:features file))) - (d/index-by :id)) - - update-page (fn [page] - (let [errors (cfv/validate-shape uuid/zero file page libs)] - (when (seq errors) - (println "******Errors in file " (:id file) " page " (:id page)) - (pprint errors {:level 3}))))] - - (update file :data h/update-pages update-page))) - -(defn repair-orphaned-shapes - "There are some shapes whose parent has been deleted. This function - detects them and puts them as children of the root node." - ([data] - (letfn [(is-orphan? [shape objects] - (and (some? (:parent-id shape)) - (nil? (get objects (:parent-id shape))))) - - (update-page [page] - (let [objects (:objects page) - orphans (into #{} (filter #(is-orphan? % objects)) (vals objects))] - (if (seq orphans) - (do - (l/info :hint "found a file with orphans" :file-id (:id data) :broken-shapes (count orphans)) - (-> page - (h/update-shapes (fn [shape] - (if (contains? orphans shape) - (assoc shape :parent-id uuid/zero) - shape))) - (update-in [:objects uuid/zero :shapes] into (map :id) orphans))) - page)))] - - (h/update-pages data update-page))) - - ;; special arity for to be called from h/analyze-files to search for - ;; files with possible issues - - ([file state] - (repair-orphaned-shapes (:data file)) - (update state :total (fnil inc 0)))) - -(defn rename-layout-attrs - ([file] - (let [found? (volatile! false)] - (letfn [(update-shape - [shape] - (when (or (= (:layout-flex-dir shape) :reverse-row) - (= (:layout-flex-dir shape) :reverse-column) - (= (:layout-wrap-type shape) :no-wrap)) - (vreset! found? true)) - (cond-> shape - (= (:layout-flex-dir shape) :reverse-row) - (assoc :layout-flex-dir :row-reverse) - (= (:layout-flex-dir shape) :reverse-column) - (assoc :layout-flex-dir :column-reverse) - (= (:layout-wrap-type shape) :no-wrap) - (assoc :layout-wrap-type :nowrap))) - - (update-page - [page] - (h/update-shapes page update-shape))] - - (let [new-file (update file :data h/update-pages update-page)] - (when @found? - (l/info :hint "Found attrs to rename in file" - :id (:id file) - :name (:name file))) - new-file)))) - - ([file state] - (rename-layout-attrs file) - (update state :total (fnil inc 0)))) - -(defn fix-components-shaperefs - ([file] - (if-not (contains? (:features file) "components/v2") - (do - (println " This file is not v2") - file) - (let [libs (->> (files/get-file-libraries app.srepl.helpers/*conn* (:id file)) - (cons file) - (map #(files/get-file app.srepl.helpers/*conn* (:id %) (:features file))) - (d/index-by :id)) - - fix-copy-item - (fn fix-copy-item [allow-head shapes-copy shapes-base copy-id base-id] - (let [copy (first (filter #(= (:id %) copy-id) shapes-copy)) - ;; do nothing if it is a copy inside of a copy. It will be treated later - stop? (and (not allow-head) (ctk/instance-head? copy)) - base (first (filter #(= (:id %) base-id) shapes-base)) - fci (partial fix-copy-item false shapes-copy shapes-base) - - updates (if (and - (not stop?) - (not= (:shape-ref copy) base-id)) - [[(:id copy) base-id]] - []) - - child-updates (if (and - (not stop?) - ;; If the base has the same number of childrens than the copy, we asume - ;; that the shaperefs can be fixed ad pointed in the same order - (= (count (:shapes copy)) (count (:shapes base)))) - (apply concat (map fci (:shapes copy) (:shapes base))) - [])] - (concat updates child-updates))) - - fix-copy - (fn [objects updates copy] - (let [component (ctf/find-component libs (:component-id copy) {:include-deleted? true}) - component-file (get libs (:component-file copy)) - component-shapes (ctf/get-component-shapes (:data component-file) component) - copy-shapes (cfh/get-children-with-self objects (:id copy)) - - copy-updates (fix-copy-item true copy-shapes component-shapes (:id copy) (:main-instance-id component))] - (concat updates copy-updates))) - - update-page - (fn [page] - (let [objects (:objects page) - fc (partial fix-copy objects) - copies (->> objects - vals - (filter #(and (ctk/instance-head? %) (not (ctk/main-instance? %))))) - updates (reduce fc [] copies) - updated-page (reduce (fn [p [id shape-ref]] - (assoc-in p [:objects id :shape-ref] shape-ref)) - page - updates)] - (println "Page " (:name page) " - Fixing " (count updates)) - updated-page))] - - (println "Updating " (:name file) (:id file)) - (-> file - (update :data h/update-pages update-page) - (assoc ::updated true))))) - - ([file save?] - (let [file (-> file - (update :data blob/decode) - (fix-components-shaperefs))] - (when (and save? (::updated file)) - (let [data (blob/encode (:data file))] - (db/update! h/*conn* :file - {:data data - ;; :revn (:revn file) - } - {:id (:id file)}) - - (files/persist-pointers! h/*conn* (:id file))))))) - -(defn fix-component-root - ([file] - (let [update-shape (fn [page shape] - (let [parent (get (:objects page) (:parent-id shape))] - (if (and parent - (:component-root shape) - (:shape-ref parent)) - (do - (println " Shape " (:name shape) (:id shape)) - (dissoc shape :component-root)) - shape))) - - update-page (fn [page] - (println "Page " (:name page)) - (h/update-shapes page (partial update-shape page)))] - - (println "Updating " (:name file) (:id file)) - (update file :data h/update-pages update-page))) - - ([file save?] - (let [file (-> file - (update :data blob/decode) - (fix-component-root))] - (when save? - (let [data (blob/encode (:data file))] - (db/update! h/*conn* :file - {:data data - ;; :revn (:revn file) - } - {:id (:id file)}) - - (files/persist-pointers! h/*conn* (:id file))))))) - -(defn update-near-components - ([file] - (println "Updating " (:name file) (:id file)) - (if-not (contains? (:features file) "components/v2") - (do - (println " This file is not v2") - file) - (let [libs (->> (files/get-file-libraries h/*conn* (:id file)) - (cons file) - (map #(files/get-file h/*conn* (:id %) (:features file))) - (d/index-by :id)) - - update-shape - (fn [page shape] - (if-not (:shape-ref shape) - shape - (do - ;; Uncomment println's to debug - ;; (println " -> Shape " (:name shape) (:id shape) " shape-ref " (:shape-ref shape)) - (let [root-shape (ctn/get-copy-root (:objects page) shape)] - (if root-shape - (let [component (ctf/get-component libs (:component-file root-shape) (:component-id root-shape) {:include-deleted? true}) - component-file (get libs (:component-file root-shape)) - component-shapes (ctf/get-component-shapes (:data component-file) component) - ref-shape (d/seek #(= (:id %) (:shape-ref shape)) component-shapes)] - (if-not (and component component-file component-shapes) - (do - ;; (println " -> Shape " (:name shape) (:id shape) " shape-ref " (:shape-ref shape)) - ;; (when-not component (println " (component not found)")) - ;; (when-not component-file (println " (component-file not found)")) - ;; (when-not component-shapes (println " (component-shapes not found)")) - shape) - (if ref-shape - shape ; This means that the copy is not nested, or this script already was run - (let [near-shape (d/seek #(= (:shape-ref %) (:shape-ref shape)) component-shapes)] - (if near-shape - (do - (println " -> Shape " (:name shape) (:id shape) " shape-ref " (:shape-ref shape)) - (println " new ref-shape " (:id near-shape)) - (assoc shape :shape-ref (:id near-shape))) - (do - ;; We assume in this case that this is a fostered sub instance, so we do nothing - ;; (println " -> Shape " (:name shape) (:id shape) " shape-ref " (:shape-ref shape)) - ;; (println (near-shape not found)") - shape)))))) - (do - ;; (println " -> Shape " (:name shape) (:id shape) " shape-ref " (:shape-ref shape)) - ;; (println " (root shape not found)") - shape)))))) - - update-page - (fn [page] - (println "Page " (:name page)) - (h/update-shapes page (partial update-shape page)))] - - (-> file - (update :data h/update-pages update-page) - (assoc ::updated true))))) - - ([file save?] - (let [file (-> file - (update :data blob/decode) - (update-near-components))] - (when (and save? (::updated file)) - (let [data (blob/encode (:data file))] - (db/update! h/*conn* :file - {:data data - ;; :revn (:revn file) - } - {:id (:id file)}) - - (files/persist-pointers! h/*conn* (:id file))))))) - -(defn fix-main-shape-name - ([file] - (println "Updating " (:name file) (:id file)) - (if-not (contains? (:features file) "components/v2") - (do - (println " This file is not v2") - file) - (let [libs (->> (files/get-file-libraries h/*conn* (:id file)) - (cons file) - (map #(files/get-file h/*conn* (:id %) (:features file))) - (d/index-by :id)) - - update-shape - (fn [shape] - (if-not (ctk/instance-head? shape) - shape - (let [component (ctf/get-component libs (:component-file shape) (:component-id shape) {:include-deleted? true}) - [_path name] (cfh/parse-path-name (:name shape)) - full-name (cfh/clean-path (str (:path component) "/" (:name component)))] - (if (= name (:name component)) - (assoc shape :name full-name) - shape)))) - - - update-page - (fn [page] - (println "Page " (:name page)) - (h/update-shapes page update-shape))] - - (-> file - (update :data h/update-pages update-page) - (assoc ::updated true))))) - - ([file save?] - (let [file (-> file - (update :data blob/decode) - (fix-main-shape-name))] - (when (and save? (::updated file)) - (let [data (blob/encode (:data file))] - (db/update! h/*conn* :file - {:data data - ;; :revn (:revn file) - } - {:id (:id file)}) - - (files/persist-pointers! h/*conn* (:id file))))))) - -(defn fix-touched - "For all copies, compare all synced attributes with the main, and set the touched attribute if needed." - ([file] - (let [libraries (->> (files/get-file-libraries app.srepl.helpers/*conn* (:id file)) - (map #(files/get-file app.srepl.helpers/*conn* (:id %) (:features file))) - (d/index-by :id)) - - update-shape (fn [page shape] - (if (ctk/in-component-copy? shape) - (let [ref-shape (ctf/find-ref-shape file - (:objects page) - libraries - shape - :include-deleted? true) - fix-touched-attr - (fn [shape [attr group]] - (if (nil? ref-shape) - shape - (let [equal? - (if (= group :geometry-group) - (if (#{:width :height} attr) - (gsh/close-attrs? attr (get shape attr) (get ref-shape attr) 1) - true) - (gsh/close-attrs? attr (get shape attr) (get ref-shape attr)))] - (when (and (not equal?) (not (cfh/touched-group? shape group))) - (println " -> set touched " (:name shape) (:id shape) attr group)) - (cond-> shape - (and (not equal?) (not (cfh/touched-group? shape group))) - (update :touched cfh/set-touched-group group))))) - - fix-touched-children - (fn [shape] - (let [matches? (fn [[child-id ref-child-id]] - (if child-id - (let [child (ctn/get-shape page child-id)] - (= (:shape-ref child) ref-child-id)) - false)) - equal? (every? matches? (d/zip-all (:shapes shape) (:shapes ref-shape)))] - (when (and (not equal?) (not (cfh/touched-group? shape :shapes-group))) - (println " -> set touched " (:name shape) (:id shape) :shapes :shapes-group)) - (cond-> shape - (and (not equal?) (not (cfh/touched-group? shape :shapes-group))) - (update :touched cfh/set-touched-group :shapes-group))))] - - (as-> shape $ - (reduce fix-touched-attr $ ctk/sync-attrs) - (fix-touched-children $))) - - shape)) - - update-page (fn [page] - (println "Page " (:name page)) - (h/update-shapes page (partial update-shape page)))] - - (println "Updating " (:name file) (:id file)) - (update file :data h/update-pages update-page))) - - ([file save?] - (let [file (-> file - (update :data blob/decode) - (fix-touched))] - (when save? - (let [data (blob/encode (:data file))] - (db/update! h/*conn* :file - {:data data - :revn (inc (:revn file))} - {:id (:id file)}) - - (files/persist-pointers! h/*conn* (:id file))))))) diff --git a/backend/src/app/srepl/helpers.clj b/backend/src/app/srepl/helpers.clj index e72bbf185..9229006fd 100644 --- a/backend/src/app/srepl/helpers.clj +++ b/backend/src/app/srepl/helpers.clj @@ -9,7 +9,6 @@ (:refer-clojure :exclude [parse-uuid]) #_:clj-kondo/ignore (:require - [app.auth :refer [derive-password]] [app.common.data :as d] [app.common.exceptions :as ex] [app.common.features :as cfeat] @@ -41,6 +40,7 @@ [promesa.exec.csp :as sp])) (def ^:dynamic *conn* nil) +(def ^:dynamic *system* nil) (defn println! [& params] @@ -53,44 +53,22 @@ v (d/parse-uuid v))) -(defn resolve-connectable - [o] - (if (db/connection? o) - o - (if (db/pool? o) - o - (or (::db/conn o) - (::db/pool o))))) - -(defn reset-password! - "Reset a password to a specific one for a concrete user or all users - if email is `:all` keyword." - [system & {:keys [email password] :or {password "123123"} :as params}] - (us/verify! (contains? params :email) "`email` parameter is mandatory") - (db/with-atomic [conn (:app.db/pool system)] - (let [password (derive-password password)] - (if (= email :all) - (db/exec! conn ["update profile set password=?" password]) - (let [email (str/lower email)] - (db/exec! conn ["update profile set password=? where email=?" password email])))))) - (defn reset-file-data! "Hardcode replace of the data of one file." [system id data] - (db/with-atomic [conn (:app.db/pool system)] - (db/update! conn :file - {:data data} - {:id id}))) + (db/tx-run! system (fn [system] + (db/update! system :file + {:data data} + {:id id})))) (defn get-file "Get the migrated data of one file." [system id] - (db/with-atomic [conn (:app.db/pool system)] - (binding [pmap/*load-fn* (partial files/load-pointer conn id)] - (-> (db/get-by-id conn :file id) - (update :data blob/decode) - (update :data pmg/migrate-data) - (files/process-pointers deref))))) + (db/run! system + (fn [{:keys [::db/conn]}] + (binding [pmap/*load-fn* (partial files/load-pointer conn id)] + (-> (files/get-file conn id) + (files/process-pointers deref)))))) (defn validate "Validate structure, referencial integrity and semantic coherence of @@ -145,95 +123,99 @@ (defn update-file! "Apply a function to the data of one file. Optionally save the changes or not. The function receives the decoded and migrated file data." - [system & {:keys [update-fn id save? migrate? inc-revn?] - :or {save? false migrate? true inc-revn? true}}] - (db/with-atomic [conn (:app.db/pool system)] - (let [file (-> (db/get-by-id conn :file id {::db/for-update? true}) - (update :features db/decode-pgarray #{}))] - (binding [*conn* conn - pmap/*tracked* (atom {}) - pmap/*load-fn* (partial files/load-pointer conn id) - cfeat/*wrap-with-pointer-map-fn* - (if (contains? (:features file) "fdata/pointer-map") pmap/wrap identity) - cfeat/*wrap-with-objects-map-fn* - (if (contains? (:features file) "fdata/objectd-map") omap/wrap identity)] - (let [file (-> file - (update :data blob/decode) - (cond-> migrate? (update :data pmg/migrate-data)) - (update-fn) - (cond-> inc-revn? (update :revn inc)))] - (when save? - (let [features (db/create-array conn "text" (:features file)) - data (blob/encode (:data file))] - (db/update! conn :file - {:data data - :revn (:revn file) - :features features} - {:id id}) + [system & {:keys [update-fn id rollback? migrate? inc-revn?] + :or {rollback? true migrate? true inc-revn? true}}] + (letfn [(process-file [conn {:keys [features] :as file}] + (binding [pmap/*tracked* (atom {}) + pmap/*load-fn* (partial files/load-pointer conn id) + cfeat/*wrap-with-pointer-map-fn* + (if (contains? features "fdata/pointer-map") pmap/wrap identity) + cfeat/*wrap-with-objects-map-fn* + (if (contains? features "fdata/objectd-map") omap/wrap identity)] + + (let [file (cond-> (update-fn file) + inc-revn? (update :revn inc)) + features (db/create-array conn "text" (:features file)) + data (blob/encode (:data file))] + + (db/update! conn :file + {:data data + :revn (:revn file) + :features features} + {:id id})) (when (contains? (:features file) "fdata/pointer-map") - (files/persist-pointers! conn id)))) + (files/persist-pointers! conn id)) - (dissoc file :data)))))) + (dissoc file :data)))] -(def ^:private sql:retrieve-files-chunk - "SELECT id, name, features, created_at, revn, data FROM file - WHERE created_at < ? AND deleted_at is NULL - ORDER BY created_at desc LIMIT ?") + (db/tx-run! system + (fn [{:keys [::db/conn] :as system}] + (binding [*conn* conn *system* system] + (try + (->> (files/get-file conn id :migrate? migrate?) + (process-file conn)) + (finally + (when rollback? + (db/rollback! conn))))))))) (defn analyze-files "Apply a function to all files in the database, reading them in batches. Do not change data. The `on-file` parameter should be a function that receives the file - and the previous state and returns the new state." + and the previous state and returns the new state. + + Emits rollback at the end of operation." [system & {:keys [chunk-size max-items start-at on-file on-error on-end on-init with-libraries?] :or {chunk-size 10 max-items Long/MAX_VALUE}}] (letfn [(get-chunk [conn cursor] - (let [rows (db/exec! conn [sql:retrieve-files-chunk cursor chunk-size])] - [(some->> rows peek :created-at) (seq rows)])) + (let [sql (str "SELECT id, created_at FROM file " + " WHERE created_at < ? AND deleted_at is NULL " + " ORDER BY created_at desc LIMIT ?") + rows (db/exec! conn [sql cursor chunk-size])] + [(some->> rows peek :created-at) (map :id rows)])) (get-candidates [conn] (->> (d/iteration (partial get-chunk conn) :vf second :kf first :initk (or start-at (dt/now))) - (take max-items) - (map #(-> % - (update :data blob/decode) - (update :features db/decode-pgarray #{}))))) + (take max-items))) (on-error* [cause file] (println "unexpected exception happened on processing file: " (:id file)) - (strace/print-stack-trace cause))] + (strace/print-stack-trace cause)) - (when (fn? on-init) (on-init)) + (process-file [conn file-id] + (let [file (binding [pmap/*load-fn* (partial files/load-pointer conn file-id)] + (-> (files/get-file conn file-id) + (files/process-pointers deref))) - (db/with-atomic [conn (:app.db/pool system)] - (doseq [file (get-candidates conn)] - (binding [*conn* conn - pmap/*tracked* (atom {}) - pmap/*load-fn* (partial files/load-pointer conn (:id file)) - cfeat/*wrap-with-pointer-map-fn* - (if (contains? (:features file) "fdata/pointer-map") pmap/wrap identity) - cfeat/*wrap-with-objects-map-fn* - (if (contains? (:features file) "fdata/objects-map") omap/wrap identity)] - (let [libraries (when with-libraries? - (->> (files/get-file-libraries conn (:id file)) - (into [file] (map (fn [{:keys [id]}] - (binding [pmap/*load-fn* (partial files/load-pointer conn id)] - (-> (files-update/get-file conn id) - (update :data blob/decode) - (files/process-pointers deref)))))) ; ensure all pointers resolved - (d/index-by :id)))] - (try - (if with-libraries? - (on-file file libraries) - (on-file file)) - (catch Throwable cause - ((or on-error on-error*) cause file))))))) + libs (when with-libraries? + (->> (files/get-file-libraries conn file-id) + (into [file] (map (fn [{:keys [id]}] + (binding [pmap/*load-fn* (partial files/load-pointer conn id)] + (-> (files/get-file conn id) + (files/process-pointers deref)))))) + (d/index-by :id)))] + (try + (if with-libraries? + (on-file file libs) + (on-file file)) + (catch Throwable cause + ((or on-error on-error*) cause file)))))] - (when (fn? on-end) (on-end)))) + (db/tx-run! system + (fn [{:keys [::db/conn] :as system}] + (try + (binding [*conn* conn *system* system] + (when (fn? on-init) (on-init)) + (run! (partial process-file conn) (get-candidates conn))) + (finally + (when (fn? on-end) + (ex/ignoring (on-end))) + (db/rollback! conn))))))) (defn process-files! "Apply a function to all files in the database, reading them in @@ -246,15 +228,18 @@ on-file on-error on-end - on-init] + on-init + rollback?] :or {chunk-size 10 max-items Long/MAX_VALUE - workers 1}}] - + workers 1 + rollback? true}}] (letfn [(get-chunk [conn cursor] - (let [rows (db/exec! conn [sql:retrieve-files-chunk cursor chunk-size])] - [(some->> rows peek :created-at) - (map #(update % :features db/decode-pgarray #{}) rows)])) + (let [sql (str "SELECT id, created_at FROM file " + " WHERE created_at < ? AND deleted_at is NULL " + " ORDER BY created_at desc LIMIT ?") + rows (db/exec! conn [sql cursor chunk-size])] + [(some->> rows peek :created-at) (map :id rows)])) (get-candidates [conn] (->> (d/iteration (partial get-chunk conn) @@ -267,38 +252,43 @@ (println! "unexpected exception happened on processing file: " (:id file)) (strace/print-stack-trace cause)) - (process-file [conn file] + (process-file [conn file-id] (try - (binding [*conn* conn - pmap/*tracked* (atom {}) - pmap/*load-fn* (partial files/load-pointer conn (:id file)) - cfeat/*wrap-with-pointer-map-fn* - (if (contains? (:features file) "fdata/pointer-map") pmap/wrap identity) - cfeat/*wrap-with-objects-map-fn* - (if (contains? (:features file) "fdata/objectd-map") omap/wrap identity)] - (on-file file)) + (let [{:keys [features] :as file} (files/get-file conn file-id)] + (binding [pmap/*tracked* (atom {}) + pmap/*load-fn* (partial files/load-pointer conn file-id) + cfeat/*wrap-with-pointer-map-fn* + (if (contains? features "fdata/pointer-map") pmap/wrap identity) + cfeat/*wrap-with-objects-map-fn* + (if (contains? features "fdata/objectd-map") omap/wrap identity)] + + (on-file file) + + (when (contains? features "fdata/pointer-map") + (files/persist-pointers! conn file-id)))) + (catch Throwable cause - ((or on-error on-error*) cause file)))) + ((or on-error on-error*) cause file-id)))) (run-worker [in index] - (db/with-atomic [conn pool] - (loop [i 0] - (when-let [file (sp/take! in)] - (println! "=> worker: index:" index "| loop:" i "| file:" (:id file) "|" (px/get-name)) - (process-file conn file) - (recur (inc i)))))) + (db/tx-run! system + (fn [{:keys [::db/conn] :as system}] + (binding [*conn* conn *system* system] + (loop [i 0] + (when-let [file-id (sp/take! in)] + (println! "=> worker: index:" index "| loop:" i "| file:" (str file-id) "|" (px/get-name)) + (process-file conn file-id) + (recur (inc i))))) + + (when rollback? + (db/rollback! conn))))) (run-producer [input] (db/with-atomic [conn pool] - (doseq [file (get-candidates conn)] - (println! "=> producer:" (:id file) "|" (px/get-name)) - (sp/put! input file)) - (sp/close! input))) - - (start-worker [input index] - (px/thread - {:name (str "penpot/srepl/worker/" index)} - (run-worker input index)))] + (doseq [file-id (get-candidates conn)] + (println! "=> producer:" file-id "|" (px/get-name)) + (sp/put! input file-id)) + (sp/close! input)))] (when (fn? on-init) (on-init)) @@ -307,19 +297,12 @@ {:name "penpot/srepl/producer"} (run-producer input)) threads (->> (range workers) - (map (partial start-worker input)) + (map (fn [index] + (px/thread + {:name (str "penpot/srepl/worker/" index)} + (run-worker input index)))) (cons producer) (doall))] (run! p/await! threads) (when (fn? on-end) (on-end))))) - -(defn update-pages - "Apply a function to all pages of one file. The function receives a page and returns an updated page." - [data f] - (update data :pages-index update-vals f)) - -(defn update-shapes - "Apply a function to all shapes of one page The function receives a shape and returns an updated shape" - [page f] - (update page :objects update-vals f)) diff --git a/backend/src/app/srepl/main.clj b/backend/src/app/srepl/main.clj index 8e4ee983b..454bc8778 100644 --- a/backend/src/app/srepl/main.clj +++ b/backend/src/app/srepl/main.clj @@ -8,6 +8,7 @@ "A collection of adhoc fixes scripts." #_:clj-kondo/ignore (:require + [app.auth :refer [derive-password]] [app.common.data :as d] [app.common.data.macros :as dm] [app.common.features :as cfeat] @@ -18,12 +19,12 @@ [app.config :as cf] [app.db :as db] [app.features.fdata :as features.fdata] + [app.main :as main] [app.msgbus :as mbus] [app.rpc.commands.auth :as auth] [app.rpc.commands.files-snapshot :as fsnap] [app.rpc.commands.profile :as profile] [app.srepl.cli :as cli] - [app.srepl.fixes :as f] [app.srepl.helpers :as h] [app.storage :as sto] [app.util.blob :as blob] @@ -112,6 +113,18 @@ (db/delete! conn :http-session {:profile-id (:id profile)}) :blocked)))) +(defn reset-password! + "Reset a password to a specific one for a concrete user or all users + if email is `:all` keyword." + [system & {:keys [email password] :or {password "123123"} :as params}] + (us/verify! (contains? params :email) "`email` parameter is mandatory") + (db/with-atomic [conn (:app.db/pool system)] + (let [password (derive-password password)] + (if (= email :all) + (db/exec! conn ["update profile set password=?" password]) + (let [email (str/lower email)] + (db/exec! conn ["update profile set password=? where email=?" password email])))))) + (defn enable-objects-map-feature-on-file! [system & {:keys [save? id]}] (h/update-file! system