From 283eb0419cc3062738629f7bbeff07babf539ce0 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Wed, 23 Jul 2025 09:47:16 +0200 Subject: [PATCH] :recycle: Refactor time related namespaces Mainly removes the custom app.util.time namespace from frontend and backend and normalize all to use the app.common.time namespace --- .clj-kondo/config.edn | 9 + backend/dev/user.clj | 2 +- backend/src/app/auth/oidc.clj | 8 +- backend/src/app/binfile/common.clj | 7 +- backend/src/app/binfile/v1.clj | 12 +- backend/src/app/binfile/v2.clj | 12 +- backend/src/app/binfile/v3.clj | 14 +- backend/src/app/config.clj | 20 +- backend/src/app/db.clj | 8 +- backend/src/app/features/logical_deletion.clj | 8 +- backend/src/app/http/assets.clj | 6 +- backend/src/app/http/debug.clj | 8 +- backend/src/app/http/session.clj | 36 +- backend/src/app/http/websocket.clj | 6 +- backend/src/app/loggers/audit.clj | 10 +- .../src/app/loggers/audit/archive_task.clj | 4 +- backend/src/app/loggers/webhooks.clj | 8 +- backend/src/app/main.clj | 25 +- backend/src/app/media.clj | 6 +- backend/src/app/msgbus.clj | 4 +- backend/src/app/redis.clj | 10 +- backend/src/app/rpc.clj | 6 +- backend/src/app/rpc/climit.clj | 6 +- backend/src/app/rpc/commands/access_token.clj | 10 +- backend/src/app/rpc/commands/audit.clj | 8 +- backend/src/app/rpc/commands/auth.clj | 20 +- backend/src/app/rpc/commands/binfile.clj | 4 +- backend/src/app/rpc/commands/comments.clj | 4 +- backend/src/app/rpc/commands/demo.clj | 20 +- backend/src/app/rpc/commands/files.clj | 36 +- backend/src/app/rpc/commands/files_create.clj | 4 +- .../src/app/rpc/commands/files_snapshot.clj | 12 +- backend/src/app/rpc/commands/files_temp.clj | 6 +- .../src/app/rpc/commands/files_thumbnails.clj | 10 +- backend/src/app/rpc/commands/files_update.clj | 26 +- backend/src/app/rpc/commands/fonts.clj | 8 +- backend/src/app/rpc/commands/management.clj | 10 +- backend/src/app/rpc/commands/media.clj | 6 +- backend/src/app/rpc/commands/profile.clj | 12 +- backend/src/app/rpc/commands/projects.clj | 6 +- backend/src/app/rpc/commands/teams.clj | 4 +- .../app/rpc/commands/teams_invitations.clj | 14 +- backend/src/app/rpc/commands/verify_token.clj | 6 +- backend/src/app/rpc/commands/webhooks.clj | 4 +- backend/src/app/rpc/quotes.clj | 4 +- backend/src/app/rpc/rlimit.clj | 38 +- backend/src/app/srepl.clj | 4 +- backend/src/app/srepl/cli.clj | 38 +- backend/src/app/srepl/helpers.clj | 6 +- backend/src/app/srepl/main.clj | 30 +- backend/src/app/storage.clj | 16 +- backend/src/app/storage/gc_deleted.clj | 8 +- backend/src/app/storage/s3.clj | 8 +- backend/src/app/storage/tmp.clj | 10 +- backend/src/app/tasks/delete_object.clj | 10 +- backend/src/app/tasks/file_gc.clj | 4 +- backend/src/app/tasks/file_gc_scheduler.clj | 4 +- backend/src/app/tasks/objects_gc.clj | 22 +- backend/src/app/tokens.clj | 8 +- backend/src/app/util/cache.clj | 4 +- backend/src/app/util/cron.clj | 138 ++++++ backend/src/app/util/pointer_map.clj | 12 +- backend/src/app/util/time.clj | 399 ----------------- backend/src/app/util/websocket.clj | 8 +- backend/src/app/worker.clj | 10 +- backend/src/app/worker/cron.clj | 27 +- backend/src/app/worker/dispatcher.clj | 6 +- backend/src/app/worker/executor.clj | 4 +- backend/src/app/worker/runner.clj | 26 +- backend/src/data_readers.clj | 6 +- backend/test/backend_tests/binfile_test.clj | 1 - .../backend_tests/bounce_handling_test.clj | 8 +- backend/test/backend_tests/helpers.clj | 12 +- backend/test/backend_tests/rpc_audit_test.clj | 6 +- .../test/backend_tests/rpc_comment_test.clj | 4 +- .../backend_tests/rpc_file_snapshot_test.clj | 1 - backend/test/backend_tests/rpc_file_test.clj | 6 +- .../rpc_file_thumbnails_test.clj | 1 - backend/test/backend_tests/rpc_media_test.clj | 4 +- .../test/backend_tests/rpc_profile_test.clj | 16 +- .../test/backend_tests/rpc_project_test.clj | 1 - backend/test/backend_tests/rpc_team_test.clj | 22 +- backend/test/backend_tests/storage_test.clj | 20 +- .../backend_tests/tasks_telemetry_test.clj | 1 - common/package.json | 6 +- common/src/app/common/schema.cljc | 61 ++- common/src/app/common/time.cljc | 416 ++++++++++++++++-- common/src/app/common/transit.cljc | 41 +- common/src/app/common/types/color.cljc | 3 +- common/src/app/common/types/component.cljc | 3 +- common/src/app/common/types/container.cljc | 3 +- common/src/app/common/types/file.cljc | 15 +- common/src/app/common/types/tokens_lib.cljc | 34 +- common/src/app/common/types/typography.cljc | 3 +- common/test/common_tests/time_test.cljc | 4 +- .../common_tests/types/tokens_lib_test.cljc | 68 +-- common/yarn.lock | 16 +- frontend/package.json | 1 - frontend/src/app/main/data/changes.cljs | 4 +- frontend/src/app/main/data/comments.cljs | 9 +- frontend/src/app/main/data/dashboard.cljs | 4 +- frontend/src/app/main/data/event.cljs | 8 +- .../src/app/main/data/exports/assets.cljs | 15 +- frontend/src/app/main/data/plugins.cljs | 6 +- .../src/app/main/data/style_dictionary.cljs | 4 +- .../app/main/data/workspace/libraries.cljs | 10 +- .../main/data/workspace/notifications.cljs | 8 +- .../app/main/data/workspace/shape_layout.cljs | 50 +-- .../src/app/main/data/workspace/texts.cljs | 32 +- .../app/main/data/workspace/thumbnails.cljs | 6 +- .../data/workspace/tokens/propagation.cljs | 4 +- .../src/app/main/data/workspace/undo.cljs | 6 +- .../src/app/main/data/workspace/versions.cljs | 6 +- frontend/src/app/main/ui/comments.cljs | 12 +- frontend/src/app/main/ui/dashboard/grid.cljs | 6 +- .../src/app/main/ui/dashboard/projects.cljs | 8 +- .../main/ui/ds/product/user_milestone.cljs | 30 +- .../src/app/main/ui/ds/utilities/date.cljs | 15 +- .../app/main/ui/settings/access_tokens.cljs | 12 +- .../app/main/ui/settings/subscription.cljs | 8 +- .../sidebar/options/menus/border_radius.cljs | 7 +- .../main/ui/workspace/sidebar/versions.cljs | 7 +- .../main/ui/workspace/viewport/presence.cljs | 4 +- frontend/src/app/plugins/file.cljs | 4 +- frontend/src/app/util/cache.cljs | 8 +- frontend/src/app/util/http.cljs | 6 +- frontend/src/app/util/i18n.cljs | 12 +- frontend/src/app/util/queue.cljs | 4 +- frontend/src/app/util/storage.cljs | 6 +- frontend/src/app/util/time.cljs | 307 ------------- frontend/yarn.lock | 8 - library/package.json | 10 +- library/yarn.lock | 94 +--- 133 files changed, 1272 insertions(+), 1560 deletions(-) create mode 100644 backend/src/app/util/cron.clj delete mode 100644 backend/src/app/util/time.clj delete mode 100644 frontend/src/app/util/time.cljs diff --git a/.clj-kondo/config.edn b/.clj-kondo/config.edn index 08c3cd7e8f..1d5149d6b6 100644 --- a/.clj-kondo/config.edn +++ b/.clj-kondo/config.edn @@ -45,10 +45,16 @@ :potok/reify-type {:level :error} + :missing-protocol-method + {:level :off} + :unresolved-namespace {:level :warning :exclude [data_readers]} + :unused-value + {:level :off} + :single-key-in {:level :warning} @@ -64,6 +70,9 @@ :redundant-nested-call {:level :off} + :redundant-str-call + {:level :off} + :earmuffed-var-not-dynamic {:level :off} diff --git a/backend/dev/user.clj b/backend/dev/user.clj index a790c10182..74d042d86b 100644 --- a/backend/dev/user.clj +++ b/backend/dev/user.clj @@ -30,7 +30,7 @@ [app.srepl.helpers :as srepl.helpers] [app.srepl.main :as srepl] [app.util.blob :as blob] - [app.util.time :as dt] + [app.common.time :as ct] [clj-async-profiler.core :as prof] [clojure.contrib.humanize :as hum] [clojure.java.io :as io] diff --git a/backend/src/app/auth/oidc.clj b/backend/src/app/auth/oidc.clj index 42de8ddb8e..1651517083 100644 --- a/backend/src/app/auth/oidc.clj +++ b/backend/src/app/auth/oidc.clj @@ -13,6 +13,7 @@ [app.common.exceptions :as ex] [app.common.logging :as l] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.uri :as u] [app.config :as cf] [app.db :as db] @@ -28,7 +29,6 @@ [app.tokens :as tokens] [app.util.inet :as inet] [app.util.json :as json] - [app.util.time :as dt] [buddy.sign.jwk :as jwk] [buddy.sign.jwt :as jwt] [clojure.set :as set] @@ -514,7 +514,7 @@ [cfg info request] (let [info (assoc info :iss :prepared-register - :exp (dt/in-future {:hours 48})) + :exp (ct/in-future {:hours 48})) params {:token (tokens/generate (::setup/props cfg) info) :provider (:provider (:path-params request)) @@ -571,7 +571,7 @@ token (or (:invitation-token info) (tokens/generate (::setup/props cfg) {:iss :auth - :exp (dt/in-future "15m") + :exp (ct/in-future "15m") :profile-id (:id profile)})) props (audit/profile->props profile) context (d/without-nils {:external-session-id (:external-session-id info)})] @@ -619,7 +619,7 @@ :invitation-token (:invitation-token params) :external-session-id esid :props props - :exp (dt/in-future "4h")} + :exp (ct/in-future "4h")} state (tokens/generate (::setup/props cfg) (d/without-nils params)) uri (build-auth-uri cfg state)] diff --git a/backend/src/app/binfile/common.clj b/backend/src/app/binfile/common.clj index b6457714c5..4f01cfe53b 100644 --- a/backend/src/app/binfile/common.clj +++ b/backend/src/app/binfile/common.clj @@ -16,6 +16,7 @@ [app.common.files.validate :as fval] [app.common.logging :as l] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.types.file :as ctf] [app.common.uuid :as uuid] [app.config :as cf] @@ -28,7 +29,6 @@ [app.storage :as sto] [app.util.blob :as blob] [app.util.pointer-map :as pmap] - [app.util.time :as dt] [app.worker :as-alias wrk] [clojure.set :as set] [cuerdas.core :as str] @@ -540,14 +540,13 @@ Returns nil" [{:keys [::timestamp] :as cfg} file & {:as opts}] + (assert (ct/inst? timestamp) "expected valid timestamp") - (assert (dt/instant? timestamp) "expected valid timestamp") (let [file (-> file (assoc :created-at timestamp) (assoc :modified-at timestamp) (cond-> (not (::overwrite cfg)) - (assoc :ignore-sync-until (dt/plus timestamp (dt/duration {:seconds 5})))) - (update :revn inc) + (assoc :ignore-sync-until (ct/plus timestamp (ct/duration {:seconds 5})))) (update :features (fn [features] (-> (::features cfg #{}) diff --git a/backend/src/app/binfile/v1.clj b/backend/src/app/binfile/v1.clj index e73f2c05a3..d78b97b3c5 100644 --- a/backend/src/app/binfile/v1.clj +++ b/backend/src/app/binfile/v1.clj @@ -17,6 +17,7 @@ [app.common.fressian :as fres] [app.common.logging :as l] [app.common.spec :as us] + [app.common.time :as ct] [app.common.types.file :as ctf] [app.common.uuid :as uuid] [app.config :as cf] @@ -30,7 +31,6 @@ [app.storage.tmp :as tmp] [app.tasks.file-gc] [app.util.events :as events] - [app.util.time :as dt] [app.worker :as-alias wrk] [clojure.java.io :as jio] [clojure.set :as set] @@ -434,7 +434,7 @@ (defn read-import! "Do the importation of the specified resource in penpot custom binary format." - [{:keys [::bfc/input ::bfc/timestamp] :or {timestamp (dt/now)} :as options}] + [{:keys [::bfc/input ::bfc/timestamp] :or {timestamp (ct/now)} :as options}] (dm/assert! "expected input stream" @@ -442,7 +442,7 @@ (dm/assert! "expected valid instant" - (dt/instant? timestamp)) + (ct/inst? timestamp)) (let [version (read-header! input)] (read-import (assoc options ::version version ::bfc/timestamp timestamp)))) @@ -682,7 +682,7 @@ (io/coercible? output)) (let [id (uuid/next) - tp (dt/tpoint) + tp (ct/tpoint) ab (volatile! false) cs (volatile! nil)] (try @@ -720,7 +720,7 @@ (satisfies? jio/IOFactory input)) (let [id (uuid/next) - tp (dt/tpoint) + tp (ct/tpoint) cs (volatile! nil)] (l/info :hint "import: started" :id (str id)) @@ -742,6 +742,6 @@ (finally (l/info :hint "import: terminated" :id (str id) - :elapsed (dt/format-duration (tp)) + :elapsed (ct/format-duration (tp)) :error? (some? @cs)))))) diff --git a/backend/src/app/binfile/v2.clj b/backend/src/app/binfile/v2.clj index 06e22bf422..93cd16c211 100644 --- a/backend/src/app/binfile/v2.clj +++ b/backend/src/app/binfile/v2.clj @@ -13,6 +13,7 @@ [app.common.data :as d] [app.common.features :as cfeat] [app.common.logging :as l] + [app.common.time :as ct] [app.common.transit :as t] [app.common.uuid :as uuid] [app.config :as cf] @@ -23,7 +24,6 @@ [app.storage :as sto] [app.storage.tmp :as tmp] [app.util.events :as events] - [app.util.time :as dt] [app.worker :as-alias wrk] [clojure.set :as set] [cuerdas.core :as str] @@ -344,7 +344,7 @@ (defn export-team! [cfg team-id] (let [id (uuid/next) - tp (dt/tpoint) + tp (ct/tpoint) cfg (create-database cfg)] (l/inf :hint "start" @@ -378,15 +378,15 @@ (l/inf :hint "end" :operation "export" :id (str id) - :elapsed (dt/format-duration elapsed))))))) + :elapsed (ct/format-duration elapsed))))))) (defn import-team! [cfg path] (let [id (uuid/next) - tp (dt/tpoint) + tp (ct/tpoint) cfg (-> (create-database cfg path) - (assoc ::bfc/timestamp (dt/now)))] + (assoc ::bfc/timestamp (ct/now)))] (l/inf :hint "start" :operation "import" @@ -434,4 +434,4 @@ (l/inf :hint "end" :operation "import" :id (str id) - :elapsed (dt/format-duration elapsed))))))) + :elapsed (ct/format-duration elapsed))))))) diff --git a/backend/src/app/binfile/v3.clj b/backend/src/app/binfile/v3.clj index 4db81f7524..e8c9f8265f 100644 --- a/backend/src/app/binfile/v3.clj +++ b/backend/src/app/binfile/v3.clj @@ -20,6 +20,7 @@ [app.common.media :as cmedia] [app.common.schema :as sm] [app.common.thumbnails :as cth] + [app.common.time :as ct] [app.common.types.color :as ctcl] [app.common.types.component :as ctc] [app.common.types.file :as ctf] @@ -35,7 +36,6 @@ [app.storage :as sto] [app.storage.impl :as sto.impl] [app.util.events :as events] - [app.util.time :as dt] [clojure.java.io :as jio] [cuerdas.core :as str] [datoteka.fs :as fs] @@ -92,7 +92,7 @@ (defn- default-now [o] - (or o (dt/now))) + (or o (ct/now))) ;; --- ENCODERS @@ -937,10 +937,10 @@ [file-id]))) (defn- import-files - [{:keys [::bfc/timestamp ::bfc/input] :or {timestamp (dt/now)} :as cfg}] + [{:keys [::bfc/timestamp ::bfc/input] :or {timestamp (ct/now)} :as cfg}] (assert (instance? ZipFile input) "expected zip file") - (assert (dt/instant? timestamp) "expected valid instant") + (assert (ct/inst? timestamp) "expected valid instant") (let [manifest (-> (read-manifest input) (validate-manifest)) @@ -1000,7 +1000,7 @@ "expected instance of jio/IOFactory for `input`") (let [id (uuid/next) - tp (dt/tpoint) + tp (ct/tpoint) ab (volatile! false) cs (volatile! nil)] (try @@ -1046,7 +1046,7 @@ "expected instance of jio/IOFactory for `input`") (let [id (uuid/next) - tp (dt/tpoint) + tp (ct/tpoint) cs (volatile! nil)] (l/info :hint "import: started" :id (str id)) @@ -1061,7 +1061,7 @@ (finally (l/info :hint "import: terminated" :id (str id) - :elapsed (dt/format-duration (tp)) + :elapsed (ct/format-duration (tp)) :error? (some? @cs)))))) (defn get-manifest diff --git a/backend/src/app/config.clj b/backend/src/app/config.clj index 07f80d431c..805608da5f 100644 --- a/backend/src/app/config.clj +++ b/backend/src/app/config.clj @@ -12,10 +12,10 @@ [app.common.exceptions :as ex] [app.common.flags :as flags] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.uri :as u] [app.common.version :as v] [app.util.overrides] - [app.util.time :as dt] [clojure.core :as c] [clojure.java.io :as io] [cuerdas.core :as str] @@ -59,10 +59,10 @@ :smtp-default-reply-to "Penpot " :smtp-default-from "Penpot " - :profile-complaint-max-age (dt/duration {:days 7}) + :profile-complaint-max-age (ct/duration {:days 7}) :profile-complaint-threshold 2 - :profile-bounce-max-age (dt/duration {:days 7}) + :profile-bounce-max-age (ct/duration {:days 7}) :profile-bounce-threshold 10 :telemetry-uri "https://telemetry.penpot.app/" @@ -102,10 +102,10 @@ [:telemetry-with-taiga {:optional true} ::sm/boolean] ;; DELETE [:auto-file-snapshot-every {:optional true} ::sm/int] - [:auto-file-snapshot-timeout {:optional true} ::dt/duration] + [:auto-file-snapshot-timeout {:optional true} ::ct/duration] [:media-max-file-size {:optional true} ::sm/int] - [:deletion-delay {:optional true} ::dt/duration] ;; REVIEW + [:deletion-delay {:optional true} ::ct/duration] ;; REVIEW [:telemetry-enabled {:optional true} ::sm/boolean] [:default-blob-version {:optional true} ::sm/int] [:allow-demo-users {:optional true} ::sm/boolean] @@ -148,10 +148,10 @@ [:auth-data-cookie-domain {:optional true} :string] [:auth-token-cookie-name {:optional true} :string] - [:auth-token-cookie-max-age {:optional true} ::dt/duration] + [:auth-token-cookie-max-age {:optional true} ::ct/duration] [:registration-domain-whitelist {:optional true} [::sm/set :string]] - [:email-verify-threshold {:optional true} ::dt/duration] + [:email-verify-threshold {:optional true} ::ct/duration] [:github-client-id {:optional true} :string] [:github-client-secret {:optional true} :string] @@ -186,9 +186,9 @@ [:ldap-starttls {:optional true} ::sm/boolean] [:ldap-user-query {:optional true} :string] - [:profile-bounce-max-age {:optional true} ::dt/duration] + [:profile-bounce-max-age {:optional true} ::ct/duration] [:profile-bounce-threshold {:optional true} ::sm/int] - [:profile-complaint-max-age {:optional true} ::dt/duration] + [:profile-complaint-max-age {:optional true} ::ct/duration] [:profile-complaint-threshold {:optional true} ::sm/int] [:redis-uri {:optional true} ::sm/uri] @@ -298,7 +298,7 @@ (defn get-deletion-delay [] (or (c/get config :deletion-delay) - (dt/duration {:days 7}))) + (ct/duration {:days 7}))) (defn get "A configuration getter. Helps code be more testable." diff --git a/backend/src/app/db.clj b/backend/src/app/db.clj index e17bc21b25..4bf4c6a91b 100644 --- a/backend/src/app/db.clj +++ b/backend/src/app/db.clj @@ -13,11 +13,11 @@ [app.common.json :as json] [app.common.logging :as l] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.transit :as t] [app.common.uuid :as uuid] [app.db.sql :as sql] [app.metrics :as mtx] - [app.util.time :as dt] [clojure.java.io :as io] [clojure.set :as set] [integrant.core :as ig] @@ -379,9 +379,9 @@ (defn is-row-deleted? [{:keys [deleted-at]}] - (and (dt/instant? deleted-at) + (and (ct/inst? deleted-at) (< (inst-ms deleted-at) - (inst-ms (dt/now))))) + (inst-ms (ct/now))))) (defn get* "Retrieve a single row from database that matches a simple filters. Do @@ -605,7 +605,7 @@ (string? o) (pginterval o) - (dt/duration? o) + (ct/duration? o) (interval (inst-ms o)) :else diff --git a/backend/src/app/features/logical_deletion.clj b/backend/src/app/features/logical_deletion.clj index a8407cdf62..5f08ed71f7 100644 --- a/backend/src/app/features/logical_deletion.clj +++ b/backend/src/app/features/logical_deletion.clj @@ -7,8 +7,8 @@ (ns app.features.logical-deletion "A code related to handle logical deletion mechanism" (:require - [app.config :as cf] - [app.util.time :as dt])) + [app.common.time :as ct] + [app.config :as cf])) (def ^:private canceled-status #{"canceled" "unpaid"}) @@ -20,10 +20,10 @@ (if-let [{:keys [type status]} (get team :subscription)] (cond (and (= "unlimited" type) (not (contains? canceled-status status))) - (dt/duration {:days 30}) + (ct/duration {:days 30}) (and (= "enterprise" type) (not (contains? canceled-status status))) - (dt/duration {:days 90}) + (ct/duration {:days 90}) :else (cf/get-deletion-delay)) diff --git a/backend/src/app/http/assets.clj b/backend/src/app/http/assets.clj index 5e7da3e003..7a33e17527 100644 --- a/backend/src/app/http/assets.clj +++ b/backend/src/app/http/assets.clj @@ -9,18 +9,18 @@ (:require [app.common.data :as d] [app.common.exceptions :as ex] + [app.common.time :as ct] [app.common.uri :as u] [app.db :as db] [app.storage :as sto] - [app.util.time :as dt] [integrant.core :as ig] [yetti.response :as-alias yres])) (def ^:private cache-max-age - (dt/duration {:hours 24})) + (ct/duration {:hours 24})) (def ^:private signature-max-age - (dt/duration {:hours 24 :minutes 15})) + (ct/duration {:hours 24 :minutes 15})) (defn get-id [{:keys [path-params]}] diff --git a/backend/src/app/http/debug.clj b/backend/src/app/http/debug.clj index f4d835b417..76d7661bdf 100644 --- a/backend/src/app/http/debug.clj +++ b/backend/src/app/http/debug.clj @@ -15,6 +15,7 @@ [app.common.features :as cfeat] [app.common.logging :as l] [app.common.pprint :as pp] + [app.common.time :as ct] [app.common.transit :as t] [app.common.uuid :as uuid] [app.config :as cf] @@ -31,7 +32,6 @@ [app.storage.tmp :as tmp] [app.util.blob :as blob] [app.util.template :as tmpl] - [app.util.time :as dt] [cuerdas.core :as str] [datoteka.io :as io] [emoji.core :as emj] @@ -137,7 +137,7 @@ file (some-> params :file :path io/read* t/decode)] (if (and file project-id) - (let [fname (str "Imported: " (:name file) "(" (dt/now) ")") + (let [fname (str "Imported: " (:name file) "(" (ct/now) ")") reuse-id? (contains? params :reuseid) file-id (or (and reuse-id? (ex/ignoring (-> params :file :filename parse-uuid))) (uuid/next))] @@ -222,7 +222,7 @@ (-> (io/resource "app/templates/error-report.v3.tmpl") (tmpl/render (-> content (assoc :id id) - (assoc :created-at (dt/format-instant created-at :rfc1123))))))] + (assoc :created-at (ct/format-inst created-at :rfc1123))))))] (if-let [report (get-report request)] (let [result (case (:version report) @@ -246,7 +246,7 @@ (defn error-list-handler [{:keys [::db/pool]} _request] (let [items (->> (db/exec! pool [sql:error-reports]) - (map #(update % :created-at dt/format-instant :rfc1123)))] + (map #(update % :created-at ct/format-inst :rfc1123)))] {::yres/status 200 ::yres/body (-> (io/resource "app/templates/error-list.tmpl") (tmpl/render {:items items})) diff --git a/backend/src/app/http/session.clj b/backend/src/app/http/session.clj index 29528bddbd..d3bc0bd2bd 100644 --- a/backend/src/app/http/session.clj +++ b/backend/src/app/http/session.clj @@ -10,6 +10,7 @@ [app.common.data :as d] [app.common.logging :as l] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.uri :as u] [app.config :as cf] [app.db :as db] @@ -18,7 +19,6 @@ [app.main :as-alias main] [app.setup :as-alias setup] [app.tokens :as tokens] - [app.util.time :as dt] [cuerdas.core :as str] [integrant.core :as ig] [yetti.request :as yreq])) @@ -35,10 +35,10 @@ (def default-auth-data-cookie-name "auth-data") ;; Default value for cookie max-age -(def default-cookie-max-age (dt/duration {:days 7})) +(def default-cookie-max-age (ct/duration {:days 7})) ;; Default age for automatic session renewal -(def default-renewal-max-age (dt/duration {:hours 6})) +(def default-renewal-max-age (ct/duration {:hours 6})) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; PROTOCOLS @@ -66,7 +66,7 @@ [:map {:title "session-params"} [:user-agent ::sm/text] [:profile-id ::sm/uuid] - [:created-at ::sm/inst]]) + [:created-at ::ct/inst]]) (def ^:private valid-params? (sm/validator schema:params)) @@ -95,7 +95,7 @@ params)) (update! [_ params] - (let [updated-at (dt/now)] + (let [updated-at (ct/now)] (db/update! pool :http-session {:updated-at updated-at} {:id (:id params)}) @@ -118,7 +118,7 @@ params)) (update! [_ params] - (let [updated-at (dt/now)] + (let [updated-at (ct/now)] (swap! cache update (:id params) assoc :updated-at updated-at) (assoc params :updated-at updated-at))) @@ -158,7 +158,7 @@ (let [uagent (yreq/get-header request "user-agent") params {:profile-id profile-id :user-agent uagent - :created-at (dt/now)} + :created-at (ct/now)} token (gen-token props params) session (write! manager token params)] (l/trace :hint "create" :profile-id (str profile-id)) @@ -203,8 +203,8 @@ (defn- renew-session? [{:keys [updated-at] :as session}] - (and (dt/instant? updated-at) - (let [elapsed (dt/diff updated-at (dt/now))] + (and (ct/inst? updated-at) + (let [elapsed (ct/diff updated-at (ct/now))] (neg? (compare default-renewal-max-age elapsed))))) (defn- wrap-soft-auth @@ -256,14 +256,14 @@ (defn- assign-auth-token-cookie [response {token :id updated-at :updated-at}] (let [max-age (cf/get :auth-token-cookie-max-age default-cookie-max-age) - created-at (or updated-at (dt/now)) - renewal (dt/plus created-at default-renewal-max-age) - expires (dt/plus created-at max-age) + created-at (or updated-at (ct/now)) + renewal (ct/plus created-at default-renewal-max-age) + expires (ct/plus created-at max-age) secure? (contains? cf/flags :secure-session-cookies) strict? (contains? cf/flags :strict-session-cookies) cors? (contains? cf/flags :cors) name (cf/get :auth-token-cookie-name default-auth-token-cookie-name) - comment (str "Renewal at: " (dt/format-instant renewal :rfc1123)) + comment (str "Renewal at: " (ct/format-inst renewal :rfc1123)) cookie {:path "/" :http-only true :expires expires @@ -279,11 +279,11 @@ domain (cf/get :auth-data-cookie-domain) cname default-auth-data-cookie-name - created-at (or updated-at (dt/now)) - renewal (dt/plus created-at default-renewal-max-age) - expires (dt/plus created-at max-age) + created-at (or updated-at (ct/now)) + renewal (ct/plus created-at default-renewal-max-age) + expires (ct/plus created-at max-age) - comment (str "Renewal at: " (dt/format-instant renewal :rfc1123)) + comment (str "Renewal at: " (ct/format-inst renewal :rfc1123)) secure? (contains? cf/flags :secure-session-cookies) strict? (contains? cf/flags :strict-session-cookies) cors? (contains? cf/flags :cors) @@ -323,7 +323,7 @@ (defmethod ig/assert-key ::tasks/gc [_ params] (assert (db/pool? (::db/pool params)) "expected valid database pool") - (assert (dt/duration? (::tasks/max-age params)))) + (assert (ct/duration? (::tasks/max-age params)))) (defmethod ig/expand-key ::tasks/gc [k v] diff --git a/backend/src/app/http/websocket.clj b/backend/src/app/http/websocket.clj index a93fd33cb3..2b6e6aa197 100644 --- a/backend/src/app/http/websocket.clj +++ b/backend/src/app/http/websocket.clj @@ -11,12 +11,12 @@ [app.common.logging :as l] [app.common.pprint :as pp] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.uuid :as uuid] [app.db :as db] [app.http.session :as session] [app.metrics :as mtx] [app.msgbus :as mbus] - [app.util.time :as dt] [app.util.websocket :as ws] [integrant.core :as ig] [promesa.exec.csp :as sp] @@ -239,7 +239,7 @@ (defn- on-connect [{:keys [::mtx/metrics]} {:keys [::ws/id] :as wsp}] - (let [created-at (dt/now)] + (let [created-at (ct/now)] (l/trace :fn "on-connect" :conn-id id) (swap! state assoc id wsp) (mtx/run! metrics @@ -253,7 +253,7 @@ (mtx/run! metrics :id :websocket-active-connections :dec 1) (mtx/run! metrics :id :websocket-session-timing - :val (/ (inst-ms (dt/diff created-at (dt/now))) 1000.0)))))) + :val (/ (inst-ms (ct/diff created-at (ct/now))) 1000.0)))))) (defn- on-rcv-message [{:keys [::mtx/metrics ::profile-id ::session-id]} message] diff --git a/backend/src/app/loggers/audit.clj b/backend/src/app/loggers/audit.clj index 20d300ea7f..46e50662c8 100644 --- a/backend/src/app/loggers/audit.clj +++ b/backend/src/app/loggers/audit.clj @@ -11,6 +11,7 @@ [app.common.data.macros :as dm] [app.common.logging :as l] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.uuid :as uuid] [app.config :as cf] [app.db :as db] @@ -23,7 +24,6 @@ [app.setup :as-alias setup] [app.util.inet :as inet] [app.util.services :as-alias sv] - [app.util.time :as dt] [app.worker :as wrk] [cuerdas.core :as str])) @@ -108,9 +108,9 @@ [::ip-addr {:optional true} ::sm/text] [::props {:optional true} [:map-of :keyword :any]] [::context {:optional true} [:map-of :keyword :any]] - [::tracked-at {:optional true} ::sm/inst] + [::tracked-at {:optional true} ::ct/inst] [::webhooks/event? {:optional true} ::sm/boolean] - [::webhooks/batch-timeout {:optional true} ::dt/duration] + [::webhooks/batch-timeout {:optional true} ::ct/duration] [::webhooks/batch-key {:optional true} [:or ::sm/fn ::sm/text :keyword]]]) @@ -199,7 +199,7 @@ (defn- handle-event! [cfg event] (let [params (event->params event) - tnow (dt/now)] + tnow (ct/now)] (when (contains? cf/flags :audit-log) ;; NOTE: this operation may cause primary key conflicts on inserts @@ -273,7 +273,7 @@ (let [event (-> (d/without-nils event) (check-event))] (db/run! cfg (fn [cfg] - (let [tnow (dt/now) + (let [tnow (ct/now) params (-> (event->params event) (assoc :created-at tnow) (update :tracked-at #(or % tnow)))] diff --git a/backend/src/app/loggers/audit/archive_task.clj b/backend/src/app/loggers/audit/archive_task.clj index fd745f8d6b..257f55a94e 100644 --- a/backend/src/app/loggers/audit/archive_task.clj +++ b/backend/src/app/loggers/audit/archive_task.clj @@ -9,6 +9,7 @@ [app.common.exceptions :as ex] [app.common.logging :as l] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.transit :as t] [app.common.uuid :as uuid] [app.config :as cf] @@ -16,7 +17,6 @@ [app.http.client :as http] [app.setup :as-alias setup] [app.tokens :as tokens] - [app.util.time :as dt] [integrant.core :as ig] [lambdaisland.uri :as u] [promesa.exec :as px])) @@ -55,7 +55,7 @@ [{:keys [::uri] :as cfg} events] (let [token (tokens/generate (::setup/props cfg) {:iss "authentication" - :iat (dt/now) + :iat (ct/now) :uid uuid/zero}) body (t/encode {:events events}) headers {"content-type" "application/transit+json" diff --git a/backend/src/app/loggers/webhooks.clj b/backend/src/app/loggers/webhooks.clj index 1b8305504d..8edb646260 100644 --- a/backend/src/app/loggers/webhooks.clj +++ b/backend/src/app/loggers/webhooks.clj @@ -10,13 +10,13 @@ [app.common.data :as d] [app.common.data.macros :as dm] [app.common.logging :as l] + [app.common.time :as ct] [app.common.transit :as t] [app.common.uri :as uri] [app.config :as cf] [app.db :as db] [app.http.client :as http] [app.loggers.audit :as audit] - [app.util.time :as dt] [app.worker :as wrk] [clojure.data.json :as json] [cuerdas.core :as str] @@ -124,7 +124,7 @@ {:id (:id whook)}))) (db/update! pool :webhook - {:updated-at (dt/now) + {:updated-at (ct/now) :error-code nil :error-count 0} {:id (:id whook)}))) @@ -132,7 +132,7 @@ (report-delivery! [whook req rsp err] (db/insert! pool :webhook-delivery {:webhook-id (:id whook) - :created-at (dt/now) + :created-at (ct/now) :error-code err :req-data (db/tjson req) :rsp-data (db/tjson rsp)}))] @@ -155,7 +155,7 @@ (let [req {:uri (:uri whook) :headers {"content-type" (:mtype whook) "user-agent" (str/ffmt "penpot/%" (:main cf/version))} - :timeout (dt/duration "4s") + :timeout (ct/duration "4s") :method :post :body body}] (try diff --git a/backend/src/app/main.clj b/backend/src/app/main.clj index c51d85f6ad..d1eca0ff2b 100644 --- a/backend/src/app/main.clj +++ b/backend/src/app/main.clj @@ -11,6 +11,7 @@ [app.auth.oidc.providers :as-alias oidc.providers] [app.common.exceptions :as ex] [app.common.logging :as l] + [app.common.time :as ct] [app.config :as cf] [app.db :as-alias db] [app.email :as-alias email] @@ -38,7 +39,7 @@ [app.storage.gc-touched :as-alias sto.gc-touched] [app.storage.s3 :as-alias sto.s3] [app.svgo :as-alias svgo] - [app.util.time :as dt] + [app.util.cron] [app.worker :as-alias wrk] [clojure.test :as test] [clojure.tools.namespace.repl :as repl] @@ -298,8 +299,8 @@ :app.http.assets/routes {::http.assets/path (cf/get :assets-path) - ::http.assets/cache-max-age (dt/duration {:hours 24}) - ::http.assets/cache-max-agesignature-max-age (dt/duration {:hours 24 :minutes 5}) + ::http.assets/cache-max-age (ct/duration {:hours 24}) + ::http.assets/cache-max-agesignature-max-age (ct/duration {:hours 24 :minutes 5}) ::sto/storage (ig/ref ::sto/storage)} ::rpc/climit @@ -480,33 +481,33 @@ {::wrk/registry (ig/ref ::wrk/registry) ::db/pool (ig/ref ::db/pool) ::wrk/entries - [{:cron #app/cron "0 0 0 * * ?" ;; daily + [{:cron #penpot/cron "0 0 0 * * ?" ;; daily :task :session-gc} - {:cron #app/cron "0 0 0 * * ?" ;; daily + {:cron #penpot/cron "0 0 0 * * ?" ;; daily :task :objects-gc} - {:cron #app/cron "0 0 0 * * ?" ;; daily + {:cron #penpot/cron "0 0 0 * * ?" ;; daily :task :storage-gc-deleted} - {:cron #app/cron "0 0 0 * * ?" ;; daily + {:cron #penpot/cron "0 0 0 * * ?" ;; daily :task :storage-gc-touched} - {:cron #app/cron "0 0 0 * * ?" ;; daily + {:cron #penpot/cron "0 0 0 * * ?" ;; daily :task :tasks-gc} - {:cron #app/cron "0 0 2 * * ?" ;; daily + {:cron #penpot/cron "0 0 2 * * ?" ;; daily :task :file-gc-scheduler} - {:cron #app/cron "0 30 */3,23 * * ?" + {:cron #penpot/cron "0 30 */3,23 * * ?" :task :telemetry} (when (contains? cf/flags :audit-log-archive) - {:cron #app/cron "0 */5 * * * ?" ;; every 5m + {:cron #penpot/cron "0 */5 * * * ?" ;; every 5m :task :audit-log-archive}) (when (contains? cf/flags :audit-log-gc) - {:cron #app/cron "30 */5 * * * ?" ;; every 5m + {:cron #penpot/cron "30 */5 * * * ?" ;; every 5m :task :audit-log-gc})]} ::wrk/dispatcher diff --git a/backend/src/app/media.clj b/backend/src/app/media.clj index 51649db3d5..fafcccd2dc 100644 --- a/backend/src/app/media.clj +++ b/backend/src/app/media.clj @@ -14,11 +14,11 @@ [app.common.media :as cm] [app.common.schema :as sm] [app.common.schema.openapi :as-alias oapi] + [app.common.time :as ct] [app.config :as cf] [app.db :as-alias db] [app.storage :as-alias sto] [app.storage.tmp :as tmp] - [app.util.time :as dt] [buddy.core.bytes :as bb] [buddy.core.codecs :as bc] [clojure.java.shell :as sh] @@ -243,7 +243,7 @@ (ex/raise :type :validation :code :invalid-svg-file :hint "uploaded svg does not provides dimensions")) - (merge input info {:ts (dt/now)})) + (merge input info {:ts (ct/now)})) (let [instance (Info. (str path)) mtype' (.getProperty instance "Mime type")] @@ -263,7 +263,7 @@ (assoc input :width width :height height - :ts (dt/now))))))) + :ts (ct/now))))))) (defmethod process-error org.im4java.core.InfoException [error] diff --git a/backend/src/app/msgbus.clj b/backend/src/app/msgbus.clj index 11de69541d..b58a51103b 100644 --- a/backend/src/app/msgbus.clj +++ b/backend/src/app/msgbus.clj @@ -10,10 +10,10 @@ [app.common.data :as d] [app.common.logging :as l] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.transit :as t] [app.config :as cfg] [app.redis :as rds] - [app.util.time :as dt] [app.worker :as wrk] [integrant.core :as ig] [promesa.core :as p] @@ -56,7 +56,7 @@ [k v] {k (-> (d/without-nils v) (assoc ::buffer-size 128) - (assoc ::timeout (dt/duration {:seconds 30})))}) + (assoc ::timeout (ct/duration {:seconds 30})))}) (def ^:private schema:params [:map ::rds/redis ::wrk/executor]) diff --git a/backend/src/app/redis.clj b/backend/src/app/redis.clj index cabefd73c9..510069e47c 100644 --- a/backend/src/app/redis.clj +++ b/backend/src/app/redis.clj @@ -12,10 +12,10 @@ [app.common.exceptions :as ex] [app.common.logging :as l] [app.common.schema :as sm] + [app.common.time :as ct] [app.metrics :as mtx] [app.redis.script :as-alias rscript] [app.util.cache :as cache] - [app.util.time :as dt] [app.worker :as-alias wrk] [clojure.core :as c] [clojure.java.io :as io] @@ -114,7 +114,7 @@ (let [cpus (px/get-available-processors) threads (max 1 (int (* cpus 0.2)))] {k (-> (d/without-nils v) - (assoc ::timeout (dt/duration "10s")) + (assoc ::timeout (ct/duration "10s")) (assoc ::io-threads (max 3 threads)) (assoc ::worker-threads (max 3 threads)))})) @@ -125,7 +125,7 @@ [::uri ::sm/uri] [::worker-threads ::sm/int] [::io-threads ::sm/int] - [::timeout ::dt/duration]]) + [::timeout ::ct/duration]]) (defmethod ig/assert-key ::redis [_ params] @@ -331,7 +331,7 @@ (p/rejected cause)))) (eval-script [sha] - (let [tpoint (dt/tpoint)] + (let [tpoint (ct/tpoint)] (->> (.evalsha ^RedisScriptingAsyncCommands cmd ^String sha ^ScriptOutputType ScriptOutputType/MULTI @@ -346,7 +346,7 @@ :name (name sname) :sha sha :params (str/join "," (::rscript/vals script)) - :elapsed (dt/format-duration elapsed)) + :elapsed (ct/format-duration elapsed)) result))) (p/merr on-error)))) diff --git a/backend/src/app/rpc.clj b/backend/src/app/rpc.clj index 5dfa56f13a..94537a9708 100644 --- a/backend/src/app/rpc.clj +++ b/backend/src/app/rpc.clj @@ -12,6 +12,7 @@ [app.common.logging :as l] [app.common.schema :as sm] [app.common.spec :as us] + [app.common.time :as ct] [app.config :as cf] [app.db :as db] [app.http :as-alias http] @@ -31,7 +32,6 @@ [app.storage :as-alias sto] [app.util.inet :as inet] [app.util.services :as sv] - [app.util.time :as dt] [clojure.spec.alpha :as s] [cuerdas.core :as str] [integrant.core :as ig] @@ -103,7 +103,7 @@ data (-> params (assoc ::handler-name handler-name) (assoc ::ip-addr ip-addr) - (assoc ::request-at (dt/now)) + (assoc ::request-at (ct/now)) (assoc ::external-session-id session-id) (assoc ::external-event-origin event-origin) (assoc ::session/id (::session/id request)) @@ -130,7 +130,7 @@ [{:keys [::mtx/metrics ::metrics-id]} f mdata] (let [labels (into-array String [(::sv/name mdata)])] (fn [cfg params] - (let [tp (dt/tpoint)] + (let [tp (ct/tpoint)] (try (f cfg params) (finally diff --git a/backend/src/app/rpc/climit.clj b/backend/src/app/rpc/climit.clj index bb3db5ba58..a56b15d402 100644 --- a/backend/src/app/rpc/climit.clj +++ b/backend/src/app/rpc/climit.clj @@ -11,11 +11,11 @@ [app.common.exceptions :as ex] [app.common.logging :as l] [app.common.schema :as sm] + [app.common.time :as ct] [app.metrics :as mtx] [app.rpc :as-alias rpc] [app.util.cache :as cache] [app.util.services :as-alias sv] - [app.util.time :as dt] [app.worker :as-alias wrk] [clojure.edn :as edn] [clojure.set :as set] @@ -154,7 +154,7 @@ :id limit-id :label limit-label :queue queue - :elapsed (some-> elapsed dt/format-duration) + :elapsed (some-> elapsed ct/format-duration) :params @limit-params))) (def ^:private idseq (AtomicLong. 0)) @@ -171,7 +171,7 @@ mlabels (into-array String [(id->str limit-id)]) limit-id (id->str limit-id limit-key) limiter (cache/get cache limit-id (partial create-limiter config)) - tpoint (dt/tpoint) + tpoint (ct/tpoint) req-id (.incrementAndGet ^AtomicLong idseq)] (try (let [stats (pbh/get-stats limiter)] diff --git a/backend/src/app/rpc/commands/access_token.clj b/backend/src/app/rpc/commands/access_token.clj index 3585e07d76..d91e39c86b 100644 --- a/backend/src/app/rpc/commands/access_token.clj +++ b/backend/src/app/rpc/commands/access_token.clj @@ -7,6 +7,7 @@ (ns app.rpc.commands.access-token (:require [app.common.schema :as sm] + [app.common.time :as ct] [app.common.uuid :as uuid] [app.db :as db] [app.main :as-alias main] @@ -15,8 +16,7 @@ [app.rpc.quotes :as quotes] [app.setup :as-alias setup] [app.tokens :as tokens] - [app.util.services :as sv] - [app.util.time :as dt])) + [app.util.services :as sv])) (defn- decode-row [row] @@ -24,13 +24,13 @@ (defn create-access-token [{:keys [::db/conn ::setup/props]} profile-id name expiration] - (let [created-at (dt/now) + (let [created-at (ct/now) token-id (uuid/next) token (tokens/generate props {:iss "access-token" :tid token-id :iat created-at}) - expires-at (some-> expiration dt/in-future) + expires-at (some-> expiration ct/in-future) token (db/insert! conn :access-token {:id token-id :name name @@ -49,7 +49,7 @@ (def ^:private schema:create-access-token [:map {:title "create-access-token"} [:name [:string {:max 250 :min 1}]] - [:expiration {:optional true} ::dt/duration]]) + [:expiration {:optional true} ::ct/duration]]) (sv/defmethod ::create-access-token {::doc/added "1.18" diff --git a/backend/src/app/rpc/commands/audit.clj b/backend/src/app/rpc/commands/audit.clj index a9bed1db44..e1e5a5ef3f 100644 --- a/backend/src/app/rpc/commands/audit.clj +++ b/backend/src/app/rpc/commands/audit.clj @@ -10,6 +10,7 @@ [app.common.data :as d] [app.common.logging :as l] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.uuid :as uuid] [app.config :as cf] [app.db :as db] @@ -20,8 +21,7 @@ [app.rpc.doc :as-alias doc] [app.rpc.helpers :as rph] [app.util.inet :as inet] - [app.util.services :as sv] - [app.util.time :as dt])) + [app.util.services :as sv])) (def ^:private event-columns [:id @@ -49,7 +49,7 @@ (defn- adjust-timestamp [{:keys [timestamp created-at] :as event}] - (let [margin (inst-ms (dt/diff timestamp created-at))] + (let [margin (inst-ms (ct/diff timestamp created-at))] (if (or (neg? margin) (> margin 3600000)) ;; If event is in future or lags more than 1 hour, we reasign @@ -63,7 +63,7 @@ [{:keys [::db/pool]} {:keys [::rpc/profile-id events] :as params}] (let [request (-> params meta ::http/request) ip-addr (inet/parse-request request) - tnow (dt/now) + tnow (ct/now) xform (comp (map (fn [event] (-> event diff --git a/backend/src/app/rpc/commands/auth.clj b/backend/src/app/rpc/commands/auth.clj index 301582a2f9..20e1a9cdc9 100644 --- a/backend/src/app/rpc/commands/auth.clj +++ b/backend/src/app/rpc/commands/auth.clj @@ -12,6 +12,7 @@ [app.common.features :as cfeat] [app.common.logging :as l] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.uuid :as uuid] [app.config :as cf] [app.db :as db] @@ -30,7 +31,6 @@ [app.setup.welcome-file :refer [create-welcome-file]] [app.tokens :as tokens] [app.util.services :as sv] - [app.util.time :as dt] [app.worker :as wrk] [cuerdas.core :as str])) @@ -42,7 +42,7 @@ (defn- elapsed-verify-threshold? [profile] - (let [elapsed (dt/diff (:modified-at profile) (dt/now)) + (let [elapsed (ct/diff (:modified-at profile) (ct/now)) verify-threshold (cf/get :email-verify-threshold)] (pos? (compare elapsed verify-threshold)))) @@ -85,7 +85,7 @@ (ex/raise :type :validation :code :wrong-credentials)) (when-let [deleted-at (:deleted-at profile)] - (when (dt/is-after? (dt/now) deleted-at) + (when (ct/is-after? (ct/now) deleted-at) (ex/raise :type :validation :code :wrong-credentials))) @@ -244,7 +244,7 @@ :backend "penpot" :iss :prepared-register :profile-id (:id profile) - :exp (dt/in-future {:days 7}) + :exp (ct/in-future {:days 7}) :props {:newsletter-updates (or accept-newsletter-updates false)}} params (d/without-nils params) @@ -344,7 +344,7 @@ [{:keys [::db/conn] :as cfg} profile] (let [vtoken (tokens/generate (::setup/props cfg) {:iss :verify-email - :exp (dt/in-future "72h") + :exp (ct/in-future "72h") :profile-id (:id profile) :email (:email profile)}) ;; NOTE: this token is mainly used for possible complains @@ -352,7 +352,7 @@ ptoken (tokens/generate (::setup/props cfg) {:iss :profile-identity :profile-id (:id profile) - :exp (dt/in-future {:days 30})})] + :exp (ct/in-future {:days 30})})] (eml/send! {::eml/conn conn ::eml/factory eml/register :public-uri (cf/get :public-uri) @@ -466,7 +466,7 @@ (when (= action "resend-email-verification") (db/update! conn :profile - {:modified-at (dt/now)} + {:modified-at (ct/now)} {:id (:id profile)}) (send-email-verification! cfg profile)) @@ -495,7 +495,7 @@ (letfn [(create-recovery-token [{:keys [id] :as profile}] (let [token (tokens/generate (::setup/props cfg) {:iss :password-recovery - :exp (dt/in-future "15m") + :exp (ct/in-future "15m") :profile-id id})] (assoc profile :token token))) @@ -503,7 +503,7 @@ (let [ptoken (tokens/generate (::setup/props cfg) {:iss :profile-identity :profile-id (:id profile) - :exp (dt/in-future {:days 30})})] + :exp (ct/in-future {:days 30})})] (eml/send! {::eml/conn conn ::eml/factory eml/password-recovery :public-uri (cf/get :public-uri) @@ -544,7 +544,7 @@ :else (do (db/update! conn :profile - {:modified-at (dt/now)} + {:modified-at (ct/now)} {:id (:id profile)}) (->> profile (create-recovery-token) diff --git a/backend/src/app/rpc/commands/binfile.clj b/backend/src/app/rpc/commands/binfile.clj index bb077776eb..bb53109bac 100644 --- a/backend/src/app/rpc/commands/binfile.clj +++ b/backend/src/app/rpc/commands/binfile.clj @@ -13,6 +13,7 @@ [app.common.features :as cfeat] [app.common.logging :as l] [app.common.schema :as sm] + [app.common.time :as ct] [app.config :as cf] [app.db :as db] [app.http.sse :as sse] @@ -26,7 +27,6 @@ [app.rpc.doc :as-alias doc] [app.tasks.file-gc] [app.util.services :as sv] - [app.util.time :as dt] [app.worker :as-alias wrk] [promesa.exec :as px] [yetti.response :as yres])) @@ -114,7 +114,7 @@ 3 (px/invoke! executor (partial bf.v3/import-files! cfg)))] (db/update! pool :project - {:modified-at (dt/now)} + {:modified-at (ct/now)} {:id project-id} {::db/return-keys false}) diff --git a/backend/src/app/rpc/commands/comments.clj b/backend/src/app/rpc/commands/comments.clj index 54d3c47201..8f449d1bf8 100644 --- a/backend/src/app/rpc/commands/comments.clj +++ b/backend/src/app/rpc/commands/comments.clj @@ -11,6 +11,7 @@ [app.common.exceptions :as ex] [app.common.geom.point :as gpt] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.uri :as uri] [app.common.uuid :as uuid] [app.config :as cf] @@ -29,7 +30,6 @@ [app.rpc.retry :as rtry] [app.util.pointer-map :as pmap] [app.util.services :as sv] - [app.util.time :as dt] [clojure.set :as set] [cuerdas.core :as str])) @@ -222,7 +222,7 @@ (defn upsert-comment-thread-status! ([conn profile-id thread-id] - (upsert-comment-thread-status! conn profile-id thread-id (dt/in-future "1s"))) + (upsert-comment-thread-status! conn profile-id thread-id (ct/in-future "1s"))) ([conn profile-id thread-id mod-at] (db/exec-one! conn [sql:upsert-comment-thread-status thread-id profile-id mod-at mod-at]))) diff --git a/backend/src/app/rpc/commands/demo.clj b/backend/src/app/rpc/commands/demo.clj index ab9121dd03..4e64eac7ba 100644 --- a/backend/src/app/rpc/commands/demo.clj +++ b/backend/src/app/rpc/commands/demo.clj @@ -8,6 +8,7 @@ "A demo specific mutations." (:require [app.common.exceptions :as ex] + [app.common.time :as ct] [app.config :as cf] [app.db :as db] [app.loggers.audit :as audit] @@ -16,7 +17,6 @@ [app.rpc.commands.profile :as profile] [app.rpc.doc :as-alias doc] [app.util.services :as sv] - [app.util.time :as dt] [buddy.core.codecs :as bc] [buddy.core.nonce :as bn])) @@ -45,15 +45,13 @@ params {:email email :fullname fullname :is-active true - :deleted-at (dt/in-future (cf/get-deletion-delay)) + :deleted-at (ct/in-future (cf/get-deletion-delay)) :password (profile/derive-password cfg password) - :props {}}] - - - (let [profile (db/tx-run! cfg (fn [{:keys [::db/conn]}] - (->> (auth/create-profile! conn params) - (auth/create-profile-rels! conn))))] - (with-meta {:email email - :password password} - {::audit/profile-id (:id profile)})))) + :props {}} + profile (db/tx-run! cfg (fn [{:keys [::db/conn]}] + (->> (auth/create-profile! conn params) + (auth/create-profile-rels! conn))))] + (with-meta {:email email + :password password} + {::audit/profile-id (:id profile)}))) diff --git a/backend/src/app/rpc/commands/files.clj b/backend/src/app/rpc/commands/files.clj index 16a53e8b5b..5adef0050c 100644 --- a/backend/src/app/rpc/commands/files.clj +++ b/backend/src/app/rpc/commands/files.clj @@ -16,6 +16,7 @@ [app.common.logging :as l] [app.common.schema :as sm] [app.common.schema.desc-js-like :as-alias smdj] + [app.common.time :as ct] [app.common.types.components-list :as ctkl] [app.common.types.file :as ctf] [app.common.uri :as uri] @@ -37,7 +38,6 @@ [app.util.blob :as blob] [app.util.pointer-map :as pmap] [app.util.services :as sv] - [app.util.time :as dt] [app.worker :as wrk] [cuerdas.core :as str] [promesa.exec :as px])) @@ -52,7 +52,7 @@ ;; --- HELPERS (def long-cache-duration - (dt/duration {:days 7})) + (ct/duration {:days 7})) (defn decode-row [{:keys [data changes features] :as row}] @@ -187,10 +187,10 @@ [:name [:string {:max 250}]] [:revn [::sm/int {:min 0}]] [:vern [::sm/int {:min 0}]] - [:modified-at ::dt/instant] + [:modified-at ::ct/inst] [:is-shared ::sm/boolean] [:project-id ::sm/uuid] - [:created-at ::dt/instant] + [:created-at ::ct/inst] [:data {:optional true} ::sm/any]]) (def schema:permissions-mixin @@ -304,7 +304,7 @@ (defn get-file-etag [{:keys [::rpc/profile-id]} {:keys [modified-at revn vern permissions]}] (str profile-id "/" revn "/" vern "/" (hash fmg/available-migrations) "/" - (dt/format-instant modified-at :iso) + (ct/format-inst modified-at :iso) "/" (uri/map->query-string permissions))) @@ -356,7 +356,7 @@ [:map {:title "FileFragment"} [:id ::sm/uuid] [:file-id ::sm/uuid] - [:created-at ::dt/instant] + [:created-at ::ct/inst] [:content any?]]) (def schema:get-file-fragment @@ -770,7 +770,7 @@ [conn {:keys [id name]}] (db/update! conn :file {:name name - :modified-at (dt/now)} + :modified-at (ct/now)} {:id id} {::db/return-keys true})) @@ -783,8 +783,8 @@ [:id ::sm/uuid] [:project-id ::sm/uuid] [:name [:string {:max 250}]] - [:created-at ::dt/instant] - [:modified-at ::dt/instant]] + [:created-at ::ct/inst] + [:modified-at ::ct/inst]] ::sm/params [:map {:title "RenameFileParams"} @@ -795,8 +795,8 @@ [:map {:title "SimplifiedFile"} [:id ::sm/uuid] [:name [:string {:max 250}]] - [:created-at ::dt/instant] - [:modified-at ::dt/instant]] + [:created-at ::ct/inst] + [:modified-at ::ct/inst]] ::db/transaction true} [{:keys [::db/conn] :as cfg} {:keys [::rpc/profile-id id] :as params}] @@ -839,7 +839,7 @@ (db/update! cfg :file {:revn (inc (:revn file)) :data (blob/encode (:data file)) - :modified-at (dt/now) + :modified-at (ct/now) :has-media-trimmed false} {:id file-id}) @@ -900,7 +900,7 @@ (db/delete! conn :file-library-rel {:library-file-id id}) (db/update! conn :file {:is-shared false - :modified-at (dt/now)} + :modified-at (ct/now)} {:id id}) (select-keys file [:id :name :is-shared])) @@ -909,7 +909,7 @@ (let [file (assoc file :is-shared true)] (db/update! conn :file {:is-shared true - :modified-at (dt/now)} + :modified-at (ct/now)} {:id id}) file) @@ -945,7 +945,7 @@ [conn team file-id] (let [delay (ldel/get-deletion-delay team) file (db/update! conn :file - {:deleted-at (dt/in-future delay)} + {:deleted-at (ct/in-future delay)} {:id file-id} {::db/return-keys [:id :name :is-shared :deleted-at :project-id :created-at :modified-at]})] @@ -1043,7 +1043,7 @@ (defn update-sync [conn {:keys [file-id library-id] :as params}] (db/update! conn :file-library-rel - {:synced-at (dt/now)} + {:synced-at (ct/now)} {:file-id file-id :library-file-id library-id} {::db/return-keys true})) @@ -1068,14 +1068,14 @@ [conn {:keys [file-id date] :as params}] (db/update! conn :file {:ignore-sync-until date - :modified-at (dt/now)} + :modified-at (ct/now)} {:id file-id} {::db/return-keys true})) (def ^:private schema:ignore-file-library-sync-status [:map {:title "ignore-file-library-sync-status"} [:file-id ::sm/uuid] - [:date ::dt/instant]]) + [:date ::ct/inst]]) ;; TODO: improve naming (sv/defmethod ::ignore-file-library-sync-status diff --git a/backend/src/app/rpc/commands/files_create.clj b/backend/src/app/rpc/commands/files_create.clj index 64ef7338e1..bcae300d30 100644 --- a/backend/src/app/rpc/commands/files_create.clj +++ b/backend/src/app/rpc/commands/files_create.clj @@ -9,6 +9,7 @@ [app.binfile.common :as bfc] [app.common.features :as cfeat] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.types.file :as ctf] [app.config :as cf] [app.db :as db] @@ -22,7 +23,6 @@ [app.rpc.quotes :as quotes] [app.util.pointer-map :as pmap] [app.util.services :as sv] - [app.util.time :as dt] [clojure.set :as set])) (defn create-file-role! @@ -63,7 +63,7 @@ (create-file-role! conn)) (db/update! conn :project - {:modified-at (dt/now)} + {:modified-at (ct/now)} {:id project-id}) file))) diff --git a/backend/src/app/rpc/commands/files_snapshot.clj b/backend/src/app/rpc/commands/files_snapshot.clj index bcfbad9428..e45769b533 100644 --- a/backend/src/app/rpc/commands/files_snapshot.clj +++ b/backend/src/app/rpc/commands/files_snapshot.clj @@ -11,6 +11,7 @@ [app.common.files.migrations :as fmg] [app.common.logging :as l] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.uuid :as uuid] [app.config :as cf] [app.db :as db] @@ -26,7 +27,6 @@ [app.storage :as sto] [app.util.blob :as blob] [app.util.services :as sv] - [app.util.time :as dt] [cuerdas.core :as str])) (defn decode-row @@ -69,8 +69,8 @@ (defn- generate-snapshot-label [] - (let [ts (-> (dt/now) - (dt/format-instant) + (let [ts (-> (ct/now) + (ct/format-inst) (str/replace #"[T:\.]" "-") (str/rtrim "Z"))] (str "snapshot-" ts))) @@ -89,9 +89,9 @@ deleted-at (cond (= deleted-at :default) - (dt/plus (dt/now) (cf/get-deletion-delay)) + (ct/plus (ct/now) (cf/get-deletion-delay)) - (dt/instant? deleted-at) + (ct/inst? deleted-at) deleted-at :else @@ -304,7 +304,7 @@ (defn- delete-file-snapshot! [conn snapshot-id] (db/update! conn :file-change - {:deleted-at (dt/now)} + {:deleted-at (ct/now)} {:id snapshot-id} {::db/return-keys false}) nil) diff --git a/backend/src/app/rpc/commands/files_temp.clj b/backend/src/app/rpc/commands/files_temp.clj index f1e603f566..ca3b107a0b 100644 --- a/backend/src/app/rpc/commands/files_temp.clj +++ b/backend/src/app/rpc/commands/files_temp.clj @@ -10,6 +10,7 @@ [app.common.features :as cfeat] [app.common.files.changes :as cpc] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.uuid :as uuid] [app.config :as cf] [app.db :as db] @@ -27,7 +28,6 @@ [app.util.blob :as blob] [app.util.pointer-map :as pmap] [app.util.services :as sv] - [app.util.time :as dt] [clojure.set :as set])) ;; --- MUTATION COMMAND: create-temp-file @@ -72,7 +72,7 @@ params (-> params (assoc :profile-id profile-id) - (assoc :deleted-at (dt/in-future {:days 1})) + (assoc :deleted-at (ct/in-future {:days 1})) (assoc :features features))] (files.create/create-file cfg params))) @@ -97,7 +97,7 @@ {:id (uuid/next) :session-id session-id :profile-id profile-id - :created-at (dt/now) + :created-at (ct/now) :file-id id :revn revn :data nil diff --git a/backend/src/app/rpc/commands/files_thumbnails.clj b/backend/src/app/rpc/commands/files_thumbnails.clj index 9d64ec504d..5b9ae88573 100644 --- a/backend/src/app/rpc/commands/files_thumbnails.clj +++ b/backend/src/app/rpc/commands/files_thumbnails.clj @@ -13,6 +13,7 @@ [app.common.geom.shapes :as gsh] [app.common.schema :as sm] [app.common.thumbnails :as thc] + [app.common.time :as ct] [app.common.types.shape-tree :as ctt] [app.config :as cf] [app.db :as db] @@ -30,13 +31,12 @@ [app.storage :as sto] [app.util.pointer-map :as pmap] [app.util.services :as sv] - [app.util.time :as dt] [cuerdas.core :as str])) ;; --- FEATURES (def long-cache-duration - (dt/duration {:days 7})) + (ct/duration {:days 7})) ;; --- COMMAND QUERY: get-file-object-thumbnails @@ -247,7 +247,7 @@ (defn- create-file-object-thumbnail! [{:keys [::sto/storage] :as cfg} file object-id media tag] (let [file-id (:id file) - timestamp (dt/now) + timestamp (ct/now) media (persist-thumbnail! storage media timestamp) [th1 th2] (db/tx-run! cfg (fn [{:keys [::db/conn]}] (let [th1 (db/exec-one! conn [sql:get-file-object-thumbnail file-id object-id tag]) @@ -302,7 +302,7 @@ {::sql/for-update true})] (sto/touch-object! storage media-id) (db/update! conn :file-tagged-object-thumbnail - {:deleted-at (dt/now)} + {:deleted-at (ct/now)} {:file-id file-id :object-id object-id :tag tag}))) @@ -338,7 +338,7 @@ hash (sto/calculate-hash path) data (-> (sto/content path) (sto/wrap-with-hash hash)) - tnow (dt/now) + tnow (ct/now) media (sto/put-object! storage {::sto/content data ::sto/deduplicate? true diff --git a/backend/src/app/rpc/commands/files_update.clj b/backend/src/app/rpc/commands/files_update.clj index a57d5bf001..9dcbf8ec94 100644 --- a/backend/src/app/rpc/commands/files_update.clj +++ b/backend/src/app/rpc/commands/files_update.clj @@ -15,6 +15,7 @@ [app.common.files.validate :as val] [app.common.logging :as l] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.uuid :as uuid] [app.config :as cf] [app.db :as db] @@ -36,7 +37,6 @@ [app.util.blob :as blob] [app.util.pointer-map :as pmap] [app.util.services :as sv] - [app.util.time :as dt] [app.worker :as wrk] [clojure.set :as set] [promesa.exec :as px])) @@ -123,7 +123,7 @@ [:update-file/global]] ::webhooks/event? true - ::webhooks/batch-timeout (dt/duration "2m") + ::webhooks/batch-timeout (ct/duration "2m") ::webhooks/batch-key (webhooks/key-fn ::rpc/profile-id :id) ::sm/params schema:update-file @@ -156,9 +156,9 @@ (assoc :file file) (assoc :changes changes)) - cfg (assoc cfg ::timestamp (dt/now)) + cfg (assoc cfg ::timestamp (ct/now)) - tpoint (dt/tpoint)] + tpoint (ct/tpoint)] (when (not= (:vern params) @@ -199,7 +199,7 @@ (errors/request->context))] (-> (update-file* cfg params) (rph/with-defer #(let [elapsed (tpoint)] - (l/trace :hint "update-file" :time (dt/format-duration elapsed)))))))))) + (l/trace :hint "update-file" :time (ct/format-duration elapsed)))))))))) (defn- update-file* "Internal function, part of the update-file process, that encapsulates @@ -244,8 +244,8 @@ :created-at timestamp :updated-at timestamp :deleted-at (if (::snapshot-data file) - (dt/plus timestamp (ldel/get-deletion-delay team)) - (dt/plus timestamp (dt/duration {:hours 1}))) + (ct/plus timestamp (ldel/get-deletion-delay team)) + (ct/plus timestamp (ct/duration {:hours 1}))) :file-id (:id file) :revn (:revn file) :version (:version file) @@ -306,7 +306,7 @@ [{:keys [::db/conn ::timestamp]} file] (let [;; The timestamp can be nil because this function is also ;; intended to be used outside of this module - modified-at (or timestamp (dt/now))] + modified-at (or timestamp (ct/now))] (db/update! conn :project {:modified-at modified-at} @@ -360,7 +360,7 @@ ;; TODO: reuse operations if file is migrated ;; TODO: move encoding to a separated thread file (if (take-snapshot? file) - (let [tpoint (dt/tpoint) + (let [tpoint (ct/tpoint) snapshot (-> (:data file) (feat.fdata/process-pointers deref) (feat.fdata/process-objects (partial into {})) @@ -372,7 +372,7 @@ :file-id (str (:id file)) :revn (:revn file) :label label - :elapsed (dt/format-duration elapsed)) + :elapsed (ct/format-duration elapsed)) (-> file (assoc ::snapshot-data snapshot) @@ -452,11 +452,11 @@ (when (contains? cf/flags :auto-file-snapshot) (let [freq (or (cf/get :auto-file-snapshot-every) 20) timeout (or (cf/get :auto-file-snapshot-timeout) - (dt/duration {:hours 1}))] + (ct/duration {:hours 1}))] (or (= 1 freq) (zero? (mod revn freq)) - (> (inst-ms (dt/diff modified-at (dt/now))) + (> (inst-ms (ct/diff modified-at (ct/now))) (inst-ms timeout)))))) (def ^:private sql:lagged-changes @@ -496,5 +496,5 @@ :file-id (:id file) :session-id session-id :revn (:revn file) - :modified-at (dt/now) + :modified-at (ct/now) :changes lchanges})))) diff --git a/backend/src/app/rpc/commands/fonts.clj b/backend/src/app/rpc/commands/fonts.clj index 68c8620d47..e5a0b71c1b 100644 --- a/backend/src/app/rpc/commands/fonts.clj +++ b/backend/src/app/rpc/commands/fonts.clj @@ -9,6 +9,7 @@ [app.common.data.macros :as dm] [app.common.exceptions :as ex] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.uuid :as uuid] [app.db :as db] [app.db.sql :as-alias sql] @@ -26,7 +27,6 @@ [app.rpc.quotes :as quotes] [app.storage :as sto] [app.util.services :as sv] - [app.util.time :as dt] [app.worker :as-alias wrk] [promesa.exec :as px])) @@ -124,7 +124,7 @@ content (-> (sto/content resource) (sto/wrap-with-hash hash))] {::sto/content content - ::sto/touched-at (dt/now) + ::sto/touched-at (ct/now) ::sto/deduplicate? true :content-type mtype :bucket "team-font-variant"}))) @@ -217,7 +217,7 @@ {::sql/for-update true}) delay (ldel/get-deletion-delay team) - tnow (dt/in-future delay)] + tnow (ct/in-future delay)] (teams/check-edition-permissions! (:permissions team)) @@ -261,7 +261,7 @@ (teams/check-edition-permissions! (:permissions team)) (db/update! conn :team-font-variant - {:deleted-at (dt/in-future delay)} + {:deleted-at (ct/in-future delay)} {:id (:id variant)} {::db/return-keys false}) diff --git a/backend/src/app/rpc/commands/management.clj b/backend/src/app/rpc/commands/management.clj index cee172df33..ca46a8f824 100644 --- a/backend/src/app/rpc/commands/management.clj +++ b/backend/src/app/rpc/commands/management.clj @@ -13,6 +13,7 @@ [app.common.exceptions :as ex] [app.common.features :as cfeat] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.uuid :as uuid] [app.config :as cf] [app.db :as db] @@ -28,7 +29,6 @@ [app.setup.templates :as tmpl] [app.storage.tmp :as tmp] [app.util.services :as sv] - [app.util.time :as dt] [app.worker :as-alias wrk] [promesa.exec :as px])) @@ -104,7 +104,7 @@ (db/exec-one! conn ["SET CONSTRAINTS ALL DEFERRED"]) (binding [bfc/*state* (volatile! {:index {file-id (uuid/next)}})] - (duplicate-file (assoc cfg ::bfc/timestamp (dt/now)) + (duplicate-file (assoc cfg ::bfc/timestamp (ct/now)) (-> params (assoc :profile-id profile-id) (assoc :reset-shared-flag true))))))) @@ -164,7 +164,7 @@ (db/tx-run! cfg (fn [cfg] ;; Defer all constraints (db/exec-one! cfg ["SET CONSTRAINTS ALL DEFERRED"]) - (-> (assoc cfg ::bfc/timestamp (dt/now)) + (-> (assoc cfg ::bfc/timestamp (ct/now)) (duplicate-project (assoc params :profile-id profile-id)))))) (defn duplicate-team @@ -320,7 +320,7 @@ ;; trully different modification date to each file. (px/sleep 10) (db/update! conn :project - {:modified-at (dt/now)} + {:modified-at (ct/now)} {:id project-id})) nil)) @@ -425,7 +425,7 @@ (db/tx-run! cfg (fn [{:keys [::db/conn] :as cfg}] (db/update! conn :project - {:modified-at (dt/now)} + {:modified-at (ct/now)} {:id project-id} {::db/return-keys false}) diff --git a/backend/src/app/rpc/commands/media.clj b/backend/src/app/rpc/commands/media.clj index 5fc05c2f00..042053a2b1 100644 --- a/backend/src/app/rpc/commands/media.clj +++ b/backend/src/app/rpc/commands/media.clj @@ -10,6 +10,7 @@ [app.common.exceptions :as ex] [app.common.media :as cm] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.uuid :as uuid] [app.config :as cf] [app.db :as db] @@ -23,7 +24,6 @@ [app.storage :as sto] [app.storage.tmp :as tmp] [app.util.services :as sv] - [app.util.time :as dt] [app.worker :as-alias wrk] [cuerdas.core :as str] [datoteka.io :as io] @@ -67,7 +67,7 @@ mobj (create-file-media-object cfg params)] (db/update! conn :file - {:modified-at (dt/now) + {:modified-at (ct/now) :has-media-trimmed false} {:id file-id} {::db/return-keys false}) @@ -192,7 +192,7 @@ mobj (create-file-media-object-from-url cfg (assoc params :profile-id profile-id))] (db/update! pool :file - {:modified-at (dt/now) + {:modified-at (ct/now) :has-media-trimmed false} {:id file-id} {::db/return-keys false}) diff --git a/backend/src/app/rpc/commands/profile.clj b/backend/src/app/rpc/commands/profile.clj index 6449bef09c..9425e009f1 100644 --- a/backend/src/app/rpc/commands/profile.clj +++ b/backend/src/app/rpc/commands/profile.clj @@ -10,6 +10,7 @@ [app.common.data :as d] [app.common.exceptions :as ex] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.types.plugins :refer [schema:plugin-registry]] [app.common.uuid :as uuid] [app.config :as cf] @@ -28,7 +29,6 @@ [app.storage :as sto] [app.tokens :as tokens] [app.util.services :as sv] - [app.util.time :as dt] [app.worker :as wrk] [cuerdas.core :as str] [promesa.exec :as px])) @@ -70,8 +70,8 @@ [:is-blocked {:optional true} ::sm/boolean] [:is-demo {:optional true} ::sm/boolean] [:is-muted {:optional true} ::sm/boolean] - [:created-at {:optional true} ::sm/inst] - [:modified-at {:optional true} ::sm/inst] + [:created-at {:optional true} ::ct/inst] + [:modified-at {:optional true} ::ct/inst] [:default-project-id {:optional true} ::sm/uuid] [:default-team-id {:optional true} ::sm/uuid] [:props {:optional true} schema:props]]) @@ -352,13 +352,13 @@ [{:keys [::db/conn] :as cfg} {:keys [profile email] :as params}] (let [token (tokens/generate (::setup/props cfg) {:iss :change-email - :exp (dt/in-future "15m") + :exp (ct/in-future "15m") :profile-id (:id profile) :email email}) ptoken (tokens/generate (::setup/props cfg) {:iss :profile-identity :profile-id (:id profile) - :exp (dt/in-future {:days 30})})] + :exp (ct/in-future {:days 30})})] (when (not= email (:email profile)) (check-profile-existence! conn params)) @@ -444,7 +444,7 @@ ::db/transaction true} [{:keys [::db/conn] :as cfg} {:keys [::rpc/profile-id] :as params}] (let [teams (get-owned-teams conn profile-id) - deleted-at (dt/now)] + deleted-at (ct/now)] ;; If we found owned teams with participants, we don't allow ;; delete profile until the user properly transfer ownership or diff --git a/backend/src/app/rpc/commands/projects.clj b/backend/src/app/rpc/commands/projects.clj index 23cdff51a5..3684824bc5 100644 --- a/backend/src/app/rpc/commands/projects.clj +++ b/backend/src/app/rpc/commands/projects.clj @@ -9,6 +9,7 @@ [app.common.data.macros :as dm] [app.common.exceptions :as ex] [app.common.schema :as sm] + [app.common.time :as ct] [app.db :as db] [app.db.sql :as-alias sql] [app.features.logical-deletion :as ldel] @@ -21,7 +22,6 @@ [app.rpc.permissions :as perms] [app.rpc.quotes :as quotes] [app.util.services :as sv] - [app.util.time :as dt] [app.worker :as wrk])) ;; --- Check Project Permissions @@ -218,7 +218,7 @@ (sv/defmethod ::update-project-pin {::doc/added "1.18" ::sm/params schema:update-project-pin - ::webhooks/batch-timeout (dt/duration "5s") + ::webhooks/batch-timeout (ct/duration "5s") ::webhooks/batch-key (webhooks/key-fn ::rpc/profile-id :id) ::webhooks/event? true ::db/transaction true} @@ -257,7 +257,7 @@ [conn team project-id] (let [delay (ldel/get-deletion-delay team) project (db/update! conn :project - {:deleted-at (dt/in-future delay)} + {:deleted-at (ct/in-future delay)} {:id project-id} {::db/return-keys true})] diff --git a/backend/src/app/rpc/commands/teams.clj b/backend/src/app/rpc/commands/teams.clj index a099223b83..4f2e236dd7 100644 --- a/backend/src/app/rpc/commands/teams.clj +++ b/backend/src/app/rpc/commands/teams.clj @@ -11,6 +11,7 @@ [app.common.exceptions :as ex] [app.common.features :as cfeat] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.types.team :as tt] [app.common.uuid :as uuid] [app.config :as cf] @@ -30,7 +31,6 @@ [app.setup :as-alias setup] [app.storage :as sto] [app.util.services :as sv] - [app.util.time :as dt] [app.worker :as wrk] [clojure.set :as set])) @@ -666,7 +666,7 @@ (let [delay (ldel/get-deletion-delay team) team (db/update! conn :team - {:deleted-at (dt/in-future delay)} + {:deleted-at (ct/in-future delay)} {:id id} {::db/return-keys true})] diff --git a/backend/src/app/rpc/commands/teams_invitations.clj b/backend/src/app/rpc/commands/teams_invitations.clj index 0fd0d91279..6d92b25d87 100644 --- a/backend/src/app/rpc/commands/teams_invitations.clj +++ b/backend/src/app/rpc/commands/teams_invitations.clj @@ -12,6 +12,7 @@ [app.common.features :as cfeat] [app.common.logging :as l] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.types.team :as types.team] [app.common.uuid :as uuid] [app.config :as cf] @@ -29,7 +30,6 @@ [app.setup :as-alias setup] [app.tokens :as tokens] [app.util.services :as sv] - [app.util.time :as dt] [cuerdas.core :as str])) ;; --- Mutation: Create Team Invitation @@ -62,7 +62,7 @@ (tokens/generate (::setup/props cfg) {:iss :profile-identity :profile-id profile-id - :exp (dt/in-future {:days 30})})) + :exp (ct/in-future {:days 30})})) (def ^:private schema:create-invitation [:map {:title "params:create-invitation"} @@ -126,7 +126,7 @@ (teams/check-email-spam conn email true) (let [id (uuid/next) - expire (dt/in-future "168h") ;; 7 days + expire (ct/in-future "168h") ;; 7 days invitation (db/exec-one! conn [sql:upsert-team-invitation id (:id team) (str/lower email) (:id profile) @@ -418,7 +418,7 @@ :code :insufficient-permissions)) (db/update! conn :team-invitation - {:role (name role) :updated-at (dt/now)} + {:role (name role) :updated-at (ct/now)} {:team-id team-id :email-to (profile/clean-email email)}) nil)) @@ -471,7 +471,7 @@ (when-let [request (db/get* conn :team-access-request {:team-id team-id :requester-id profile-id})] - (when (dt/is-after? (:valid-until request) (dt/now)) + (when (ct/is-after? (:valid-until request) (ct/now)) (ex/raise :type :validation :code :request-already-sent :hint "you have already made a request to join this team less than 24 hours ago")))) @@ -487,8 +487,8 @@ "Create or update team access request for provided team and profile-id" [conn team-id requester-id] (check-existing-team-access-request conn team-id requester-id) - (let [valid-until (dt/in-future {:hours 24}) - auto-join-until (dt/in-future {:days 7}) + (let [valid-until (ct/in-future {:hours 24}) + auto-join-until (ct/in-future {:days 7}) request-id (uuid/next)] (db/exec-one! conn [sql:upsert-team-access-request request-id team-id requester-id diff --git a/backend/src/app/rpc/commands/verify_token.clj b/backend/src/app/rpc/commands/verify_token.clj index 27679536ef..afab9a10c4 100644 --- a/backend/src/app/rpc/commands/verify_token.clj +++ b/backend/src/app/rpc/commands/verify_token.clj @@ -8,6 +8,7 @@ (:require [app.common.exceptions :as ex] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.types.team :as types.team] [app.config :as cf] [app.db :as db] @@ -23,8 +24,7 @@ [app.setup :as-alias setup] [app.tokens :as tokens] [app.tokens.spec.team-invitation :as-alias spec.team-invitation] - [app.util.services :as sv] - [app.util.time :as dt])) + [app.util.services :as sv])) (defmulti process-token (fn [_ _ claims] (:iss claims))) @@ -126,7 +126,7 @@ (def schema:team-invitation-claims [:map {:title "TeamInvitationClaims"} [:iss :keyword] - [:exp ::dt/instant] + [:exp ::ct/inst] [:profile-id ::sm/uuid] [:role ::types.team/role] [:team-id ::sm/uuid] diff --git a/backend/src/app/rpc/commands/webhooks.clj b/backend/src/app/rpc/commands/webhooks.clj index 07ea614b4e..5b64541824 100644 --- a/backend/src/app/rpc/commands/webhooks.clj +++ b/backend/src/app/rpc/commands/webhooks.clj @@ -9,6 +9,7 @@ [app.common.data.macros :as dm] [app.common.exceptions :as ex] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.uri :as u] [app.common.uuid :as uuid] [app.db :as db] @@ -19,7 +20,6 @@ [app.rpc.doc :as-alias doc] [app.rpc.permissions :as perms] [app.util.services :as sv] - [app.util.time :as dt] [cuerdas.core :as str])) (defn get-webhooks-permissions @@ -54,7 +54,7 @@ (http/req! cfg {:method :head :uri (str (:uri params)) - :timeout (dt/duration "3s")} + :timeout (ct/duration "3s")} {:sync? true}))] (if (ex/exception? response) (if-let [hint (webhooks/interpret-exception response)] diff --git a/backend/src/app/rpc/quotes.clj b/backend/src/app/rpc/quotes.clj index e939dbe2ea..fefcf5952d 100644 --- a/backend/src/app/rpc/quotes.clj +++ b/backend/src/app/rpc/quotes.clj @@ -10,9 +10,9 @@ [app.common.exceptions :as ex] [app.common.logging :as l] [app.common.schema :as sm] + [app.common.time :as ct] [app.config :as cf] [app.db :as db] - [app.util.time :as dt] [app.worker :as wrk] [cuerdas.core :as str])) @@ -95,7 +95,7 @@ "- Total: ~(::total params) (INCR ~(::incr params 1))\n")] (wrk/submit! {::db/conn conn ::wrk/task :sendmail - ::wrk/delay (dt/duration "30s") + ::wrk/delay (ct/duration "30s") ::wrk/max-retries 4 ::wrk/priority 200 ::wrk/dedupe true diff --git a/backend/src/app/rpc/rlimit.clj b/backend/src/app/rpc/rlimit.clj index 67b8b2ef8d..bb2ccededa 100644 --- a/backend/src/app/rpc/rlimit.clj +++ b/backend/src/app/rpc/rlimit.clj @@ -47,6 +47,7 @@ [app.common.exceptions :as ex] [app.common.logging :as l] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.uri :as uri] [app.common.uuid :as uuid] [app.config :as cf] @@ -58,7 +59,6 @@ [app.rpc.rlimit.result :as-alias lresult] [app.util.inet :as inet] [app.util.services :as-alias sv] - [app.util.time :as dt] [app.worker :as wrk] [clojure.edn :as edn] [cuerdas.core :as str] @@ -67,7 +67,7 @@ [promesa.exec :as px])) (def ^:private default-timeout - (dt/duration 400)) + (ct/duration 400)) (def ^:private default-options {:codec rds/string-codec @@ -94,6 +94,10 @@ (defmulti parse-limit (fn [[_ strategy _]] strategy)) (defmulti process-limit (fn [_ _ _ o] (::strategy o))) +(defn- ->seconds + [d] + (-> d inst-ms (/ 1000) int)) + (sm/register! {:type ::rpc/rlimit :pred #(instance? clojure.lang.Agent %)}) @@ -115,7 +119,7 @@ [:map [::capacity ::sm/int] [::rate ::sm/int] - [::internal ::dt/duration] + [::internal ::ct/duration] [::params [::sm/vec :any]]] [:map [::nreq ::sm/int] @@ -157,7 +161,7 @@ (assert (valid-limit-tuple? vlimit) "expected valid limit tuple") (if-let [[_ capacity rate interval] (re-find bucket-opts-re opts)] - (let [interval (dt/duration interval) + (let [interval (ct/duration interval) rate (parse-long rate) capacity (parse-long capacity)] {::name name @@ -166,7 +170,7 @@ ::rate rate ::interval interval ::opts opts - ::params [(dt/->seconds interval) rate capacity] + ::params [(->seconds interval) rate capacity] ::key (str "ratelimit.bucket." (d/name name))}) (ex/raise :type :validation :code :invalid-bucket-limit-opts @@ -176,7 +180,7 @@ [redis user-id now {:keys [::key ::params ::service ::capacity ::interval ::rate] :as limit}] (let [script (-> bucket-rate-limit-script (assoc ::rscript/keys [(str key "." service "." user-id)]) - (assoc ::rscript/vals (conj params (dt/->seconds now)))) + (assoc ::rscript/vals (conj params (->seconds now)))) result (rds/eval redis script) allowed? (boolean (nth result 0)) remaining (nth result 1) @@ -191,16 +195,16 @@ :remaining remaining) (-> limit (assoc ::lresult/allowed allowed?) - (assoc ::lresult/reset (dt/plus now reset)) + (assoc ::lresult/reset (ct/plus now reset)) (assoc ::lresult/remaining remaining)))) (defmethod process-limit :window [redis user-id now {:keys [::nreq ::unit ::key ::service] :as limit}] - (let [ts (dt/truncate now unit) - ttl (dt/diff now (dt/plus ts {unit 1})) + (let [ts (ct/truncate now unit) + ttl (ct/diff now (ct/plus ts {unit 1})) script (-> window-rate-limit-script - (assoc ::rscript/keys [(str key "." service "." user-id "." (dt/format-instant ts))]) - (assoc ::rscript/vals [nreq (dt/->seconds ttl)])) + (assoc ::rscript/keys [(str key "." service "." user-id "." (ct/format-inst ts))]) + (assoc ::rscript/vals [nreq (->seconds ttl)])) result (rds/eval redis script) allowed? (boolean (nth result 0)) remaining (nth result 1)] @@ -214,7 +218,7 @@ (-> limit (assoc ::lresult/allowed allowed?) (assoc ::lresult/remaining remaining) - (assoc ::lresult/reset (dt/plus ts {unit 1}))))) + (assoc ::lresult/reset (ct/plus ts {unit 1}))))) (defn- process-limits! [redis user-id limits now] @@ -223,7 +227,7 @@ (d/index-by ::name ::lresult/remaining) (uri/map->query-string)) reset (->> results - (d/index-by ::name (comp dt/->seconds ::lresult/reset)) + (d/index-by ::name (comp ->seconds ::lresult/reset)) (uri/map->query-string)) rejected (d/seek (complement ::lresult/allowed) results)] @@ -261,7 +265,7 @@ (let [redis (rds/get-or-connect redis ::rpc/rlimit default-options) uid (get-uid params) ;; FIXME: why not clasic try/catch? - result (ex/try! (process-limits! redis uid limits (dt/now)))] + result (ex/try! (process-limits! redis uid limits (ct/now)))] (l/trc :hint "process-limits" :service sname @@ -321,7 +325,7 @@ (sm/check-fn schema:config)) (def ^:private check-refresh - (sm/check-fn ::dt/duration)) + (sm/check-fn ::ct/duration)) (def ^:private check-limits (sm/check-fn schema:limits)) @@ -351,7 +355,7 @@ config)))] (when-let [config (some->> path slurp edn/read-string check-config)] - (let [refresh (->> config meta :refresh dt/duration check-refresh) + (let [refresh (->> config meta :refresh ct/duration check-refresh) limits (->> config compile-pass-1 compile-pass-2 check-limits)] {::refresh refresh @@ -410,7 +414,7 @@ (l/info :hint "initializing rlimit config reader" :path (str path)) ;; Initialize the state with initial refresh value - (send-via executor state (constantly {::refresh (dt/duration "5s")})) + (send-via executor state (constantly {::refresh (ct/duration "5s")})) ;; Force a refresh (refresh-config (assoc cfg ::path path ::state state))) diff --git a/backend/src/app/srepl.clj b/backend/src/app/srepl.clj index db28fd0386..e4442cf6de 100644 --- a/backend/src/app/srepl.clj +++ b/backend/src/app/srepl.clj @@ -11,11 +11,11 @@ [app.common.exceptions :as ex] [app.common.json :as json] [app.common.logging :as l] + [app.common.time :as ct] [app.config :as cf] [app.srepl.cli :as cli] [app.srepl.main] [app.util.locks :as locks] - [app.util.time :as dt] [clojure.core :as c] [clojure.core.server :as ccs] [clojure.main :as cm] @@ -77,7 +77,7 @@ (loop [] (when (try (let [data (read-line) - tpoint (dt/tpoint)] + tpoint (ct/tpoint)] (l/dbg :hint "received" :data (if (= data ::eof) "EOF" data)) diff --git a/backend/src/app/srepl/cli.clj b/backend/src/app/srepl/cli.clj index e9c441d240..75c0cb2a0a 100644 --- a/backend/src/app/srepl/cli.clj +++ b/backend/src/app/srepl/cli.clj @@ -10,13 +10,14 @@ [app.auth :as auth] [app.common.exceptions :as ex] [app.common.schema :as sm] + [app.common.schema.generators :as sg] + [app.common.time :as ct] [app.common.uuid :as uuid] [app.db :as db] [app.rpc.commands.auth :as cmd.auth] [app.rpc.commands.profile :as cmd.profile] [app.setup :as-alias setup] [app.tokens :as tokens] - [app.util.time :as dt] [cuerdas.core :as str])) (defn coercer @@ -101,7 +102,7 @@ (fn [{:keys [::db/conn] :as system}] (let [res (if soft (db/update! conn :profile - {:deleted-at (dt/now)} + {:deleted-at (ct/now)} {:email email :deleted-at nil}) (db/delete! conn :profile {:email email}))] @@ -173,6 +174,21 @@ :num-editors (get-customer-slots system id) :subscription (get props :subscription)}))) +(def ^:private schema:timestamp + (sm/type-schema + {:type ::timestamp + :pred ct/inst? + :type-properties + {:title "inst" + :description "The same as :app.common.time/inst but encodes to epoch" + :error/message "should be an instant" + :gen/gen (->> (sg/small-int) + (sg/fmap (fn [v] (ct/inst v)))) + :decode/string ct/inst + :encode/string inst-ms + :decode/json ct/inst + :encode/json inst-ms}})) + (def ^:private schema:customer-subscription [:map {:title "CustomerSubscription"} [:id ::sm/text] @@ -198,15 +214,15 @@ "year"]] [:quantity :int] [:description [:maybe ::sm/text]] - [:created-at ::sm/timestamp] - [:start-date [:maybe ::sm/timestamp]] - [:ended-at [:maybe ::sm/timestamp]] - [:trial-end [:maybe ::sm/timestamp]] - [:trial-start [:maybe ::sm/timestamp]] - [:cancel-at [:maybe ::sm/timestamp]] - [:canceled-at [:maybe ::sm/timestamp]] - [:current-period-end [:maybe ::sm/timestamp]] - [:current-period-start [:maybe ::sm/timestamp]] + [:created-at schema:timestamp] + [:start-date [:maybe schema:timestamp]] + [:ended-at [:maybe schema:timestamp]] + [:trial-end [:maybe schema:timestamp]] + [:trial-start [:maybe schema:timestamp]] + [:cancel-at [:maybe schema:timestamp]] + [:canceled-at [:maybe schema:timestamp]] + [:current-period-end [:maybe schema:timestamp]] + [:current-period-start [:maybe schema:timestamp]] [:cancel-at-period-end :boolean] [:cancellation-details diff --git a/backend/src/app/srepl/helpers.clj b/backend/src/app/srepl/helpers.clj index 6a67cd2375..cb1d54e2fa 100644 --- a/backend/src/app/srepl/helpers.clj +++ b/backend/src/app/srepl/helpers.clj @@ -12,11 +12,11 @@ [app.common.data :as d] [app.common.files.migrations :as fmg] [app.common.files.validate :as cfv] + [app.common.time :as ct] [app.db :as db] [app.main :as main] [app.rpc.commands.files :as files] - [app.rpc.commands.files-snapshot :as fsnap] - [app.util.time :as dt])) + [app.rpc.commands.files-snapshot :as fsnap])) (def ^:dynamic *system* nil) @@ -165,7 +165,7 @@ (when (string? label) (fsnap/create-file-snapshot! system file {:label label - :deleted-at (dt/in-future {:days 30}) + :deleted-at (ct/in-future {:days 30}) :created-by :admin})) (let [file' (update file' :revn inc)] diff --git a/backend/src/app/srepl/main.clj b/backend/src/app/srepl/main.clj index 2566fd5f0c..2ffe1caa41 100644 --- a/backend/src/app/srepl/main.clj +++ b/backend/src/app/srepl/main.clj @@ -19,6 +19,7 @@ [app.common.pprint :as p] [app.common.schema :as sm] [app.common.spec :as us] + [app.common.time :as ct] [app.common.uuid :as uuid] [app.config :as cf] [app.db :as db] @@ -38,7 +39,6 @@ [app.srepl.helpers :as h] [app.util.blob :as blob] [app.util.pointer-map :as pmap] - [app.util.time :as dt] [app.worker :as wrk] [clojure.java.io :as io] [clojure.pprint :refer [print-table]] @@ -476,7 +476,7 @@ :max-jobs max-jobs :max-items max-items) - (let [tpoint (dt/tpoint) + (let [tpoint (ct/tpoint) factory (px/thread-factory :virtual false :prefix "penpot/file-process/") executor (px/cached-executor :factory factory) sjobs (ps/create :permits max-jobs) @@ -506,7 +506,7 @@ (Thread/sleep (int pause))) (ps/release! sjobs) - (let [elapsed (dt/format-duration (tpoint))] + (let [elapsed (ct/format-duration (tpoint))] (l/trc :hint "process:file:end" :tid thread-id :file-id (str file-id) @@ -516,7 +516,7 @@ process-file* (fn [idx file-id] (ps/acquire! sjobs) - (px/run! executor (partial process-file file-id idx (dt/tpoint))) + (px/run! executor (partial process-file file-id idx (ct/tpoint))) (inc idx)) process-files @@ -542,7 +542,7 @@ (l/dbg :hint "process:error" :cause cause)) (finally - (let [elapsed (dt/format-duration (tpoint))] + (let [elapsed (ct/format-duration (tpoint))] (l/dbg :hint "process:end" :rollback rollback? :elapsed elapsed)))))) @@ -556,7 +556,7 @@ "Mark a project for deletion" [file-id] (let [file-id (h/parse-uuid file-id) - tnow (dt/now)] + tnow (ct/now)] (audit/insert! main/system {::audit/name "delete-file" @@ -618,7 +618,7 @@ ::audit/props file ::audit/context {:triggered-by "srepl" :cause "explicit call to restore-file!"} - ::audit/tracked-at (dt/now)}) + ::audit/tracked-at (ct/now)}) (restore-file* system file-id)))))) @@ -626,7 +626,7 @@ "Mark a project for deletion" [project-id] (let [project-id (h/parse-uuid project-id) - tnow (dt/now)] + tnow (ct/now)] (audit/insert! main/system {::audit/name "delete-project" @@ -673,7 +673,7 @@ ::audit/props project ::audit/context {:triggered-by "srepl" :cause "explicit call to restore-team!"} - ::audit/tracked-at (dt/now)}) + ::audit/tracked-at (ct/now)}) (restore-project* system project-id)))))) @@ -681,7 +681,7 @@ "Mark a team for deletion" [team-id] (let [team-id (h/parse-uuid team-id) - tnow (dt/now)] + tnow (ct/now)] (audit/insert! main/system {::audit/name "delete-team" @@ -733,7 +733,7 @@ ::audit/props team ::audit/context {:triggered-by "srepl" :cause "explicit call to restore-team!"} - ::audit/tracked-at (dt/now)}) + ::audit/tracked-at (ct/now)}) (restore-team* system team-id)))))) @@ -741,7 +741,7 @@ "Mark a profile for deletion." [profile-id] (let [profile-id (h/parse-uuid profile-id) - tnow (dt/now)] + tnow (ct/now)] (audit/insert! main/system {::audit/name "delete-profile" @@ -775,7 +775,7 @@ ::audit/props (audit/profile->props profile) ::audit/context {:triggered-by "srepl" :cause "explicit call to restore-profile!"} - ::audit/tracked-at (dt/now)}) + ::audit/tracked-at (ct/now)}) (db/update! system :profile {:deleted-at nil} @@ -821,7 +821,7 @@ {:deleted deleted :total total})))] (let [path (fs/path path) - deleted-at (dt/minus (dt/now) (cf/get-deletion-delay))] + deleted-at (ct/minus (ct/now) (cf/get-deletion-delay))] (when-not (fs/exists? path) (throw (ex-info "path does not exists" {:path path}))) @@ -905,7 +905,7 @@ (db/tx-run! main/system (fn [{:keys [::db/conn] :as cfg}] (db/exec-one! conn ["SET CONSTRAINTS ALL DEFERRED"]) - (let [team (-> (assoc cfg ::bfc/timestamp (dt/now)) + (let [team (-> (assoc cfg ::bfc/timestamp (ct/now)) (mgmt/duplicate-team :team-id team-id :name name)) rels (db/query conn :team-profile-rel {:team-id team-id})] diff --git a/backend/src/app/storage.clj b/backend/src/app/storage.clj index fd573079f5..f92f2ac7ef 100644 --- a/backend/src/app/storage.clj +++ b/backend/src/app/storage.clj @@ -12,13 +12,13 @@ [app.common.data.macros :as dm] [app.common.logging :as l] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.uuid :as uuid] [app.config :as cf] [app.db :as db] [app.storage.fs :as sfs] [app.storage.impl :as impl] [app.storage.s3 :as ss3] - [app.util.time :as dt] [cuerdas.core :as str] [datoteka.fs :as fs] [integrant.core :as ig]) @@ -122,7 +122,7 @@ (dissoc :id)) touched-at (if touch - (or touched-at (dt/now)) + (or touched-at (ct/now)) touched-at) ;; NOTE: for now we don't reuse the deleted objects, but in @@ -224,7 +224,7 @@ (assert (valid-storage? storage)) (let [id (if (impl/object? object-or-id) (:id object-or-id) object-or-id)] (-> (db/update! connectable :storage-object - {:touched-at (dt/now)} + {:touched-at (ct/now)} {:id id}) (db/get-update-count) (pos?)))) @@ -235,7 +235,7 @@ [storage object] (assert (valid-storage? storage)) (when (or (nil? (:expired-at object)) - (dt/is-after? (:expired-at object) (dt/now))) + (ct/is-after? (:expired-at object) (ct/now))) (-> (impl/resolve-backend storage (:backend object)) (impl/get-object-data object)))) @@ -244,7 +244,7 @@ [storage object] (assert (valid-storage? storage)) (when (or (nil? (:expired-at object)) - (dt/is-after? (:expired-at object) (dt/now))) + (ct/is-after? (:expired-at object) (ct/now))) (-> (impl/resolve-backend storage (:backend object)) (impl/get-object-bytes object)))) @@ -254,7 +254,7 @@ ([storage object options] (assert (valid-storage? storage)) (when (or (nil? (:expired-at object)) - (dt/is-after? (:expired-at object) (dt/now))) + (ct/is-after? (:expired-at object) (ct/now))) (-> (impl/resolve-backend storage (:backend object)) (impl/get-object-url object options))))) @@ -266,7 +266,7 @@ (let [backend (impl/resolve-backend storage (:backend object))] (when (and (= :fs (::type backend)) (or (nil? (:expired-at object)) - (dt/is-after? (:expired-at object) (dt/now)))) + (ct/is-after? (:expired-at object) (ct/now)))) (-> (impl/get-object-url backend object nil) file-url->path)))) (defn del-object! @@ -274,7 +274,7 @@ (assert (valid-storage? storage)) (let [id (if (impl/object? object-or-id) (:id object-or-id) object-or-id) res (db/update! connectable :storage-object - {:deleted-at (dt/now)} + {:deleted-at (ct/now)} {:id id})] (pos? (db/get-update-count res)))) diff --git a/backend/src/app/storage/gc_deleted.clj b/backend/src/app/storage/gc_deleted.clj index 369ddc11b4..51dd18037b 100644 --- a/backend/src/app/storage/gc_deleted.clj +++ b/backend/src/app/storage/gc_deleted.clj @@ -15,10 +15,10 @@ (:require [app.common.data :as d] [app.common.logging :as l] + [app.common.time :as ct] [app.db :as db] [app.storage :as sto] [app.storage.impl :as impl] - [app.util.time :as dt] [integrant.core :as ig])) (def ^:private sql:lock-sobjects @@ -106,18 +106,18 @@ (defmethod ig/expand-key ::handler [k v] - {k (assoc v ::min-age (dt/duration {:hours 2}))}) + {k (assoc v ::min-age (ct/duration {:hours 2}))}) (defmethod ig/init-key ::handler [_ {:keys [::min-age] :as cfg}] (fn [{:keys [props] :as task}] - (let [min-age (dt/duration (or (:min-age props) min-age))] + (let [min-age (ct/duration (or (:min-age props) min-age))] (db/tx-run! cfg (fn [cfg] (let [cfg (assoc cfg ::min-age min-age) total (clean-deleted! cfg)] (l/inf :hint "task finished" - :min-age (dt/format-duration min-age) + :min-age (ct/format-duration min-age) :total total) {:deleted total})))))) diff --git a/backend/src/app/storage/s3.clj b/backend/src/app/storage/s3.clj index 36fccd120c..73929c1639 100644 --- a/backend/src/app/storage/s3.clj +++ b/backend/src/app/storage/s3.clj @@ -12,11 +12,11 @@ [app.common.exceptions :as ex] [app.common.logging :as l] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.uri :as u] [app.storage :as-alias sto] [app.storage.impl :as impl] [app.storage.tmp :as tmp] - [app.util.time :as dt] [app.worker :as-alias wrk] [clojure.java.io :as io] [datoteka.fs :as fs] @@ -69,7 +69,7 @@ 20000) (def default-timeout - (dt/duration {:seconds 30})) + (ct/duration {:seconds 30})) (declare put-object) (declare get-object-bytes) @@ -338,11 +338,11 @@ (p/fmap #(.asByteArray ^ResponseBytes %))))) (def default-max-age - (dt/duration {:minutes 10})) + (ct/duration {:minutes 10})) (defn- get-object-url [{:keys [::presigner ::bucket ::prefix]} {:keys [id]} {:keys [max-age] :or {max-age default-max-age}}] - (assert (dt/duration? max-age) "expected valid duration instance") + (assert (ct/duration? max-age) "expected valid duration instance") (let [gor (.. (GetObjectRequest/builder) (bucket bucket) diff --git a/backend/src/app/storage/tmp.clj b/backend/src/app/storage/tmp.clj index d40baf6676..915195fbb2 100644 --- a/backend/src/app/storage/tmp.clj +++ b/backend/src/app/storage/tmp.clj @@ -12,8 +12,8 @@ (:require [app.common.logging :as l] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.uuid :as uuid] - [app.util.time :as dt] [app.worker :as wrk] [datoteka.fs :as fs] [datoteka.io :as io] @@ -38,7 +38,7 @@ (defmethod ig/expand-key ::cleaner [k v] - {k (assoc v ::min-age (dt/duration "60m"))}) + {k (assoc v ::min-age (ct/duration "60m"))}) (defmethod ig/init-key ::cleaner [_ cfg] @@ -52,13 +52,13 @@ (defn- io-loop [{:keys [::min-age] :as cfg}] - (l/inf :hint "started tmp cleaner" :default-min-age (dt/format-duration min-age)) + (l/inf :hint "started tmp cleaner" :default-min-age (ct/format-duration min-age)) (try (loop [] (when-let [[path min-age'] (sp/take! queue)] (let [min-age (or min-age' min-age)] (l/dbg :hint "schedule tempfile deletion" :path path - :expires-at (dt/plus (dt/now) min-age)) + :expires-at (ct/plus (ct/now) min-age)) (px/schedule! (inst-ms min-age) (partial remove-temp-file cfg path)) (recur)))) (catch InterruptedException _ @@ -87,7 +87,7 @@ path (fs/join default-tmp-dir (str prefix (uuid/next) suffix)) path (Files/createFile path attrs)] (fs/delete-on-exit! path) - (sp/offer! queue [path (some-> min-age dt/duration)]) + (sp/offer! queue [path (some-> min-age ct/duration)]) path)) (defn tempfile-from diff --git a/backend/src/app/tasks/delete_object.clj b/backend/src/app/tasks/delete_object.clj index 29370e61e0..a9e50921ae 100644 --- a/backend/src/app/tasks/delete_object.clj +++ b/backend/src/app/tasks/delete_object.clj @@ -8,10 +8,10 @@ "A generic task for object deletion cascade handling" (:require [app.common.logging :as l] + [app.common.time :as ct] [app.db :as db] [app.rpc.commands.files :as files] [app.rpc.commands.profile :as profile] - [app.util.time :as dt] [integrant.core :as ig])) (def ^:dynamic *team-deletion* false) @@ -23,7 +23,7 @@ [{:keys [::db/conn] :as cfg} {:keys [id deleted-at]}] (when-let [file (db/get* conn :file {:id id} {::db/remove-deleted false})] (l/trc :hint "marking for deletion" :rel "file" :id (str id) - :deleted-at (dt/format-instant deleted-at)) + :deleted-at (ct/format-inst deleted-at)) (db/update! conn :file {:deleted-at deleted-at} @@ -62,7 +62,7 @@ (defmethod delete-object :project [{:keys [::db/conn] :as cfg} {:keys [id deleted-at]}] (l/trc :hint "marking for deletion" :rel "project" :id (str id) - :deleted-at (dt/format-instant deleted-at)) + :deleted-at (ct/format-inst deleted-at)) (db/update! conn :project {:deleted-at deleted-at} @@ -79,7 +79,7 @@ (defmethod delete-object :team [{:keys [::db/conn] :as cfg} {:keys [id deleted-at]}] (l/trc :hint "marking for deletion" :rel "team" :id (str id) - :deleted-at (dt/format-instant deleted-at)) + :deleted-at (ct/format-inst deleted-at)) (db/update! conn :team {:deleted-at deleted-at} {:id id} @@ -101,7 +101,7 @@ (defmethod delete-object :profile [{:keys [::db/conn] :as cfg} {:keys [id deleted-at]}] (l/trc :hint "marking for deletion" :rel "profile" :id (str id) - :deleted-at (dt/format-instant deleted-at)) + :deleted-at (ct/format-inst deleted-at)) (db/update! conn :profile {:deleted-at deleted-at} diff --git a/backend/src/app/tasks/file_gc.clj b/backend/src/app/tasks/file_gc.clj index e2358061d7..0201f194c8 100644 --- a/backend/src/app/tasks/file_gc.clj +++ b/backend/src/app/tasks/file_gc.clj @@ -16,6 +16,7 @@ [app.common.files.validate :as cfv] [app.common.logging :as l] [app.common.thumbnails :as thc] + [app.common.time :as ct] [app.common.types.components-list :as ctkl] [app.common.types.file :as ctf] [app.common.types.shape-tree :as ctt] @@ -23,7 +24,6 @@ [app.db :as db] [app.features.fdata :as feat.fdata] [app.storage :as sto] - [app.util.time :as dt] [app.worker :as wrk] [integrant.core :as ig])) @@ -282,7 +282,7 @@ (defmethod ig/init-key ::handler [_ cfg] (fn [{:keys [props] :as task}] - (let [min-age (dt/duration (or (:min-age props) + (let [min-age (ct/duration (or (:min-age props) (cf/get-deletion-delay))) file-id (get props :file-id) cfg (-> cfg diff --git a/backend/src/app/tasks/file_gc_scheduler.clj b/backend/src/app/tasks/file_gc_scheduler.clj index dfa08ebcfc..5dd732f404 100644 --- a/backend/src/app/tasks/file_gc_scheduler.clj +++ b/backend/src/app/tasks/file_gc_scheduler.clj @@ -8,9 +8,9 @@ "A maintenance task that is responsible of properly scheduling the file-gc task for all files that matches the eligibility threshold." (:require + [app.common.time :as ct] [app.config :as cf] [app.db :as db] - [app.util.time :as dt] [app.worker :as wrk] [integrant.core :as ig])) @@ -53,7 +53,7 @@ (defmethod ig/init-key ::handler [_ cfg] (fn [{:keys [props] :as task}] - (let [min-age (dt/duration (or (:min-age props) (::min-age cfg)))] + (let [min-age (ct/duration (or (:min-age props) (::min-age cfg)))] (-> cfg (assoc ::db/rollback (:rollback? props)) (assoc ::min-age min-age) diff --git a/backend/src/app/tasks/objects_gc.clj b/backend/src/app/tasks/objects_gc.clj index 548324f98a..b44286e3dd 100644 --- a/backend/src/app/tasks/objects_gc.clj +++ b/backend/src/app/tasks/objects_gc.clj @@ -9,9 +9,9 @@ of deleted or unreachable objects." (:require [app.common.logging :as l] + [app.common.time :as ct] [app.db :as db] [app.storage :as sto] - [app.util.time :as dt] [integrant.core :as ig])) (def ^:private sql:get-profiles @@ -53,7 +53,7 @@ (l/trc :hint "permanently delete" :rel "team" :id (str id) - :deleted-at (dt/format-instant deleted-at)) + :deleted-at (ct/format-inst deleted-at)) ;; Mark as deleted the storage object (some->> photo-id (sto/touch-object! storage)) @@ -82,7 +82,7 @@ :rel "team-font-variant" :id (str id) :team-id (str team-id) - :deleted-at (dt/format-instant deleted-at)) + :deleted-at (ct/format-inst deleted-at)) ;; Mark as deleted the all related storage objects (some->> (:woff1-file-id font) (sto/touch-object! storage)) @@ -114,7 +114,7 @@ :rel "project" :id (str id) :team-id (str team-id) - :deleted-at (dt/format-instant deleted-at)) + :deleted-at (ct/format-inst deleted-at)) ;; And finally, permanently delete the project. (db/delete! conn :project {:id id}) @@ -140,7 +140,7 @@ :rel "file" :id (str id) :project-id (str project-id) - :deleted-at (dt/format-instant deleted-at)) + :deleted-at (ct/format-inst deleted-at)) (when (= "objects-storage" (:data-backend file)) (sto/touch-object! storage (:data-ref-id file))) @@ -169,7 +169,7 @@ :rel "file-thumbnail" :file-id (str file-id) :revn revn - :deleted-at (dt/format-instant deleted-at)) + :deleted-at (ct/format-inst deleted-at)) ;; Mark as deleted the storage object (some->> media-id (sto/touch-object! storage)) @@ -198,7 +198,7 @@ :rel "file-tagged-object-thumbnail" :file-id (str file-id) :object-id object-id - :deleted-at (dt/format-instant deleted-at)) + :deleted-at (ct/format-inst deleted-at)) ;; Mark as deleted the storage object (some->> media-id (sto/touch-object! storage)) @@ -227,7 +227,7 @@ :rel "file-data-fragment" :id (str id) :file-id (str file-id) - :deleted-at (dt/format-instant deleted-at)) + :deleted-at (ct/format-inst deleted-at)) (some->> data-ref-id (sto/touch-object! storage)) (db/delete! conn :file-data-fragment {:file-id file-id :id id}) @@ -253,7 +253,7 @@ :rel "file-media-object" :id (str id) :file-id (str file-id) - :deleted-at (dt/format-instant deleted-at)) + :deleted-at (ct/format-inst deleted-at)) ;; Mark as deleted the all related storage objects (some->> (:media-id fmo) (sto/touch-object! storage)) @@ -282,7 +282,7 @@ :rel "file-change" :id (str id) :file-id (str file-id) - :deleted-at (dt/format-instant deleted-at)) + :deleted-at (ct/format-inst deleted-at)) (when (= "objects-storage" (:data-backend xlog)) (sto/touch-object! storage (:data-ref-id xlog))) @@ -328,7 +328,7 @@ (defmethod ig/init-key ::handler [_ cfg] (fn [{:keys [props] :as task}] - (let [threshold (dt/duration (get props :deletion-threshold 0)) + (let [threshold (ct/duration (get props :deletion-threshold 0)) cfg (assoc cfg ::deletion-threshold (db/interval threshold))] (loop [procs (map deref deletion-proc-vars) total 0] diff --git a/backend/src/app/tokens.clj b/backend/src/app/tokens.clj index 60b0d50b22..14415d0892 100644 --- a/backend/src/app/tokens.clj +++ b/backend/src/app/tokens.clj @@ -10,8 +10,8 @@ [app.common.data :as d] [app.common.data.macros :as dm] [app.common.exceptions :as ex] + [app.common.time :as ct] [app.common.transit :as t] - [app.util.time :as dt] [buddy.sign.jwe :as jwe])) (defn generate @@ -22,7 +22,7 @@ (bytes? tokens-key)) (let [payload (-> claims - (assoc :iat (dt/now)) + (assoc :iat (ct/now)) (d/without-nils) (t/encode))] (jwe/encrypt payload tokens-key {:alg :a256kw :enc :a256gcm}))) @@ -35,8 +35,8 @@ (defn verify [sprops {:keys [token] :as params}] (let [claims (decode sprops token)] - (when (and (dt/instant? (:exp claims)) - (dt/is-before? (:exp claims) (dt/now))) + (when (and (ct/inst? (:exp claims)) + (ct/is-before? (:exp claims) (ct/now))) (ex/raise :type :validation :code :invalid-token :reason :token-expired diff --git a/backend/src/app/util/cache.clj b/backend/src/app/util/cache.clj index 4cba3ae822..3506a2fb3c 100644 --- a/backend/src/app/util/cache.clj +++ b/backend/src/app/util/cache.clj @@ -9,7 +9,7 @@ (:refer-clojure :exclude [get]) (:require [app.common.schema :as sm] - [app.util.time :as dt] + [app.common.time :as ct] [promesa.exec :as px]) (:import com.github.benmanes.caffeine.cache.AsyncCache @@ -51,7 +51,7 @@ (let [cache (as-> (Caffeine/newBuilder) builder (if (fn? on-remove) (.removalListener builder (create-listener on-remove)) builder) (if executor (.executor builder ^Executor (px/resolve-executor executor)) builder) - (if keepalive (.expireAfterAccess builder ^Duration (dt/duration keepalive)) builder) + (if keepalive (.expireAfterAccess builder ^Duration (ct/duration keepalive)) builder) (if (int? max-size) (.maximumSize builder (long max-size)) builder) (.recordStats builder) (.buildAsync builder)) diff --git a/backend/src/app/util/cron.clj b/backend/src/app/util/cron.clj new file mode 100644 index 0000000000..5f810825c7 --- /dev/null +++ b/backend/src/app/util/cron.clj @@ -0,0 +1,138 @@ +;; 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.util.cron + (:require + [app.common.exceptions :as ex]) + (:import + java.time.Instant + java.util.Date + org.apache.logging.log4j.core.util.CronExpression)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Cron Expression +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; Cron expressions are comprised of 6 required fields and one +;; optional field separated by white space. The fields respectively +;; are described as follows: +;; +;; Field Name Allowed Values Allowed Special Characters +;; Seconds 0-59 , - * / +;; Minutes 0-59 , - * / +;; Hours 0-23 , - * / +;; Day-of-month 1-31 , - * ? / L W +;; Month 0-11 or JAN-DEC , - * / +;; Day-of-Week 1-7 or SUN-SAT , - * ? / L # +;; Year (Optional) empty, 1970-2199 , - * / +;; +;; The '*' character is used to specify all values. For example, "*" +;; in the minute field means "every minute". +;; +;; The '?' character is allowed for the day-of-month and day-of-week +;; fields. It is used to specify 'no specific value'. This is useful +;; when you need to specify something in one of the two fields, but +;; not the other. +;; +;; The '-' character is used to specify ranges For example "10-12" in +;; the hour field means "the hours 10, 11 and 12". +;; +;; The ',' character is used to specify additional values. For +;; example "MON,WED,FRI" in the day-of-week field means "the days +;; Monday, Wednesday, and Friday". +;; +;; The '/' character is used to specify increments. For example "0/15" +;; in the seconds field means "the seconds 0, 15, 30, and +;; 45". And "5/15" in the seconds field means "the seconds 5, 20, 35, +;; and 50". Specifying '*' before the '/' is equivalent to specifying +;; 0 is the value to start with. Essentially, for each field in the +;; expression, there is a set of numbers that can be turned on or +;; off. For seconds and minutes, the numbers range from 0 to 59. For +;; hours 0 to 23, for days of the month 0 to 31, and for months 0 to +;; 11 (JAN to DEC). The "/" character simply helps you turn on +;; every "nth" value in the given set. Thus "7/6" in the month field +;; only turns on month "7", it does NOT mean every 6th month, please +;; note that subtlety. +;; +;; The 'L' character is allowed for the day-of-month and day-of-week +;; fields. This character is short-hand for "last", but it has +;; different meaning in each of the two fields. For example, the +;; value "L" in the day-of-month field means "the last day of the +;; month" - day 31 for January, day 28 for February on non-leap +;; years. If used in the day-of-week field by itself, it simply +;; means "7" or "SAT". But if used in the day-of-week field after +;; another value, it means "the last xxx day of the month" - for +;; example "6L" means "the last friday of the month". You can also +;; specify an offset from the last day of the month, such as "L-3" +;; which would mean the third-to-last day of the calendar month. When +;; using the 'L' option, it is important not to specify lists, or +;; ranges of values, as you'll get confusing/unexpected results. +;; +;; The 'W' character is allowed for the day-of-month field. This +;; character is used to specify the weekday (Monday-Friday) nearest +;; the given day. As an example, if you were to specify "15W" as the +;; value for the day-of-month field, the meaning is: "the nearest +;; weekday to the 15th of the month". So if the 15th is a Saturday, +;; the trigger will fire on Friday the 14th. If the 15th is a Sunday, +;; the trigger will fire on Monday the 16th. If the 15th is a Tuesday, +;; then it will fire on Tuesday the 15th. However if you specify "1W" +;; as the value for day-of-month, and the 1st is a Saturday, the +;; trigger will fire on Monday the 3rd, as it will not 'jump' over the +;; boundary of a month's days. The 'W' character can only be specified +;; when the day-of-month is a single day, not a range or list of days. +;; +;; The 'L' and 'W' characters can also be combined for the +;; day-of-month expression to yield 'LW', which translates to "last +;; weekday of the month". +;; +;; The '#' character is allowed for the day-of-week field. This +;; character is used to specify "the nth" XXX day of the month. For +;; example, the value of "6#3" in the day-of-week field means the +;; third Friday of the month (day 6 = Friday and "#3" = the 3rd one in +;; the month). Other examples: "2#1" = the first Monday of the month +;; and "4#5" = the fifth Wednesday of the month. Note that if you +;; specify "#5" and there is not 5 of the given day-of-week in the +;; month, then no firing will occur that month. If the '#' character +;; is used, there can only be one expression in the day-of-week +;; field ("3#1,6#3" is not valid, since there are two expressions). +;; +;; The legal characters and the names of months and days of the week +;; are not case sensitive. + +(defn cron + "Creates an instance of CronExpression from string." + [s] + (try + (CronExpression. s) + (catch java.text.ParseException e + (ex/raise :type :parse + :code :invalid-cron-expression + :cause e + :context {:expr s})))) + +(defn cron-expr? + [v] + (instance? CronExpression v)) + +(defn next-valid-instant-from + [^CronExpression cron ^Instant now] + (assert (cron-expr? cron)) + (.toInstant (.getNextValidTimeAfter cron (Date/from now)))) + +(defn get-next + [cron tnow] + (let [nt (next-valid-instant-from cron tnow)] + (cons nt (lazy-seq (get-next cron nt))))) + +(defmethod print-method CronExpression + [o w] + (print-dup o w)) + +(defmethod print-dup CronExpression + [mv ^java.io.Writer writer] + ;; Do not delete this comment + ;; (print-ctor o (fn [o w] (print-dup (.toString ^CronExpression o) w)) w) + (.write writer (str "#penpot/cron \"" (.toString ^CronExpression mv) "\""))) diff --git a/backend/src/app/util/pointer_map.clj b/backend/src/app/util/pointer_map.clj index eb5fc19b20..b82143df9f 100644 --- a/backend/src/app/util/pointer_map.clj +++ b/backend/src/app/util/pointer_map.clj @@ -37,9 +37,9 @@ (:require [app.common.fressian :as fres] + [app.common.time :as ct] [app.common.transit :as t] [app.common.uuid :as uuid] - [app.util.time :as dt] [clojure.core :as c] [clojure.data.json :as json]) (:import @@ -104,7 +104,7 @@ (clone [this] (when-not loaded? (load! this)) - (let [mdata (assoc mdata :created-at (dt/now)) + (let [mdata (assoc mdata :created-at (ct/now)) id (uuid/next) pmap (PointerMap. id mdata @@ -179,7 +179,7 @@ (let [odata' (assoc odata key val)] (if (identical? odata odata') this - (let [mdata (assoc mdata :created-at (dt/now)) + (let [mdata (assoc mdata :created-at (ct/now)) id (if modified? id (uuid/next)) pmap (PointerMap. id mdata @@ -197,7 +197,7 @@ (let [odata' (dissoc odata key)] (if (identical? odata odata') this - (let [mdata (assoc mdata :created-at (dt/now)) + (let [mdata (assoc mdata :created-at (ct/now)) id (if modified? id (uuid/next)) pmap (PointerMap. id mdata @@ -220,7 +220,7 @@ (defn create ([] (let [id (uuid/next) - mdata (assoc *metadata* :created-at (dt/now)) + mdata (assoc *metadata* :created-at (ct/now)) pmap (PointerMap. id mdata {} true true)] (some-> *tracked* (swap! assoc id pmap)) pmap)) @@ -239,7 +239,7 @@ (do (some-> *tracked* (swap! assoc (get-id data) data)) data) - (let [mdata (assoc (meta data) :created-at (dt/now)) + (let [mdata (assoc (meta data) :created-at (ct/now)) id (uuid/next) pmap (PointerMap. id mdata diff --git a/backend/src/app/util/time.clj b/backend/src/app/util/time.clj deleted file mode 100644 index d2ffc4ef85..0000000000 --- a/backend/src/app/util/time.clj +++ /dev/null @@ -1,399 +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.util.time - (:require - [app.common.data.macros :as dm] - [app.common.exceptions :as ex] - [app.common.schema :as sm] - [app.common.schema.openapi :as-alias oapi] - [app.common.time :as common-time] - [clojure.spec.alpha :as s] - [clojure.test.check.generators :as tgen] - [cuerdas.core :as str] - [fipp.ednize :as fez]) - (:import - java.nio.file.attribute.FileTime - java.time.Duration - java.time.Instant - java.time.OffsetDateTime - java.time.ZoneId - java.time.ZonedDateTime - java.time.format.DateTimeFormatter - java.time.temporal.ChronoUnit - java.time.temporal.Temporal - java.time.temporal.TemporalAmount - java.time.temporal.TemporalUnit - java.util.Date - org.apache.logging.log4j.core.util.CronExpression)) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Instant & Duration -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defn temporal-unit - [o] - (if (instance? TemporalUnit o) - o - (case o - :nanos ChronoUnit/NANOS - :millis ChronoUnit/MILLIS - :micros ChronoUnit/MICROS - :seconds ChronoUnit/SECONDS - :minutes ChronoUnit/MINUTES - :hours ChronoUnit/HOURS - :days ChronoUnit/DAYS))) - -;; --- DURATION - -(defn- obj->duration - [params] - (reduce-kv (fn [o k v] - (.plus ^Duration o ^long v ^TemporalUnit (temporal-unit k))) - (Duration/ofMillis 0) - params)) - -(defn duration? - [v] - (instance? Duration v)) - -(defn duration - [ms-or-obj] - (cond - (string? ms-or-obj) - (Duration/parse (str "PT" ms-or-obj)) - - (duration? ms-or-obj) - ms-or-obj - - (integer? ms-or-obj) - (Duration/ofMillis ms-or-obj) - - :else - (obj->duration ms-or-obj))) - -(defn ->seconds - [d] - (-> d inst-ms (/ 1000) int)) - -(defn diff - [t1 t2] - (Duration/between t1 t2)) - -(defn truncate - [o unit] - (let [unit (temporal-unit unit)] - (cond - (instance? Instant o) - (.truncatedTo ^Instant o ^TemporalUnit unit) - - (instance? Duration o) - (.truncatedTo ^Duration o ^TemporalUnit unit) - - :else - (throw (IllegalArgumentException. "only instant and duration allowed"))))) - -(s/def ::duration - (s/conformer - (fn [v] - (cond - (duration? v) v - - (string? v) - (try - (duration v) - (catch java.time.format.DateTimeParseException _e - ::s/invalid)) - - :else - ::s/invalid)) - (fn [v] - (subs (str v) 2)))) - -(extend-protocol clojure.core/Inst - java.time.Duration - (inst-ms* [v] (.toMillis ^Duration v)) - - OffsetDateTime - (inst-ms* [v] (.toEpochMilli (.toInstant ^OffsetDateTime v))) - - FileTime - (inst-ms* [v] (.toMillis ^FileTime v))) - -(defmethod print-method Duration - [mv ^java.io.Writer writer] - (.write writer (str "#app/duration \"" (str/lower (subs (str mv) 2)) "\""))) - -(defmethod print-dup Duration [o w] - (print-method o w)) - -(extend-protocol fez/IEdn - Duration - (-edn [o] - (tagged-literal 'app/duration (str o)))) - -(defn format-duration - [o] - (str/lower (subs (str o) 2))) - -;; --- INSTANT - -(defn instant? - [v] - (instance? Instant v)) - -(defn instant - ([s] - (cond - (instant? s) s - (int? s) (Instant/ofEpochMilli s) - :else (Instant/parse s))) - ([s fmt] - (case fmt - :rfc1123 (Instant/from (.parse DateTimeFormatter/RFC_1123_DATE_TIME ^String s)) - :iso (Instant/from (.parse DateTimeFormatter/ISO_INSTANT ^String s)) - :iso8601 (Instant/from (.parse DateTimeFormatter/ISO_INSTANT ^String s))))) - -(defn is-after? - "Analgous to: da > db" - [da db] - (.isAfter ^Instant da ^Instant db)) - -(defn is-before? - [da db] - (.isBefore ^Instant da ^Instant db)) - -(defn plus - [d ta] - (let [^TemporalAmount ta (duration ta)] - (cond - (instance? Duration d) - (.plus ^Duration d ta) - - (instance? Temporal d) - (.plus ^Temporal d ta) - - :else - (throw (UnsupportedOperationException. "unsupported type"))))) - -(defn minus - [d ta] - (let [^TemporalAmount ta (duration ta)] - (cond - (instance? Duration d) - (.minus ^Duration d ta) - - (instance? Temporal d) - (.minus ^Temporal d ta) - - :else - (throw (UnsupportedOperationException. "unsupported type"))))) - -(dm/export common-time/now) - -(defn in-future - [v] - (plus (now) v)) - -(defn in-past - [v] - (minus (now) v)) - -(defn instant->zoned-date-time - [v] - (ZonedDateTime/ofInstant v (ZoneId/of "UTC"))) - -(defn format-instant - ([v] (.format DateTimeFormatter/ISO_INSTANT ^Instant v)) - ([v fmt] - (case fmt - :iso - (.format DateTimeFormatter/ISO_INSTANT ^Instant v) - - :iso-local-time - (.format DateTimeFormatter/ISO_LOCAL_TIME - ^ZonedDateTime (instant->zoned-date-time v)) - - :rfc1123 - (.format DateTimeFormatter/RFC_1123_DATE_TIME - ^ZonedDateTime (instant->zoned-date-time v))))) - -(defmethod print-method Instant - [mv ^java.io.Writer writer] - (.write writer (str "#app/instant \"" (format-instant mv) "\""))) - -(defmethod print-dup Instant [o w] - (print-method o w)) - -(extend-protocol fez/IEdn - Instant - (-edn [o] (tagged-literal 'app/instant (format-instant o)))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Cron Expression -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; Cron expressions are comprised of 6 required fields and one -;; optional field separated by white space. The fields respectively -;; are described as follows: -;; -;; Field Name Allowed Values Allowed Special Characters -;; Seconds 0-59 , - * / -;; Minutes 0-59 , - * / -;; Hours 0-23 , - * / -;; Day-of-month 1-31 , - * ? / L W -;; Month 0-11 or JAN-DEC , - * / -;; Day-of-Week 1-7 or SUN-SAT , - * ? / L # -;; Year (Optional) empty, 1970-2199 , - * / -;; -;; The '*' character is used to specify all values. For example, "*" -;; in the minute field means "every minute". -;; -;; The '?' character is allowed for the day-of-month and day-of-week -;; fields. It is used to specify 'no specific value'. This is useful -;; when you need to specify something in one of the two fields, but -;; not the other. -;; -;; The '-' character is used to specify ranges For example "10-12" in -;; the hour field means "the hours 10, 11 and 12". -;; -;; The ',' character is used to specify additional values. For -;; example "MON,WED,FRI" in the day-of-week field means "the days -;; Monday, Wednesday, and Friday". -;; -;; The '/' character is used to specify increments. For example "0/15" -;; in the seconds field means "the seconds 0, 15, 30, and -;; 45". And "5/15" in the seconds field means "the seconds 5, 20, 35, -;; and 50". Specifying '*' before the '/' is equivalent to specifying -;; 0 is the value to start with. Essentially, for each field in the -;; expression, there is a set of numbers that can be turned on or -;; off. For seconds and minutes, the numbers range from 0 to 59. For -;; hours 0 to 23, for days of the month 0 to 31, and for months 0 to -;; 11 (JAN to DEC). The "/" character simply helps you turn on -;; every "nth" value in the given set. Thus "7/6" in the month field -;; only turns on month "7", it does NOT mean every 6th month, please -;; note that subtlety. -;; -;; The 'L' character is allowed for the day-of-month and day-of-week -;; fields. This character is short-hand for "last", but it has -;; different meaning in each of the two fields. For example, the -;; value "L" in the day-of-month field means "the last day of the -;; month" - day 31 for January, day 28 for February on non-leap -;; years. If used in the day-of-week field by itself, it simply -;; means "7" or "SAT". But if used in the day-of-week field after -;; another value, it means "the last xxx day of the month" - for -;; example "6L" means "the last friday of the month". You can also -;; specify an offset from the last day of the month, such as "L-3" -;; which would mean the third-to-last day of the calendar month. When -;; using the 'L' option, it is important not to specify lists, or -;; ranges of values, as you'll get confusing/unexpected results. -;; -;; The 'W' character is allowed for the day-of-month field. This -;; character is used to specify the weekday (Monday-Friday) nearest -;; the given day. As an example, if you were to specify "15W" as the -;; value for the day-of-month field, the meaning is: "the nearest -;; weekday to the 15th of the month". So if the 15th is a Saturday, -;; the trigger will fire on Friday the 14th. If the 15th is a Sunday, -;; the trigger will fire on Monday the 16th. If the 15th is a Tuesday, -;; then it will fire on Tuesday the 15th. However if you specify "1W" -;; as the value for day-of-month, and the 1st is a Saturday, the -;; trigger will fire on Monday the 3rd, as it will not 'jump' over the -;; boundary of a month's days. The 'W' character can only be specified -;; when the day-of-month is a single day, not a range or list of days. -;; -;; The 'L' and 'W' characters can also be combined for the -;; day-of-month expression to yield 'LW', which translates to "last -;; weekday of the month". -;; -;; The '#' character is allowed for the day-of-week field. This -;; character is used to specify "the nth" XXX day of the month. For -;; example, the value of "6#3" in the day-of-week field means the -;; third Friday of the month (day 6 = Friday and "#3" = the 3rd one in -;; the month). Other examples: "2#1" = the first Monday of the month -;; and "4#5" = the fifth Wednesday of the month. Note that if you -;; specify "#5" and there is not 5 of the given day-of-week in the -;; month, then no firing will occur that month. If the '#' character -;; is used, there can only be one expression in the day-of-week -;; field ("3#1,6#3" is not valid, since there are two expressions). -;; -;; The legal characters and the names of months and days of the week -;; are not case sensitive. - -(defn cron - "Creates an instance of CronExpression from string." - [s] - (try - (CronExpression. s) - (catch java.text.ParseException e - (ex/raise :type :parse - :code :invalid-cron-expression - :cause e - :context {:expr s})))) - -(defn cron? - [v] - (instance? CronExpression v)) - -(defn next-valid-instant-from - [^CronExpression cron ^Instant now] - (s/assert cron? cron) - (.toInstant (.getNextValidTimeAfter cron (Date/from now)))) - -(defn get-next - [cron tnow] - (let [nt (next-valid-instant-from cron tnow)] - (cons nt (lazy-seq (get-next cron nt))))) - -(defmethod print-method CronExpression - [mv ^java.io.Writer writer] - (.write writer (str "#app/cron \"" (.toString ^CronExpression mv) "\""))) - -(defmethod print-dup CronExpression - [o w] - (print-ctor o (fn [o w] (print-dup (.toString ^CronExpression o) w)) w)) - -(extend-protocol fez/IEdn - CronExpression - (-edn [o] (pr-str o))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Measurement Helpers -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defn tpoint - "Create a measurement checkpoint for time measurement of potentially - asynchronous flow." - [] - (let [p1 (System/nanoTime)] - #(duration {:nanos (- (System/nanoTime) p1)}))) - -(sm/register! - {:type ::instant - :pred instant? - :type-properties - {:error/message "should be an instant" - :title "instant" - :decode/string instant - :encode/string format-instant - :decode/json instant - :encode/json format-instant - :gen/gen (tgen/fmap (fn [i] (in-past i)) tgen/pos-int) - ::oapi/type "string" - ::oapi/format "iso"}}) - -(sm/register! - {:type ::duration - :pred duration? - :type-properties - {:error/message "should be a duration" - :gen/gen (tgen/fmap duration tgen/pos-int) - :title "duration" - :decode/string duration - :encode/string format-duration - :decode/json duration - :encode/json format-duration - ::oapi/type "string" - ::oapi/format "duration"}}) diff --git a/backend/src/app/util/websocket.clj b/backend/src/app/util/websocket.clj index c8c38ab79e..c266bd7c09 100644 --- a/backend/src/app/util/websocket.clj +++ b/backend/src/app/util/websocket.clj @@ -9,10 +9,10 @@ (:require [app.common.exceptions :as ex] [app.common.logging :as l] + [app.common.time :as ct] [app.common.transit :as t] [app.common.uuid :as uuid] [app.util.inet :as inet] - [app.util.time :as dt] [promesa.exec :as px] [promesa.exec.csp :as sp] [promesa.util :as pu] @@ -93,7 +93,7 @@ (assoc ::id id) (assoc ::state state) (assoc ::beats beats) - (assoc ::created-at (dt/now)) + (assoc ::created-at (ct/now)) (assoc ::input-ch input-ch) (assoc ::heartbeat-ch hbeat-ch) (assoc ::output-ch output-ch) @@ -107,7 +107,7 @@ (let [options (-> options (assoc ::channel channel) (on-connect)) - timeout (dt/duration idle-timeout)] + timeout (ct/duration idle-timeout)] (yws/set-idle-timeout! channel timeout) (px/submit! :vthread (partial start-io-loop! options)))) @@ -128,7 +128,7 @@ (fn on-message [_channel message] (when (string? message) (sp/offer! input-ch message) - (swap! state assoc ::last-activity-at (dt/now)))) + (swap! state assoc ::last-activity-at (ct/now)))) :on-pong (fn on-pong [_channel data] diff --git a/backend/src/app/worker.clj b/backend/src/app/worker.clj index a7eaf836f8..d1f728cdb7 100644 --- a/backend/src/app/worker.clj +++ b/backend/src/app/worker.clj @@ -10,11 +10,11 @@ [app.common.data :as d] [app.common.logging :as l] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.uuid :as uuid] [app.config :as cf] [app.db :as db] [app.metrics :as mtx] - [app.util.time :as dt] [cuerdas.core :as str] [integrant.core :as ig])) @@ -31,7 +31,7 @@ [f metrics tname] (let [labels (into-array String [tname])] (fn [params] - (let [tp (dt/tpoint)] + (let [tp (ct/tpoint)] (try (f params) (finally @@ -95,7 +95,7 @@ [::task [:or ::sm/text :keyword]] [::label {:optional true} ::sm/text] [::delay {:optional true} - [:or ::sm/int ::dt/duration]] + [:or ::sm/int ::ct/duration]] [::queue {:optional true} [:or ::sm/text :keyword]] [::priority {:optional true} ::sm/int] [::max-retries {:optional true} ::sm/int] @@ -111,7 +111,7 @@ (check-options! options) - (let [duration (dt/duration delay) + (let [duration (ct/duration delay) interval (db/interval duration) props (db/tjson params) id (uuid/next) @@ -129,7 +129,7 @@ :queue queue :label label :dedupe (boolean dedupe) - :delay (dt/format-duration duration) + :delay (ct/format-duration duration) :replace (or deleted 0)) (db/exec-one! conn [sql:insert-new-task id task props queue diff --git a/backend/src/app/worker/cron.clj b/backend/src/app/worker/cron.clj index 1bca3798bf..b660a4f160 100644 --- a/backend/src/app/worker/cron.clj +++ b/backend/src/app/worker/cron.clj @@ -10,8 +10,9 @@ [app.common.exceptions :as ex] [app.common.logging :as l] [app.common.schema :as sm] + [app.common.time :as ct] [app.db :as db] - [app.util.time :as dt] + [app.util.cron :as cron] [app.worker :as wrk] [app.worker.runner :refer [get-error-context]] [cuerdas.core :as str] @@ -49,7 +50,7 @@ [cfg {:keys [id cron] :as task}] (px/thread {:name (str "penpot/cron-task/" id)} - (let [tpoint (dt/tpoint)] + (let [tpoint (ct/tpoint)] (try (db/tx-run! cfg (fn [{:keys [::db/conn]}] (db/exec-one! conn ["SET LOCAL statement_timeout=0;"]) @@ -57,20 +58,20 @@ (when (lock-scheduled-task! conn id) (db/update! conn :scheduled-task {:cron-expr (str cron) - :modified-at (dt/now)} + :modified-at (ct/now)} {:id id} {::db/return-keys false}) (l/dbg :hint "start" :id id) ((:fn task) task) - (let [elapsed (dt/format-duration (tpoint))] + (let [elapsed (ct/format-duration (tpoint))] (l/dbg :hint "end" :id id :elapsed elapsed))))) (catch InterruptedException _ - (let [elapsed (dt/format-duration (tpoint))] + (let [elapsed (ct/format-duration (tpoint))] (l/debug :hint "task interrupted" :id id :elapsed elapsed))) (catch Throwable cause - (let [elapsed (dt/format-duration (tpoint))] + (let [elapsed (ct/format-duration (tpoint))] (binding [l/*context* (get-error-context cause task)] (l/err :hint "unhandled exception on running task" :id id @@ -82,10 +83,10 @@ (defn- ms-until-valid [cron] - (assert (dt/cron? cron) "expected cron instance") - (let [now (dt/now) - next (dt/next-valid-instant-from cron now)] - (dt/diff now next))) + (assert (cron/cron-expr? cron) "expected cron instance") + (let [now (ct/now) + next (cron/next-valid-instant-from cron now)] + (ct/diff now next))) (defn- schedule-cron-task [{:keys [::running] :as cfg} {:keys [cron id] :as task}] @@ -93,8 +94,8 @@ ft (px/schedule! ts (partial execute-cron-task cfg task))] (l/dbg :hint "schedule" :id id - :ts (dt/format-duration ts) - :at (dt/format-instant (dt/in-future ts))) + :ts (ct/format-duration ts) + :at (ct/format-inst (ct/in-future ts))) (swap! running #(into #{ft} (filter p/pending?) %)))) @@ -104,7 +105,7 @@ [:vector [:maybe [:map - [:cron [:fn dt/cron?]] + [:cron [:fn cron/cron-expr?]] [:task :keyword] [:props {:optional true} :map] [:id {:optional true} :keyword]]]]] diff --git a/backend/src/app/worker/dispatcher.clj b/backend/src/app/worker/dispatcher.clj index 190a46ff84..3d1ed18707 100644 --- a/backend/src/app/worker/dispatcher.clj +++ b/backend/src/app/worker/dispatcher.clj @@ -10,11 +10,11 @@ [app.common.data.macros :as dm] [app.common.logging :as l] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.transit :as t] [app.db :as db] [app.metrics :as mtx] [app.redis :as rds] - [app.util.time :as dt] [app.worker :as-alias wrk] [cuerdas.core :as str] [integrant.core :as ig] @@ -32,9 +32,9 @@ (defmethod ig/expand-key ::wrk/dispatcher [k v] {k (-> (d/without-nils v) - (assoc ::timeout (dt/duration "10s")) + (assoc ::timeout (ct/duration "10s")) (assoc ::batch-size 100) - (assoc ::wait-duration (dt/duration "5s")))}) + (assoc ::wait-duration (ct/duration "5s")))}) (defmethod ig/assert-key ::wrk/dispatcher [_ cfg] diff --git a/backend/src/app/worker/executor.clj b/backend/src/app/worker/executor.clj index 1419f2c296..acd6290bba 100644 --- a/backend/src/app/worker/executor.clj +++ b/backend/src/app/worker/executor.clj @@ -10,8 +10,8 @@ [app.common.data :as d] [app.common.logging :as l] [app.common.schema :as sm] + [app.common.time :as ct] [app.metrics :as mtx] - [app.util.time :as dt] [app.worker :as-alias wrk] [integrant.core :as ig] [promesa.exec :as px]) @@ -55,7 +55,7 @@ (defmethod ig/expand-key ::wrk/monitor [k v] {k (-> (d/without-nils v) - (assoc ::interval (dt/duration "2s")))}) + (assoc ::interval (ct/duration "2s")))}) (defmethod ig/init-key ::wrk/monitor [_ {:keys [::wrk/executor ::mtx/metrics ::interval ::wrk/name]}] diff --git a/backend/src/app/worker/runner.clj b/backend/src/app/worker/runner.clj index b8b1e84153..ce9b7ee5a9 100644 --- a/backend/src/app/worker/runner.clj +++ b/backend/src/app/worker/runner.clj @@ -12,11 +12,11 @@ [app.common.exceptions :as ex] [app.common.logging :as l] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.transit :as t] [app.db :as db] [app.metrics :as mtx] [app.redis :as rds] - [app.util.time :as dt] [app.worker :as wrk] [cuerdas.core :as str] [integrant.core :as ig] @@ -29,10 +29,10 @@ [:id ::sm/uuid] [:queue :string] [:name :string] - [:created-at ::sm/inst] - [:modified-at ::sm/inst] - [:scheduled-at {:optional true} ::sm/inst] - [:completed-at {:optional true} ::sm/inst] + [:created-at ::ct/inst] + [:modified-at ::ct/inst] + [:scheduled-at {:optional true} ::ct/inst] + [:completed-at {:optional true} ::ct/inst] [:error {:optional true} :string] [:max-retries :int] [:retry-num :int] @@ -76,10 +76,10 @@ :queue queue :runner-id id :retry (:retry-num task)) - (let [tpoint (dt/tpoint) + (let [tpoint (ct/tpoint) task-fn (wrk/get-task registry (:name task)) result (when task-fn (task-fn task)) - elapsed (dt/format-duration (tpoint)) + elapsed (ct/format-duration (tpoint)) result (if (valid-task-result? result) result {:status "completed"})] @@ -105,7 +105,7 @@ (:max-retries task)) (= ::retry (:type edata))) (cond-> {:status "retry" :error cause} - (dt/duration? (:delay edata)) + (ct/duration? (:delay edata)) (assoc :delay (:delay edata)) (= ::noop (:strategy edata)) @@ -156,13 +156,13 @@ (str error)) task (-> result meta ::task) nretry (+ (:retry-num task) inc-by) - now (dt/now) + now (ct/now) delay (->> (iterate #(* % 2) delay) (take nretry) (last))] (db/update! pool :task {:error explain :status "retry" :modified-at now - :scheduled-at (dt/plus now delay) + :scheduled-at (ct/plus now delay) :retry-num nretry} {:id (:id task)}) nil)) @@ -172,14 +172,14 @@ explain (ex-message error)] (db/update! pool :task {:error explain - :modified-at (dt/now) + :modified-at (ct/now) :status "failed"} {:id (:id task)}) nil)) (handle-task-completion [result] (let [task (-> result meta ::task) - now (dt/now)] + now (ct/now)] (db/update! pool :task {:completed-at now :modified-at now @@ -255,7 +255,7 @@ (let [cfg (-> cfg (assoc ::rds/rconn rconn) (assoc ::queue (str/ffmt "%:%" tenant queue)) - (assoc ::timeout (dt/duration "5s")))] + (assoc ::timeout (ct/duration "5s")))] (loop [] (when (px/interrupted?) (throw (InterruptedException. "interrupted"))) diff --git a/backend/src/data_readers.clj b/backend/src/data_readers.clj index cdbc3f9e60..10e9987d67 100644 --- a/backend/src/data_readers.clj +++ b/backend/src/data_readers.clj @@ -1,3 +1,3 @@ -{app/instant app.util.time/instant - app/cron app.util.time/cron - app/duration app.util.time/duration} +{penpot/inst app.common.time/inst + penpot/cron app.util.cron/cron + penpot/duration app.common.time/duration} diff --git a/backend/test/backend_tests/binfile_test.clj b/backend/test/backend_tests/binfile_test.clj index 6ddfe24634..3c774cd5d6 100644 --- a/backend/test/backend_tests/binfile_test.clj +++ b/backend/test/backend_tests/binfile_test.clj @@ -20,7 +20,6 @@ [app.rpc :as-alias rpc] [app.storage :as sto] [app.storage.tmp :as tmp] - [app.util.time :as dt] [backend-tests.helpers :as th] [clojure.test :as t] [cuerdas.core :as str] diff --git a/backend/test/backend_tests/bounce_handling_test.clj b/backend/test/backend_tests/bounce_handling_test.clj index 13774e042e..3df4789ad7 100644 --- a/backend/test/backend_tests/bounce_handling_test.clj +++ b/backend/test/backend_tests/bounce_handling_test.clj @@ -6,11 +6,11 @@ (ns backend-tests.bounce-handling-test (:require + [app.common.time :as ct] [app.db :as db] [app.email :as email] [app.http.awsns :as awsns] [app.tokens :as tokens] - [app.util.time :as dt] [backend-tests.helpers :as th] [clojure.pprint :refer [pprint]] [clojure.test :as t] @@ -250,7 +250,7 @@ (let [profile (th/create-profile* 1) pool (:app.db/pool th/*system*)] - (th/create-complaint-for pool {:type :bounce :id (:id profile) :created-at (dt/in-past {:days 8})}) + (th/create-complaint-for pool {:type :bounce :id (:id profile) :created-at (ct/in-past {:days 8})}) (th/create-complaint-for pool {:type :bounce :id (:id profile)}) (th/create-complaint-for pool {:type :bounce :id (:id profile)}) @@ -268,8 +268,8 @@ :profile-complaint-threshold 2})}] (let [profile (th/create-profile* 1) pool (:app.db/pool th/*system*)] - (th/create-complaint-for pool {:type :bounce :id (:id profile) :created-at (dt/in-past {:days 8})}) - (th/create-complaint-for pool {:type :bounce :id (:id profile) :created-at (dt/in-past {:days 8})}) + (th/create-complaint-for pool {:type :bounce :id (:id profile) :created-at (ct/in-past {:days 8})}) + (th/create-complaint-for pool {:type :bounce :id (:id profile) :created-at (ct/in-past {:days 8})}) (th/create-complaint-for pool {:type :bounce :id (:id profile)}) (th/create-complaint-for pool {:type :bounce :id (:id profile)}) (th/create-complaint-for pool {:type :complaint :id (:id profile)}) diff --git a/backend/test/backend_tests/helpers.clj b/backend/test/backend_tests/helpers.clj index 332a93789d..a589c6fe7e 100644 --- a/backend/test/backend_tests/helpers.clj +++ b/backend/test/backend_tests/helpers.clj @@ -15,6 +15,7 @@ [app.common.pprint :as pp] [app.common.schema :as sm] [app.common.spec :as us] + [app.common.time :as ct] [app.common.transit :as tr] [app.common.uuid :as uuid] [app.config :as cf] @@ -33,7 +34,6 @@ [app.rpc.helpers :as rph] [app.util.blob :as blob] [app.util.services :as sv] - [app.util.time :as dt] [app.worker :as wrk] [app.worker.runner] [clojure.java.io :as io] @@ -263,7 +263,7 @@ (dm/with-open [conn (db/open system)] (db/insert! conn :profile-complaint-report {:profile-id id - :created-at (or created-at (dt/now)) + :created-at (or created-at (ct/now)) :type (name type) :content (db/tjson {})}))) @@ -273,7 +273,7 @@ (db/insert! conn :global-complaint-report {:email email :type (name type) - :created-at (or created-at (dt/now)) + :created-at (or created-at (ct/now)) :content (db/tjson {})}))) (defn create-team-role* @@ -305,7 +305,7 @@ ([system {:keys [file-id changes session-id profile-id revn] :or {session-id (uuid/next) revn 0}}] (-> system - (assoc ::files.update/timestamp (dt/now)) + (assoc ::files.update/timestamp (ct/now)) (db/tx-run! (fn [{:keys [::db/conn] :as system}] (let [file (files.update/get-file conn file-id)] (#'files.update/update-file* system @@ -379,7 +379,7 @@ ;; (app.common.pprint/pprint (:app.rpc/methods *system*)) (try-on! (method-fn (-> data (dissoc ::type) - (assoc :app.rpc/request-at (dt/now))))))) + (assoc :app.rpc/request-at (ct/now))))))) (defn run-task! ([name] @@ -525,7 +525,7 @@ (defn sleep [ms-or-duration] - (Thread/sleep (inst-ms (dt/duration ms-or-duration)))) + (Thread/sleep (inst-ms (ct/duration ms-or-duration)))) (defn config-get-mock [data] diff --git a/backend/test/backend_tests/rpc_audit_test.clj b/backend/test/backend_tests/rpc_audit_test.clj index fe5353e126..be612455d0 100644 --- a/backend/test/backend_tests/rpc_audit_test.clj +++ b/backend/test/backend_tests/rpc_audit_test.clj @@ -7,10 +7,10 @@ (ns backend-tests.rpc-audit-test (:require [app.common.pprint :as pp] + [app.common.time :as ct] [app.common.uuid :as uuid] [app.db :as db] [app.rpc :as-alias rpc] - [app.util.time :as dt] [backend-tests.helpers :as th] [clojure.test :as t] [yetti.request])) @@ -46,7 +46,7 @@ :route "dashboard-files"} :context {:engine "blink"} :profile-id (:id prof) - :timestamp (dt/now) + :timestamp (ct/now) :type "action"}]} params (with-meta params @@ -79,7 +79,7 @@ :route "dashboard-files"} :context {:engine "blink"} :profile-id uuid/zero - :timestamp (dt/now) + :timestamp (ct/now) :type "action"}]} params (with-meta params {:app.http/request http-request}) diff --git a/backend/test/backend_tests/rpc_comment_test.clj b/backend/test/backend_tests/rpc_comment_test.clj index d500352b3f..1822d28e0e 100644 --- a/backend/test/backend_tests/rpc_comment_test.clj +++ b/backend/test/backend_tests/rpc_comment_test.clj @@ -7,6 +7,7 @@ (ns backend-tests.rpc-comment-test (:require [app.common.geom.point :as gpt] + [app.common.time :as ct] [app.common.uuid :as uuid] [app.db :as db] [app.http :as http] @@ -14,7 +15,6 @@ [app.rpc.commands.comments :as comments] [app.rpc.cond :as cond] [app.rpc.quotes :as-alias quotes] - [app.util.time :as dt] [backend-tests.helpers :as th] [clojure.test :as t] [datoteka.fs :as fs] @@ -78,7 +78,7 @@ (let [{:keys [result] :as out} (th/command! data)] (t/is (th/success? out)) - (t/is (dt/instant? (:modified-at result)))) + (t/is (ct/inst? (:modified-at result)))) (let [status' (th/db-get :comment-thread-status {:thread-id (:id thread) diff --git a/backend/test/backend_tests/rpc_file_snapshot_test.clj b/backend/test/backend_tests/rpc_file_snapshot_test.clj index 1da63c4f36..fc1b3b63d0 100644 --- a/backend/test/backend_tests/rpc_file_snapshot_test.clj +++ b/backend/test/backend_tests/rpc_file_snapshot_test.clj @@ -17,7 +17,6 @@ [app.http :as http] [app.rpc :as-alias rpc] [app.storage :as sto] - [app.util.time :as dt] [backend-tests.helpers :as th] [clojure.test :as t] [cuerdas.core :as str])) diff --git a/backend/test/backend_tests/rpc_file_test.clj b/backend/test/backend_tests/rpc_file_test.clj index 9a46efd373..2443d76737 100644 --- a/backend/test/backend_tests/rpc_file_test.clj +++ b/backend/test/backend_tests/rpc_file_test.clj @@ -9,6 +9,7 @@ [app.common.features :as cfeat] [app.common.pprint :as pp] [app.common.thumbnails :as thc] + [app.common.time :as ct] [app.common.types.shape :as cts] [app.common.uuid :as uuid] [app.config :as cf] @@ -18,7 +19,6 @@ [app.rpc :as-alias rpc] [app.rpc.commands.files :as files] [app.storage :as sto] - [app.util.time :as dt] [backend-tests.helpers :as th] [clojure.test :as t] [cuerdas.core :as str])) @@ -135,7 +135,7 @@ (t/is (nil? (:users result)))))) (th/db-update! :file - {:deleted-at (dt/now)} + {:deleted-at (ct/now)} {:id file-id}) (t/testing "query single file after delete and wait" @@ -1844,7 +1844,7 @@ (th/run-task! :delete-object {:object :file - :deleted-at (dt/now) + :deleted-at (ct/now) :id (:id file-1)}) ;; Check that file media object references are marked all for deletion diff --git a/backend/test/backend_tests/rpc_file_thumbnails_test.clj b/backend/test/backend_tests/rpc_file_thumbnails_test.clj index e5a44fbdb9..b59bdbfcf3 100644 --- a/backend/test/backend_tests/rpc_file_thumbnails_test.clj +++ b/backend/test/backend_tests/rpc_file_thumbnails_test.clj @@ -16,7 +16,6 @@ [app.rpc.commands.auth :as cauth] [app.storage :as sto] [app.tokens :as tokens] - [app.util.time :as dt] [backend-tests.helpers :as th] [clojure.java.io :as io] [clojure.test :as t] diff --git a/backend/test/backend_tests/rpc_media_test.clj b/backend/test/backend_tests/rpc_media_test.clj index aa24c5e57b..d583565f39 100644 --- a/backend/test/backend_tests/rpc_media_test.clj +++ b/backend/test/backend_tests/rpc_media_test.clj @@ -6,11 +6,11 @@ (ns backend-tests.rpc-media-test (:require + [app.common.time :as ct] [app.common.uuid :as uuid] [app.db :as db] [app.rpc :as-alias rpc] [app.storage :as sto] - [app.util.time :as dt] [backend-tests.helpers :as th] [clojure.test :as t] [datoteka.fs :as fs])) @@ -257,7 +257,7 @@ :is-shared false}) _ (th/db-update! :file - {:deleted-at (dt/now)} + {:deleted-at (ct/now)} {:id (:id file)}) mfile {:filename "sample.jpg" diff --git a/backend/test/backend_tests/rpc_profile_test.clj b/backend/test/backend_tests/rpc_profile_test.clj index 8327d510c5..226df0dc61 100644 --- a/backend/test/backend_tests/rpc_profile_test.clj +++ b/backend/test/backend_tests/rpc_profile_test.clj @@ -6,6 +6,7 @@ (ns backend-tests.rpc-profile-test (:require + [app.common.time :as ct] [app.common.uuid :as uuid] [app.config :as cf] [app.db :as db] @@ -14,7 +15,6 @@ [app.rpc :as-alias rpc] [app.rpc.commands.profile :as profile] [app.tokens :as tokens] - [app.util.time :as dt] [backend-tests.helpers :as th] [clojure.java.io :as io] [clojure.test :as t] @@ -158,7 +158,7 @@ (let [row (th/db-get :team {:id (:default-team-id prof)} {::db/remove-deleted false})] - (t/is (dt/instant? (:deleted-at row)))) + (t/is (ct/inst? (:deleted-at row)))) ;; execute permanent deletion task (let [result (th/run-task! :objects-gc {:min-age 0})] @@ -212,7 +212,7 @@ ;; (th/print-result! out) (let [team (th/db-get :team {:id (:id team1)} {::db/remove-deleted false})] - (t/is (dt/instant? (:deleted-at team))))) + (t/is (ct/inst? (:deleted-at team))))) ;; Request profile to be deleted (let [params {::th/type :delete-profile @@ -517,7 +517,7 @@ (let [sprops (:app.setup/props th/*system*) itoken (tokens/generate sprops {:iss :team-invitation - :exp (dt/in-future "48h") + :exp (ct/in-future "48h") :role :editor :team-id uuid/zero :member-email "user@example.com"}) @@ -546,7 +546,7 @@ (let [sprops (:app.setup/props th/*system*) itoken (tokens/generate sprops {:iss :team-invitation - :exp (dt/in-future "48h") + :exp (ct/in-future "48h") :role :editor :team-id uuid/zero :member-email "user2@example.com"}) @@ -568,7 +568,7 @@ (let [sprops (:app.setup/props th/*system*) itoken (tokens/generate sprops {:iss :team-invitation - :exp (dt/in-future "48h") + :exp (ct/in-future "48h") :role :editor :team-id uuid/zero :member-email "user@example.com"}) @@ -589,7 +589,7 @@ (let [sprops (:app.setup/props th/*system*) itoken (tokens/generate sprops {:iss :team-invitation - :exp (dt/in-future "48h") + :exp (ct/in-future "48h") :role :editor :team-id uuid/zero :member-email "user2@example.com"}) @@ -611,7 +611,7 @@ (let [sprops (:app.setup/props th/*system*) itoken (tokens/generate sprops {:iss :team-invitation - :exp (dt/in-future "48h") + :exp (ct/in-future "48h") :role :editor :team-id uuid/zero :member-email "user2@example.com"}) diff --git a/backend/test/backend_tests/rpc_project_test.clj b/backend/test/backend_tests/rpc_project_test.clj index 728cfd6e43..652894aa47 100644 --- a/backend/test/backend_tests/rpc_project_test.clj +++ b/backend/test/backend_tests/rpc_project_test.clj @@ -11,7 +11,6 @@ [app.db :as db] [app.http :as http] [app.rpc :as-alias rpc] - [app.util.time :as dt] [backend-tests.helpers :as th] [clojure.test :as t])) diff --git a/backend/test/backend_tests/rpc_team_test.clj b/backend/test/backend_tests/rpc_team_test.clj index 60bdb62d6d..516abc824b 100644 --- a/backend/test/backend_tests/rpc_team_test.clj +++ b/backend/test/backend_tests/rpc_team_test.clj @@ -7,6 +7,7 @@ (ns backend-tests.rpc-team-test (:require [app.common.logging :as l] + [app.common.time :as ct] [app.common.uuid :as uuid] [app.config :as cf] [app.db :as db] @@ -14,7 +15,6 @@ [app.rpc :as-alias rpc] [app.storage :as sto] [app.tokens :as tokens] - [app.util.time :as dt] [backend-tests.helpers :as th] [clojure.test :as t] [datoteka.fs :as fs] @@ -163,7 +163,7 @@ ;; Proceed to delete the requester user (th/db-update! :profile - {:deleted-at (dt/in-past "1h")} + {:deleted-at (ct/in-past "1h")} {:id (:id requester)}) ;; Create a new profile with the same email @@ -271,7 +271,7 @@ (let [token (tokens/generate sprops {:iss :team-invitation - :exp (dt/in-future "1h") + :exp (ct/in-future "1h") :profile-id (:id profile1) :role :editor :team-id (:id team) @@ -283,7 +283,7 @@ {:team-id (:id team) :email-to (:email profile2) :role "editor" - :valid-until (dt/in-future "48h")}) + :valid-until (ct/in-future "48h")}) (let [data {::th/type :verify-token :token token} out (th/command! data)] @@ -328,7 +328,7 @@ {:team-id (:id team) :email-to (:email profile3) :role "editor" - :valid-until (dt/in-future "48h")}) + :valid-until (ct/in-future "48h")}) (let [data {::th/type :verify-token ::rpc/profile-id (:id profile1) @@ -381,14 +381,14 @@ {:team-id (:team-id data) :email-to "test1@mail.com" :role "editor" - :valid-until (dt/in-future "48h")}) + :valid-until (ct/in-future "48h")}) ;; insert an entry on the database with an expired invitation (db/insert! th/*pool* :team-invitation {:team-id (:team-id data) :email-to "test2@mail.com" :role "editor" - :valid-until (dt/in-past "48h")}) + :valid-until (ct/in-past "48h")}) (let [out (th/command! data)] (t/is (th/success? out)) @@ -415,7 +415,7 @@ {:team-id (:team-id data) :email-to "test1@mail.com" :role "editor" - :valid-until (dt/in-future "48h")}) + :valid-until (ct/in-future "48h")}) (let [out (th/command! data) ;; retrieve the value from the database and check its content @@ -438,7 +438,7 @@ {:team-id (:team-id data) :email-to "test1@mail.com" :role "editor" - :valid-until (dt/in-future "48h")}) + :valid-until (ct/in-future "48h")}) (let [out (th/command! data) ;; retrieve the value from the database and check its content @@ -582,7 +582,7 @@ (let [rows (th/db-exec! ["select * from team where id = ?" (:id team)])] (t/is (= 1 (count rows))) - (t/is (dt/instant? (:deleted-at (first rows))))) + (t/is (ct/inst? (:deleted-at (first rows))))) (let [result (th/run-task! :objects-gc {:deletion-threshold (cf/get-deletion-delay)})] (t/is (= 5 (:processed result)))))) @@ -626,7 +626,7 @@ (th/reset-mock! mock) (th/db-update! :team-access-request - {:valid-until (dt/in-past "1h")} + {:valid-until (ct/in-past "1h")} {:team-id (:id team) :requester-id (:id requester)}) diff --git a/backend/test/backend_tests/storage_test.clj b/backend/test/backend_tests/storage_test.clj index 64498f71a6..d282b20985 100644 --- a/backend/test/backend_tests/storage_test.clj +++ b/backend/test/backend_tests/storage_test.clj @@ -7,11 +7,11 @@ (ns backend-tests.storage-test (:require [app.common.exceptions :as ex] + [app.common.time :as ct] [app.common.uuid :as uuid] [app.db :as db] [app.rpc :as-alias rpc] [app.storage :as sto] - [app.util.time :as dt] [backend-tests.helpers :as th] [clojure.test :as t] [cuerdas.core :as str] @@ -53,12 +53,12 @@ (configure-storage-backend)) content (sto/content "content") object (sto/put-object! storage {::sto/content content - ::sto/expired-at (dt/in-future {:seconds 1}) + ::sto/expired-at (ct/in-future {:seconds 1}) :content-type "text/plain"})] (t/is (sto/object? object)) - (t/is (dt/instant? (:expired-at object))) - (t/is (dt/is-after? (:expired-at object) (dt/now))) + (t/is (ct/inst? (:expired-at object))) + (t/is (ct/is-after? (:expired-at object) (ct/now))) (t/is (= object (sto/get-object storage (:id object)))) (th/sleep 1000) @@ -73,7 +73,7 @@ content (sto/content "content") object (sto/put-object! storage {::sto/content content :content-type "text/plain" - :expired-at (dt/in-future {:seconds 1})})] + :expired-at (ct/in-future {:seconds 1})})] (t/is (sto/object? object)) (t/is (true? (sto/del-object! storage object))) @@ -95,13 +95,13 @@ content3 (sto/content "content3") object1 (sto/put-object! storage {::sto/content content1 - ::sto/expired-at (dt/now) + ::sto/expired-at (ct/now) :content-type "text/plain"}) object2 (sto/put-object! storage {::sto/content content2 - ::sto/expired-at (dt/in-past {:hours 2}) + ::sto/expired-at (ct/in-past {:hours 2}) :content-type "text/plain"}) object3 (sto/put-object! storage {::sto/content content3 - ::sto/expired-at (dt/in-past {:hours 1}) + ::sto/expired-at (ct/in-past {:hours 1}) :content-type "text/plain"})] @@ -154,7 +154,7 @@ (t/is (= (:media-id result-1) (:media-id result-2))) (th/db-update! :file-media-object - {:deleted-at (dt/now)} + {:deleted-at (ct/now)} {:id (:id result-1)}) ;; run the objects gc task for permanent deletion @@ -239,7 +239,7 @@ result-2 (:result out2)] (th/db-update! :team-font-variant - {:deleted-at (dt/now)} + {:deleted-at (ct/now)} {:id (:id result-2)}) ;; run the objects gc task for permanent deletion diff --git a/backend/test/backend_tests/tasks_telemetry_test.clj b/backend/test/backend_tests/tasks_telemetry_test.clj index 40c7b01df7..bc1b2b06ef 100644 --- a/backend/test/backend_tests/tasks_telemetry_test.clj +++ b/backend/test/backend_tests/tasks_telemetry_test.clj @@ -7,7 +7,6 @@ (ns backend-tests.tasks-telemetry-test (:require [app.db :as db] - [app.util.time :as dt] [backend-tests.helpers :as th] [clojure.pprint :refer [pprint]] [clojure.test :as t] diff --git a/common/package.json b/common/package.json index 384cb33d22..51da0f2925 100644 --- a/common/package.json +++ b/common/package.json @@ -10,15 +10,15 @@ "type": "git", "url": "https://github.com/penpot/penpot" }, - "dependencies": { - "luxon": "^3.6.1" - }, "devDependencies": { "concurrently": "^9.1.2", "nodemon": "^3.1.10", "source-map-support": "^0.5.21", "ws": "^8.18.2" }, + "dependencies": { + "date-fns": "^4.1.0" + }, "scripts": { "fmt:clj:check": "cljfmt check --parallel=false src/ test/", "fmt:clj": "cljfmt fix --parallel=true src/ test/", diff --git a/common/src/app/common/schema.cljc b/common/src/app/common/schema.cljc index 0c91aa6de3..70281ff419 100644 --- a/common/src/app/common/schema.cljc +++ b/common/src/app/common/schema.cljc @@ -14,7 +14,6 @@ [app.common.schema.generators :as sg] [app.common.schema.openapi :as-alias oapi] [app.common.schema.registry :as sr] - [app.common.time :as tm] [app.common.uri :as u] [app.common.uuid :as uuid] [clojure.core :as c] @@ -850,38 +849,38 @@ {:title "contains any" :description "contains predicate"}}))}) -(register! - {:type ::inst - :pred inst? - :type-properties - {:title "inst" - :description "Satisfies Inst protocol" - :error/message "should be an instant" - :gen/gen (->> (sg/small-int :min 0 :max 100000) - (sg/fmap (fn [v] (tm/parse-instant v)))) +;; (register! +;; {:type ::inst +;; :pred tm/instant? +;; :type-properties +;; {:title "inst" +;; :description "Satisfies Inst protocol" +;; :error/message "should be an instant" +;; :gen/gen (->> (sg/small-int :min 0 :max 100000) +;; (sg/fmap (fn [v] (tm/parse-inst v)))) - :decode/string tm/parse-instant - :encode/string tm/format-instant - :decode/json tm/parse-instant - :encode/json tm/format-instant - ::oapi/type "string" - ::oapi/format "iso"}}) +;; :decode/string tm/parse-inst +;; :encode/string tm/format-inst +;; :decode/json tm/parse-inst +;; :encode/json tm/format-inst +;; ::oapi/type "string" +;; ::oapi/format "iso"}}) -(register! - {:type ::timestamp - :pred inst? - :type-properties - {:title "inst" - :description "Satisfies Inst protocol" - :error/message "should be an instant" - :gen/gen (->> (sg/small-int) - (sg/fmap (fn [v] (tm/parse-instant v)))) - :decode/string tm/parse-instant - :encode/string inst-ms - :decode/json tm/parse-instant - :encode/json inst-ms - ::oapi/type "string" - ::oapi/format "number"}}) +;; (register! +;; {:type ::timestamp +;; :pred tm/instant? +;; :type-properties +;; {:title "inst" +;; :description "Satisfies Inst protocol, the same as ::inst but encodes to epoch" +;; :error/message "should be an instant" +;; :gen/gen (->> (sg/small-int) +;; (sg/fmap (fn [v] (tm/parse-inst v)))) +;; :decode/string tm/parse-inst +;; :encode/string inst-ms +;; :decode/json tm/parse-inst +;; :encode/json inst-ms +;; ::oapi/type "string" +;; ::oapi/format "number"}}) (register! {:type ::fn diff --git a/common/src/app/common/time.cljc b/common/src/app/common/time.cljc index 20e8b68dab..12d9ddf750 100644 --- a/common/src/app/common/time.cljc +++ b/common/src/app/common/time.cljc @@ -4,28 +4,152 @@ ;; ;; Copyright (c) KALEIDOS INC +#_{:clj-kondo/ignore [:unused-namespace]} (ns app.common.time "Minimal cross-platoform date time api for specific use cases on types definition and other common code." - #?(:cljs - (:require - ["luxon" :as lxn]) - :clj + (:refer-clojure :exclude [inst?]) + (:require + #?@(:cljs [["date-fns/format$default" :as dfn-format] + ["date-fns/formatISO$default" :as dfn-format-iso] + ["date-fns/setDefaultOptions$default" :as dfn-set-default-options] + ["date-fns/differenceInMilliseconds$default" :as dfn-diff] + ["date-fns/formatDistanceToNowStrict$default" :as dfn-distance-to-now] + ["date-fns/add$default" :as dfn-add] + ["date-fns/sub$default" :as dfn-sub] + ["date-fns/parseISO$default" :as dfn-parse-iso] + ["date-fns/locale/ar-SA$default" :as dfn-ar] + ["date-fns/locale/ca$default" :as dfn-ca] + ["date-fns/locale/cs$default" :as dfn-cs] + ["date-fns/locale/de$default" :as dfn-de] + ["date-fns/locale/el$default" :as dfn-el] + ["date-fns/locale/en-US$default" :as df-en-us] + ["date-fns/locale/es$default" :as dfn-es] + ["date-fns/locale/eu$default" :as dfn-eu] + ["date-fns/locale/fa-IR$default" :as dfn-fa-ir] + ["date-fns/locale/fr$default" :as dfn-fr] + ["date-fns/locale/gl$default" :as dfn-gl] + ["date-fns/locale/he$default" :as dfn-he] + ["date-fns/locale/hr$default" :as dfn-hr] + ["date-fns/locale/id$default" :as dfn-id] + ["date-fns/locale/it$default" :as dfn-it] + ["date-fns/locale/ja$default" :as dfn-ja] + ["date-fns/locale/ko$default" :as dfn-ko] + ["date-fns/locale/lv$default" :as dfn-lv] + ["date-fns/locale/nb$default" :as dfn-nb] + ["date-fns/locale/nl$default" :as dfn-nl] + ["date-fns/locale/pl$default" :as dfn-pl] + ["date-fns/locale/pt$default" :as dfn-pt] + ["date-fns/locale/pt-BR$default" :as dfn-pt-br] + ["date-fns/locale/ro$default" :as dfn-ro] + ["date-fns/locale/ru$default" :as dfn-ru] + ["date-fns/locale/tr$default" :as dfn-tr] + ["date-fns/locale/uk$default" :as dfn-uk] + ["date-fns/locale/zh-CN$default" :as dfn-zh-cn]]) + [app.common.schema :as sm] + [app.common.schema.generators :as sg] + [app.common.schema.openapi :as-alias oapi] + [cuerdas.core :as str]) + #?(:clj (:import - java.time.format.DateTimeFormatter + java.time.Duration java.time.Instant - java.time.Duration))) - -#?(:cljs - (def DateTime lxn/DateTime)) - -#?(:cljs - (def Duration lxn/Duration)) + java.time.OffsetDateTime + java.time.ZoneId + java.time.ZonedDateTime + java.time.format.DateTimeFormatter + java.time.temporal.ChronoUnit + java.time.temporal.Temporal + java.time.temporal.TemporalAmount + java.time.temporal.TemporalUnit))) (defn now [] #?(:clj (Instant/now) - :cljs (.local ^js DateTime))) + :cljs (new js/Date))) + +;; --- DURATION + +(defn- resolve-temporal-unit + [o] + (case o + (:nanos :nano) + #?(:clj ChronoUnit/NANOS + :cljs (throw (js/Error. "not supported nanos"))) + + (:micros :microsecond :micro) + #?(:clj ChronoUnit/MICROS + :cljs (throw (js/Error. "not supported nanos"))) + + (:millis :millisecond :milli) + #?(:clj ChronoUnit/MILLIS + :cljs "millisecond") + + (:seconds :second) + #?(:clj ChronoUnit/SECONDS + :cljs "second") + + (:minutes :minute) + #?(:clj ChronoUnit/MINUTES + :cljs "minute") + + (:hours :hour) + #?(:clj ChronoUnit/HOURS + :cljs "hour") + + (:days :day) + #?(:clj ChronoUnit/DAYS + :cljs "day"))) + +(defn temporal-unit + [o] + #?(:clj (if (instance? TemporalUnit o) o (resolve-temporal-unit o)) + :cljs (resolve-temporal-unit o))) + +#?(:clj + (defn- obj->duration + [params] + (reduce-kv (fn [o k v] + (.plus ^Duration o ^long v ^TemporalUnit (temporal-unit k))) + (Duration/ofMillis 0) + params))) + +#?(:clj + (defn duration? + [o] + (instance? Duration o))) + +(defn duration + [ms-or-obj] + #?(:clj + (cond + (string? ms-or-obj) + (Duration/parse (str "PT" ms-or-obj)) + + (duration? ms-or-obj) + ms-or-obj + + (integer? ms-or-obj) + + (Duration/ofMillis ms-or-obj) + + :else + (obj->duration ms-or-obj)) + + :cljs + (clj->js ms-or-obj))) + +#?(:clj + (defn parse-duration + [s] + (duration s))) + +#?(:clj + (defn format-duration + [o] + (str/lower (subs (str o) 2)))) + +;; --- INSTNANT & DATETIME (defn is-after? "Analgous to: da > db" @@ -44,65 +168,261 @@ (zero? result) false :else false))) -(defn instant? +(defn inst? [o] #?(:clj (instance? Instant o) - :cljs (instance? DateTime o))) + :cljs (instance? js/Date o))) -(defn parse-instant +(defn seconds + [d] + (-> d inst-ms (/ 1000) int)) + +(defn format-inst + ([v] (format-inst v :iso)) + ([v fmt] + (case fmt + (:iso :iso8601) + #?(:clj (.format DateTimeFormatter/ISO_INSTANT ^Instant v) + :cljs (dfn-format-iso v)) + + :iso-date + #?(:clj (.format DateTimeFormatter/ISO_LOCAL_DATE + ^ZonedDateTime (ZonedDateTime/ofInstant v (ZoneId/of "UTC"))) + :cljs (dfn-format-iso v #js {:representation "date"})) + + (:rfc1123 :http) + #?(:clj (.format DateTimeFormatter/RFC_1123_DATE_TIME + ^ZonedDateTime (ZonedDateTime/ofInstant v (ZoneId/of "UTC"))) + :cljs (dfn-format v "EEE, dd LLL yyyy HH:mm:ss 'GMT'")) + + #?@(:cljs [:time-24-simple + (dfn-format v "HH:mm") + + ;; DEPRECATED + :date-full + (dfn-format v "PPP") + + :localized-date + (dfn-format v "PPP") + + :localized-time + (dfn-format v "p") + + :localized-date-time + (dfn-format v "PPPp") + + (if (string? fmt) + (dfn-format v fmt) + (throw (js/Error. "unpexted format")))])))) + +#?(:cljs + (def locales + #js {:ar dfn-ar + :ca dfn-ca + :de dfn-de + :el dfn-el + :en df-en-us + :en_us df-en-us + :es dfn-es + :es_es dfn-es + :fa dfn-fa-ir + :fa_ir dfn-fa-ir + :fr dfn-fr + :he dfn-he + :pt dfn-pt + :pt_pt dfn-pt + :pt_br dfn-pt-br + :ro dfn-ro + :ru dfn-ru + :tr dfn-tr + :zh-cn dfn-zh-cn + :nl dfn-nl + :eu dfn-eu + :gl dfn-gl + :hr dfn-hr + :it dfn-it + :nb dfn-nb + :nb_no dfn-nb + :pl dfn-pl + :id dfn-id + :uk dfn-uk + :cs dfn-cs + :lv dfn-lv + :ko dfn-ko + :ja dfn-ja + :ja_jp dfn-ja})) + +#?(:cljs + (defn timeago + [v] + (when v + (dfn-distance-to-now v #js {:includeSeconds true + :addSuffix true})))) + +(defn inst [s] (cond - (instant? s) + (inst? s) s (int? s) - #?(:clj (Instant/ofEpochMilli s) - :cljs (.fromMillis ^js DateTime s #js {:zone "local" :setZone false})) + #?(:clj (Instant/ofEpochMilli s) + :cljs (new js/Date s)) (string? s) - #?(:clj (Instant/parse s) - :cljs (.fromISO ^js DateTime s)))) + #?(:clj (Instant/from (.parse DateTimeFormatter/ISO_DATE_TIME ^String s)) + :cljs (dfn-parse-iso s)) -(defn format-instant + :else + (throw (ex-info "invalid parameters" {})))) + +#?(:clj + (defn truncate + [o unit] + (let [unit (temporal-unit unit)] + (cond + (inst? o) + (.truncatedTo ^Instant o ^TemporalUnit unit) + + (instance? Duration o) + (.truncatedTo ^Duration o ^TemporalUnit unit) + + :else + (throw (IllegalArgumentException. "only instant and duration allowed")))))) + +(defn plus + [d ta] + (let [ta (duration ta)] + (cond + #?@(:clj [(duration? d) (.plus ^Duration d ^TemporalAmount ta)]) + + #?(:cljs (inst? d) + :clj (instance? Temporal d)) + #?(:cljs (dfn-add d ta) + :clj (.plus ^Temporal d ^Duration ta)) + + :else + (throw #?(:clj (UnsupportedOperationException. "unsupported type") + :cljs (js/Error. "unsupported type")))))) + +(defn minus + [d ta] + (let [^TemporalAmount ta (duration ta)] + (cond + #?@(:clj [(duration? d) (.minus ^Duration d ^TemporalAmount ta)]) + + #?(:cljs (inst? d) :clj (instance? Temporal d)) + #?(:cljs (dfn-sub d ta) + :clj (.minus ^Temporal d ^Duration ta)) + + :else + (throw #?(:clj (UnsupportedOperationException. "unsupported type") + :cljs (js/Error. "unsupported type")))))) + +(defn in-future [v] - #?(:clj (.format DateTimeFormatter/ISO_INSTANT ^Instant v) - :cljs (.toISO ^js v))) + (plus (now) v)) -;; To check for valid date time we can just use the core inst? function +(defn in-past + [v] + (minus (now) v)) + +#?(:clj + (defn diff + [t1 t2] + (Duration/between t1 t2))) #?(:cljs - (extend-protocol IComparable - DateTime - (-compare [it other] - (if ^boolean (.equals it other) - 0 - (if (< (inst-ms it) (inst-ms other)) -1 1))) - - Duration - (-compare [it other] - (if ^boolean (.equals it other) - 0 - (if (< (inst-ms it) (inst-ms other)) -1 1))))) + (defn diff-ms + [t1 t2] + (dfn-diff t2 t1))) #?(:cljs - (extend-type DateTime - cljs.core/IEquiv - (-equiv [o other] - (and (instance? DateTime other) - (== (.valueOf o) (.valueOf other)))))) + (defn set-default-locale! + [locale] + (when-let [locale (unchecked-get locales locale)] + (dfn-set-default-options #js {:locale locale})))) + +;; --- HELPERS + +#?(:clj + (defn tpoint + "Create a measurement checkpoint for time measurement of potentially + asynchronous flow." + [] + (let [p1 (System/nanoTime)] + #(duration {:nanos (- (System/nanoTime) p1)})))) #?(:cljs - (extend-protocol cljs.core/Inst - DateTime - (inst-ms* [inst] (.toMillis ^js inst)) + (defn tpoint-ms + "Create a measurement checkpoint for time measurement of potentially + asynchronous flow." + [] + (let [p1 (.now js/performance)] + #(- (.now js/performance) p1)))) - Duration - (inst-ms* [inst] (.toMillis ^js inst))) +;; --- EXTENSIONS - :clj +#?(:clj (extend-protocol clojure.core/Inst Duration (inst-ms* [v] (.toMillis ^Duration v)) + OffsetDateTime + (inst-ms* [v] (.toEpochMilli (.toInstant ^OffsetDateTime v))) + Instant (inst-ms* [v] (.toEpochMilli ^Instant v)))) + +#?(:clj + (defmethod print-method Duration + [o w] + (print-dup o w))) + +#?(:clj + (defmethod print-dup Duration + [mv ^java.io.Writer writer] + (.write writer (str "#penpot/duration \"" (str/lower (subs (str mv) 2)) "\"")))) + +#?(:clj + (defmethod print-method Instant + [o w] + (print-dup o w))) + +#?(:clj + (defmethod print-dup Instant + [mv ^java.io.Writer writer] + (.write writer (str "#penpot/inst \"" (format-inst mv) "\"")))) + +(def schema:inst + (sm/register! + {:type ::inst + :pred inst? + :type-properties + {:error/message "should be an instant" + :title "instant" + :decode/string inst + :encode/string format-inst + :decode/json inst + :encode/json format-inst + :gen/gen (->> (sg/small-int :min 0) + (sg/fmap (fn [i] (in-past i)))) + ::oapi/type "string" + ::oapi/format "iso"}})) + +#?(:clj + (def schema:duration + (sm/register! + {:type ::duration + :pred duration? + :type-properties + {:error/message "should be a duration" + :gen/gen (->> (sg/small-int :min 0) + (sg/fmap duration)) + :title "duration" + :decode/string parse-duration + :encode/string format-duration + :decode/json parse-duration + :encode/json format-duration + ::oapi/type "string" + ::oapi/format "duration"}}))) diff --git a/common/src/app/common/transit.cljc b/common/src/app/common/transit.cljc index 9226ecbc69..417cebe86b 100644 --- a/common/src/app/common/transit.cljc +++ b/common/src/app/common/transit.cljc @@ -7,7 +7,6 @@ (ns app.common.transit (:require #?(:clj [datoteka.fs :as fs]) - #?(:cljs ["luxon" :as lxn]) [app.common.data :as d] [app.common.uri :as uri] [cognitect.transit :as t] @@ -130,20 +129,18 @@ :wfn vec :rfn #(into lks/empty-linked-set %)} - {:id "duration" - :class #?(:clj Duration :cljs lxn/Duration) - :rfn (fn [v] - #?(:clj (Duration/ofMillis v) - :cljs (.fromMillis ^js lxn/Duration v))) - :wfn inst-ms} + #?(:clj + {:id "duration" + :class Duration + :rfn (fn [v] (Duration/ofMillis v)) + :wfn inst-ms}) {:id "m" - :class #?(:clj Instant :cljs lxn/DateTime) + :class #?(:clj Instant :cljs js/Date) :rfn (fn [v] - #?(:clj (-> (Long/parseLong v) - (Instant/ofEpochMilli)) - :cljs (let [ms (js/parseInt v 10)] - (.fromMillis ^js lxn/DateTime ms)))) + #?(:clj (-> (Long/parseLong v) + (Instant/ofEpochMilli)) + :cljs (new js/Date (js/parseInt v 10)))) :wfn (comp str inst-ms)} {:id "penpot/pointer" @@ -204,12 +201,30 @@ (with-open [input (ByteArrayInputStream. ^bytes data)] (t/read (reader input opts)))))) +#?(:cljs + (defn- is-date-like? + [obj] + (and ^boolean (some? obj) + ^boolean (fn? (.-getTime obj)) + ^boolean (some? (.getTime obj))))) + +#_:clj-kondo/ignore +(def ^:private date-write-handler + (t/write-handler (constantly "m") + (comp str inst-ms))) + (defn encode-str ([data] (encode-str data nil)) ([data opts] #?(:cljs (let [type (:type opts :json) - params {:handlers @write-handler-map} + params {:handlers @write-handler-map + ;; NOTE: this is necessary because the plugin + ;; secure context alters the js/Date constructor + :handlerForForeign (fn [x _] + (if (is-date-like? x) + date-write-handler + nil))} params (if (:with-meta opts) (assoc params :transform t/write-meta) params) diff --git a/common/src/app/common/types/color.cljc b/common/src/app/common/types/color.cljc index f80aebf693..cd8354eeec 100644 --- a/common/src/app/common/types/color.cljc +++ b/common/src/app/common/types/color.cljc @@ -14,6 +14,7 @@ [app.common.schema :as sm] [app.common.schema.generators :as sg] [app.common.schema.openapi :as-alias oapi] + [app.common.time :as ct] [app.common.types.plugins :as ctpg] [clojure.set :as set] [cuerdas.core :as str])) @@ -136,7 +137,7 @@ [:name ::sm/text] [:path {:optional true} :string] [:opacity {:optional true} [::sm/number {:min 0 :max 1}]] - [:modified-at {:optional true} ::sm/inst] + [:modified-at {:optional true} ::ct/inst] [:plugin-data {:optional true} ::ctpg/plugin-data]]) (def schema:library-color diff --git a/common/src/app/common/types/component.cljc b/common/src/app/common/types/component.cljc index 2ffbc70d25..55347d0135 100644 --- a/common/src/app/common/types/component.cljc +++ b/common/src/app/common/types/component.cljc @@ -8,6 +8,7 @@ (:require [app.common.data :as d] [app.common.schema :as sm] + [app.common.time :as-alias ct] [app.common.types.page :as ctp] [app.common.types.plugins :as ctpg] [app.common.types.variant :as ctv] @@ -25,7 +26,7 @@ [:id ::sm/uuid] [:name :string] [:path {:optional true} [:maybe :string]] - [:modified-at {:optional true} ::sm/inst] + [:modified-at {:optional true} ::ct/inst] [:objects {:gen/max 10 :optional true} ctp/schema:objects] [:main-instance-id ::sm/uuid] [:main-instance-page ::sm/uuid] diff --git a/common/src/app/common/types/container.cljc b/common/src/app/common/types/container.cljc index c231ba222c..b8061e58ba 100644 --- a/common/src/app/common/types/container.cljc +++ b/common/src/app/common/types/container.cljc @@ -12,6 +12,7 @@ [app.common.geom.point :as gpt] [app.common.geom.shapes :as gsh] [app.common.schema :as sm] + [app.common.time :as-alias ct] [app.common.types.component :as ctk] [app.common.types.components-list :as ctkl] [app.common.types.pages-list :as ctpl] @@ -39,7 +40,7 @@ [::sm/one-of valid-container-types]] [:name :string] [:path {:optional true} [:maybe :string]] - [:modified-at {:optional true} ::sm/inst] + [:modified-at {:optional true} ::ct/inst] [:objects {:optional true} [:map-of {:gen/max 10} ::sm/uuid :map]] [:plugin-data {:optional true} schema:plugin-data]])) diff --git a/common/src/app/common/types/file.cljc b/common/src/app/common/types/file.cljc index 08b62e686e..a49bc22df9 100644 --- a/common/src/app/common/types/file.cljc +++ b/common/src/app/common/types/file.cljc @@ -16,7 +16,7 @@ [app.common.geom.shapes.tree-seq :as gsts] [app.common.logging :as l] [app.common.schema :as sm] - [app.common.time :as dt] + [app.common.time :as ct] [app.common.types.color :as ctc] [app.common.types.component :as ctk] [app.common.types.components-list :as ctkl] @@ -34,7 +34,6 @@ [app.common.uuid :as uuid] [cuerdas.core :as str])) - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; CONSTANTS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -49,8 +48,8 @@ "A schema that represents the file media object" [:map {:title "FileMedia"} [:id ::sm/uuid] - [:created-at {:optional true} ::sm/inst] - [:deleted-at {:optional true} ::sm/inst] + [:created-at {:optional true} ::ct/inst] + [:deleted-at {:optional true} ::ct/inst] [:name :string] [:width ::sm/safe-int] [:height ::sm/safe-int] @@ -96,9 +95,9 @@ [:name :string] [:revn :int] [:vern {:optional true} :int] - [:created-at ::sm/inst] - [:modified-at ::sm/inst] - [:deleted-at {:optional true} ::sm/inst] + [:created-at ::ct/inst] + [:modified-at ::ct/inst] + [:deleted-at {:optional true} ::ct/inst] [:project-id {:optional true} ::sm/uuid] [:team-id {:optional true} ::sm/uuid] [:is-shared {:optional true} ::sm/boolean] @@ -164,7 +163,7 @@ :or {create-page true with-data true}}] (let [id (or id (uuid/next)) - created-at (or created-at (dt/now)) + created-at (or created-at (ct/now)) modified-at (or modified-at created-at) features (d/nilv features #{}) diff --git a/common/src/app/common/types/tokens_lib.cljc b/common/src/app/common/types/tokens_lib.cljc index 9f2dd2afee..5e6e869eb2 100644 --- a/common/src/app/common/types/tokens_lib.cljc +++ b/common/src/app/common/types/tokens_lib.cljc @@ -13,7 +13,7 @@ [app.common.files.helpers :as cfh] [app.common.schema :as sm] [app.common.schema.generators :as sg] - [app.common.time :as dt] + [app.common.time :as ct] [app.common.transit :as t] [app.common.types.token :as cto] [app.common.uuid :as uuid] @@ -98,7 +98,7 @@ [:type [::sm/one-of cto/token-types]] [:value ::sm/any] [:description {:optional true} :string] - [:modified-at {:optional true} ::sm/inst]]) + [:modified-at {:optional true} ::ct/inst]]) (declare make-token) @@ -118,7 +118,7 @@ [& {:as attrs}] (-> attrs (update :id #(or % (uuid/next))) - (update :modified-at #(or % (dt/now))) + (update :modified-at #(or % (ct/now))) (update :description d/nilv "") (check-token-attrs) (map->Token))) @@ -208,14 +208,14 @@ (TokenSet. id new-name description - (dt/now) + (ct/now) tokens)) (set-description [_ new-description] (TokenSet. id name (d/nilv new-description "") - (dt/now) + (ct/now) tokens)) ITokenSet @@ -231,17 +231,17 @@ (TokenSet. id name description - (dt/now) + (ct/now) (assoc tokens (:name token) token)))) (update-token [this id f] (if-let [token (token-by-id this id)] (let [token' (-> (make-token (f token)) - (assoc :modified-at (dt/now)))] + (assoc :modified-at (ct/now)))] (TokenSet. id name description - (dt/now) + (ct/now) (if (= (:name token) (:name token')) (assoc tokens (:name token') token') (-> tokens @@ -254,7 +254,7 @@ (TokenSet. id name description - (dt/now) + (ct/now) (dissoc tokens (:name token))))) (get-token [this id] @@ -279,7 +279,7 @@ [:id ::sm/uuid] [:name :string] [:description {:optional true} :string] - [:modified-at {:optional true} ::sm/inst] + [:modified-at {:optional true} ::ct/inst] [:tokens {:optional true :gen/gen (->> (sg/map-of (sg/generator ::sm/text) (sg/generator schema:token)) @@ -316,7 +316,7 @@ [& {:as attrs}] (let [attrs (-> attrs (update :id #(or % (uuid/next))) - (update :modified-at #(or % (dt/now))) + (update :modified-at #(or % (ct/now))) (update :tokens #(into (d/ordered-map) %)) (update :description d/nilv "") (check-token-set-attrs))] @@ -552,7 +552,7 @@ description is-source external-id - (dt/now) + (ct/now) set-names)) (enable-set [this set-name] @@ -580,7 +580,7 @@ description is-source external-id - (dt/now) + (ct/now) (conj (disj sets prev-set-name) set-name)) this)) @@ -606,7 +606,7 @@ [:description {:optional true} :string] [:is-source {:optional true} :boolean] [:external-id {:optional true} :string] - [:modified-at {:optional true} ::sm/inst] + [:modified-at {:optional true} ::ct/inst] [:sets {:optional true} [:set {:gen/max 5} :string]]]) (def schema:token-theme @@ -640,7 +640,7 @@ (update :description d/nilv "") (update :is-source d/nilv false) (update :external-id #(or % (str new-id))) - (update :modified-at #(or % (dt/now))) + (update :modified-at #(or % (ct/now))) (update :sets set) (check-token-theme-attrs) (map->TokenTheme)))) @@ -1070,7 +1070,7 @@ Will return a value that matches this schema: (let [theme (dm/get-in themes [group name])] (if theme (let [theme' (-> (make-token-theme (f theme)) - (assoc :modified-at (dt/now))) + (assoc :modified-at (ct/now))) group' (:group theme') name' (:name theme') same-group? (= group group') @@ -1490,7 +1490,7 @@ Will return a value that matches this schema: :is-source (get theme "is-source") :external-id (get theme "id") :modified-at (some-> (get theme "modified-at") - (dt/parse-instant)) + (ct/inst)) :sets (into #{} (comp (map key) xf-normalize-set-name diff --git a/common/src/app/common/types/typography.cljc b/common/src/app/common/types/typography.cljc index 9b2edddd77..0f7052b994 100644 --- a/common/src/app/common/types/typography.cljc +++ b/common/src/app/common/types/typography.cljc @@ -8,6 +8,7 @@ (:require [app.common.data :as d] [app.common.schema :as sm] + [app.common.time :as-alias ct] [app.common.types.plugins :as ctpg] [app.common.types.text :as txt] [app.common.uuid :as uuid])) @@ -31,7 +32,7 @@ [:line-height :string] [:letter-spacing :string] [:text-transform :string] - [:modified-at {:optional true} ::sm/inst] + [:modified-at {:optional true} ::ct/inst] [:path {:optional true} [:maybe :string]] [:plugin-data {:optional true} ::ctpg/plugin-data]])) diff --git a/common/test/common_tests/time_test.cljc b/common/test/common_tests/time_test.cljc index 982ca8a43a..03bb8ef5ea 100644 --- a/common/test/common_tests/time_test.cljc +++ b/common/test/common_tests/time_test.cljc @@ -10,7 +10,7 @@ [clojure.test :as t])) (t/deftest compare-time - (let [dta (dt/parse-instant 10000) - dtb (dt/parse-instant 20000)] + (let [dta (dt/inst 10000) + dtb (dt/inst 20000)] (t/is (false? (dt/is-after? dta dtb))) (t/is (true? (dt/is-before? dta dtb))))) diff --git a/common/test/common_tests/types/tokens_lib_test.cljc b/common/test/common_tests/types/tokens_lib_test.cljc index 7eee6e01f0..430dc7c97e 100644 --- a/common/test/common_tests/types/tokens_lib_test.cljc +++ b/common/test/common_tests/types/tokens_lib_test.cljc @@ -12,7 +12,7 @@ #?(:clj [clojure.datafy :refer [datafy]]) [app.common.data :as d] [app.common.test-helpers.ids-map :as thi] - [app.common.time :as dt] + [app.common.time :as ct] [app.common.transit :as tr] [app.common.types.tokens-lib :as ctob] [app.common.uuid :as uuid] @@ -20,14 +20,14 @@ (defn setup-virtual-time [next] - (let [current (volatile! (inst-ms (dt/now)))] - (with-redefs [dt/now #(dt/parse-instant (vswap! current inc))] + (let [current (volatile! (inst-ms (ct/now)))] + (with-redefs [ct/now #(ct/inst (vswap! current inc))] (next)))) (t/use-fixtures :once setup-virtual-time) (t/deftest make-token - (let [now (dt/now) + (let [now (ct/now) token1 (ctob/make-token :id (thi/new-id! :token1) :name "test-token-1" :type :boolean @@ -71,7 +71,7 @@ (t/is (= #{"foo" "bar"} (ctob/find-token-value-references "{foo}} + {bar}"))))) (t/deftest make-token-set - (let [now (dt/now) + (let [now (ct/now) token-set1 (ctob/make-token-set :name "test-token-set-1") token-set2 (ctob/make-token-set :name "test-token-set-2" :description "test description" @@ -196,7 +196,7 @@ (t/is (= (get-in expected ["baz" "boo" :name]) "baz.boo")))) (t/deftest make-token-theme - (let [now (dt/now) + (let [now (ct/now) token-theme1 (ctob/make-token-theme :name "test-token-theme-1") token-theme2 (ctob/make-token-theme :name "test-token-theme-2" :group "group-1" @@ -266,7 +266,7 @@ (t/is (= (ctob/set-count tokens-lib') 1)) (t/is (= (ctob/get-name token-set') "test-token-set")) (t/is (= (ctob/get-description token-set') "some description")) - (t/is (dt/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set))))) + (t/is (ct/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set))))) (t/deftest rename-token-set (let [tokens-lib (-> (ctob/make-tokens-lib) @@ -282,7 +282,7 @@ (t/is (= (ctob/set-count tokens-lib') 1)) (t/is (= (ctob/get-name token-set') "updated-name")) - (t/is (dt/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set))))) + (t/is (ct/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set))))) (t/deftest rename-token-set-group (let [tokens-lib (-> (ctob/make-tokens-lib) @@ -403,7 +403,7 @@ (t/is (= (ctob/set-count tokens-lib') 1)) (t/is (= (count (ctob/get-tokens-map token-set')) 1)) (t/is (= (:name token') "test-token")) - (t/is (dt/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set))))) + (t/is (ct/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set))))) (t/deftest update-token (let [tokens-lib (-> (ctob/make-tokens-lib) @@ -445,8 +445,8 @@ (t/is (= (:name token') "test-token-1")) (t/is (= (:description token') "some description")) (t/is (= (:value token') false)) - (t/is (dt/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set))) - (t/is (dt/is-after? (:modified-at token') (:modified-at token))))) + (t/is (ct/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set))) + (t/is (ct/is-after? (:modified-at token') (:modified-at token))))) (t/deftest rename-token (let [tokens-lib (-> (ctob/make-tokens-lib) @@ -479,8 +479,8 @@ (t/is (= (:name token') "updated-name")) (t/is (= (:description token') "")) (t/is (= (:value token') true)) - (t/is (dt/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set))) - (t/is (dt/is-after? (:modified-at token') (:modified-at token))))) + (t/is (ct/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set))) + (t/is (ct/is-after? (:modified-at token') (:modified-at token))))) (t/deftest delete-token (let [tokens-lib (-> (ctob/make-tokens-lib) @@ -502,7 +502,7 @@ (t/is (= (ctob/set-count tokens-lib') 1)) (t/is (= (count (ctob/get-tokens-map token-set')) 0)) (t/is (nil? token')) - (t/is (dt/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set))))) + (t/is (ct/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set))))) (t/deftest get-ordered-sets (let [tokens-lib (-> (ctob/make-tokens-lib) @@ -788,7 +788,7 @@ (t/is (= (ctob/theme-count tokens-lib') 2)) (t/is (= (:name token-theme') "test-token-theme")) (t/is (= (:description token-theme') "some description")) - (t/is (dt/is-after? (:modified-at token-theme') (:modified-at token-theme))))) + (t/is (ct/is-after? (:modified-at token-theme') (:modified-at token-theme))))) (t/deftest rename-token-theme (let [tokens-lib (-> (ctob/make-tokens-lib) @@ -805,7 +805,7 @@ (t/is (= (ctob/theme-count tokens-lib') 2)) (t/is (= (:name token-theme') "updated-name")) - (t/is (dt/is-after? (:modified-at token-theme') (:modified-at token-theme))))) + (t/is (ct/is-after? (:modified-at token-theme') (:modified-at token-theme))))) (t/deftest delete-token-theme (let [tokens-lib (-> (ctob/make-tokens-lib) @@ -834,7 +834,7 @@ token-theme (ctob/get-theme tokens-lib "" "test-token-theme") token-theme' (ctob/get-theme tokens-lib' "" "test-token-theme")] - (t/is (dt/is-after? (:modified-at token-theme') (:modified-at token-theme))))) + (t/is (ct/is-after? (:modified-at token-theme') (:modified-at token-theme))))) (t/deftest transit-serialization (let [tokens-lib (-> (ctob/make-tokens-lib) @@ -952,8 +952,8 @@ (t/is (= (:name token') "group1.test-token-2")) (t/is (= (:description token') "some description")) (t/is (= (:value token') false)) - (t/is (dt/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set))) - (t/is (dt/is-after? (:modified-at token') (:modified-at token))))) + (t/is (ct/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set))) + (t/is (ct/is-after? (:modified-at token') (:modified-at token))))) (t/deftest update-token-in-sets-rename (let [tokens-lib (-> (ctob/make-tokens-lib) @@ -989,8 +989,8 @@ (t/is (= (:name token') "group1.updated-name")) (t/is (= (:description token') "")) (t/is (= (:value token') true)) - (t/is (dt/is-after? (ctob/get-modified-at token-set') (:ctob/get-modified-at token-set))) - (t/is (dt/is-after? (:modified-at token') (:modified-at token))))) + (t/is (ct/is-after? (ctob/get-modified-at token-set') (:ctob/get-modified-at token-set))) + (t/is (ct/is-after? (:modified-at token') (:modified-at token))))) (t/deftest move-token-of-group (let [tokens-lib (-> (ctob/make-tokens-lib) @@ -1027,8 +1027,8 @@ (t/is (= (:name token') "group2.updated-name")) (t/is (= (:description token') "")) (t/is (= (:value token') true)) - (t/is (dt/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set))) - (t/is (dt/is-after? (:modified-at token') (:modified-at token))))) + (t/is (ct/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set))) + (t/is (ct/is-after? (:modified-at token') (:modified-at token))))) (t/deftest delete-token-in-group (let [tokens-lib (-> (ctob/make-tokens-lib) @@ -1053,7 +1053,7 @@ (t/is (= (ctob/set-count tokens-lib') 1)) (t/is (= (count (ctob/get-tokens-map token-set')) 1)) (t/is (nil? token')) - (t/is (dt/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set))))) + (t/is (ct/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set))))) (t/deftest update-token-set-in-groups (let [tokens-lib (-> (ctob/make-tokens-lib) @@ -1079,7 +1079,7 @@ (t/is (= (d/index-of (keys group1') "S-token-set-2") 0)) (t/is (= (ctob/get-name token-set') "group1/token-set-2")) (t/is (= (ctob/get-description token-set') "some description")) - (t/is (dt/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set))))) + (t/is (ct/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set))))) (t/deftest rename-token-set-in-groups (let [tokens-lib (-> (ctob/make-tokens-lib) @@ -1105,7 +1105,7 @@ (t/is (= (d/index-of (keys group1') "S-updated-name") 0)) (t/is (= (ctob/get-name token-set') "group1/updated-name")) (t/is (= (ctob/get-description token-set') "")) - (t/is (dt/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set))))) + (t/is (ct/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set))))) (t/deftest move-token-set-of-group (let [tokens-lib (-> (ctob/make-tokens-lib) @@ -1133,7 +1133,7 @@ (t/is (nil? (get group1' "S-updated-name"))) (t/is (= (ctob/get-name token-set') "group2/updated-name")) (t/is (= (ctob/get-description token-set') "")) - (t/is (dt/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set))))) + (t/is (ct/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set))))) (t/deftest delete-token-set-in-group (let [tokens-lib (-> (ctob/make-tokens-lib) @@ -1174,7 +1174,7 @@ (t/is (= (:name token-theme') "token-theme-2")) (t/is (= (:group token-theme') "group1")) (t/is (= (:description token-theme') "some description")) - (t/is (dt/is-after? (:modified-at token-theme') (:modified-at token-theme))))) + (t/is (ct/is-after? (:modified-at token-theme') (:modified-at token-theme))))) (t/deftest get-token-theme-groups (let [token-lib (-> (ctob/make-tokens-lib) @@ -1211,7 +1211,7 @@ (t/is (= (:name token-theme') "updated-name")) (t/is (= (:group token-theme') "group1")) (t/is (= (:description token-theme') "")) - (t/is (dt/is-after? (:modified-at token-theme') (:modified-at token-theme))))) + (t/is (ct/is-after? (:modified-at token-theme') (:modified-at token-theme))))) (t/deftest move-token-theme-of-group (let [tokens-lib (-> (ctob/make-tokens-lib) @@ -1241,7 +1241,7 @@ (t/is (= (:name token-theme') "updated-name")) (t/is (= (:group token-theme') "group2")) (t/is (= (:description token-theme') "")) - (t/is (dt/is-after? (:modified-at token-theme') (:modified-at token-theme))))) + (t/is (ct/is-after? (:modified-at token-theme') (:modified-at token-theme))))) (t/deftest delete-token-theme-in-group (let [tokens-lib (-> (ctob/make-tokens-lib) @@ -1357,7 +1357,7 @@ #?(:clj (t/deftest export-dtcg-json - (let [now (dt/now) + (let [now (ct/now) tokens-lib (-> (ctob/make-tokens-lib) (ctob/add-set (ctob/make-token-set :name "core" :tokens {"colors.red.600" @@ -1408,7 +1408,7 @@ #?(:clj (t/deftest export-parse-dtcg-json - (with-redefs [dt/now (constantly #inst "2024-10-16T12:01:20.257840055-00:00") + (with-redefs [ct/now (constantly #inst "2024-10-16T12:01:20.257840055-00:00") uuid/next (constantly uuid/zero)] (let [tokens-lib (-> (ctob/make-tokens-lib) (ctob/add-set (ctob/make-token-set :name "core" @@ -1477,7 +1477,7 @@ #?(:clj (t/deftest export-dtcg-json-with-active-theme-and-set - (let [now (dt/now) + (let [now (ct/now) tokens-lib (-> (ctob/make-tokens-lib) (ctob/add-set (ctob/make-token-set :name "core" :tokens {"colors.red.600" @@ -1530,7 +1530,7 @@ #?(:clj (t/deftest export-dtcg-multi-file - (let [now (dt/now) + (let [now (ct/now) tokens-lib (-> (ctob/make-tokens-lib) (ctob/add-set (ctob/make-token-set :name "some/set" :tokens {"colors.red.600" diff --git a/common/yarn.lock b/common/yarn.lock index b6310e8f69..d7a4958f77 100644 --- a/common/yarn.lock +++ b/common/yarn.lock @@ -248,7 +248,7 @@ __metadata: resolution: "common@workspace:." dependencies: concurrently: "npm:^9.1.2" - luxon: "npm:^3.6.1" + date-fns: "npm:^4.1.0" nodemon: "npm:^3.1.10" source-map-support: "npm:^0.5.21" ws: "npm:^8.18.2" @@ -291,6 +291,13 @@ __metadata: languageName: node linkType: hard +"date-fns@npm:^4.1.0": + version: 4.1.0 + resolution: "date-fns@npm:4.1.0" + checksum: 10c0/b79ff32830e6b7faa009590af6ae0fb8c3fd9ffad46d930548fbb5acf473773b4712ae887e156ba91a7b3dc30591ce0f517d69fd83bd9c38650fdc03b4e0bac8 + languageName: node + linkType: hard + "debug@npm:4, debug@npm:^4, debug@npm:^4.3.4": version: 4.4.1 resolution: "debug@npm:4.4.1" @@ -620,13 +627,6 @@ __metadata: languageName: node linkType: hard -"luxon@npm:^3.6.1": - version: 3.6.1 - resolution: "luxon@npm:3.6.1" - checksum: 10c0/906d57a9dc4d1de9383f2e9223e378c298607c1b4d17b6657b836a3cd120feb1c1de3b5d06d846a3417e1ca764de8476e8c23b3cd4083b5cdb870adcb06a99d5 - languageName: node - linkType: hard - "make-fetch-happen@npm:^14.0.3": version: 14.0.3 resolution: "make-fetch-happen@npm:14.0.3" diff --git a/frontend/package.json b/frontend/package.json index e78b0932cb..d29fe75d5f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -114,7 +114,6 @@ "js-beautify": "^1.15.4", "lodash": "^4.17.21", "lodash.debounce": "^4.0.8", - "luxon": "^3.6.1", "opentype.js": "^1.3.4", "postcss-modules": "^6.0.1", "randomcolor": "^0.6.2", diff --git a/frontend/src/app/main/data/changes.cljs b/frontend/src/app/main/data/changes.cljs index 077a6418e7..b2b41f7e5c 100644 --- a/frontend/src/app/main/data/changes.cljs +++ b/frontend/src/app/main/data/changes.cljs @@ -10,11 +10,11 @@ [app.common.data.macros :as dm] [app.common.files.changes :as cpc] [app.common.logging :as log] + [app.common.time :as ct] [app.common.types.shape-tree :as ctst] [app.common.uuid :as uuid] [app.main.data.helpers :as dsh] [app.main.worker :as mw] - [app.util.time :as dt] [beicon.v2.core :as rx] [potok.v2.core :as ptk])) @@ -118,7 +118,7 @@ source (d/nilv source :local) local? (= source :local) commit {:id commit-id - :created-at (dt/now) + :created-at (ct/now) :source source :origin (ptk/type origin) :features features diff --git a/frontend/src/app/main/data/comments.cljs b/frontend/src/app/main/data/comments.cljs index e4205f9bd7..57fc450ba0 100644 --- a/frontend/src/app/main/data/comments.cljs +++ b/frontend/src/app/main/data/comments.cljs @@ -10,6 +10,7 @@ [app.common.data.macros :as dm] [app.common.geom.point :as gpt] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.types.shape-tree :as ctst] [app.common.uuid :as uuid] [app.main.data.event :as ev] @@ -35,8 +36,8 @@ [:seqn :int] [:content :string] [:participants ::sm/set-of-uuid] - [:created-at ::sm/inst] - [:modified-at ::sm/inst] + [:created-at ::ct/inst] + [:modified-at ::ct/inst] [:position ::gpt/point] [:count-unread-comments {:optional true} :int] [:count-comments {:optional true} :int]]) @@ -49,8 +50,8 @@ [:owner-id ::sm/uuid] [:owner-fullname {:optional true} ::sm/text] [:owner-email {:optional true} ::sm/email] - [:created-at ::sm/inst] - [:modified-at ::sm/inst] + [:created-at ::ct/inst] + [:modified-at ::ct/inst] [:content :string]]) (def check-comment-thread! diff --git a/frontend/src/app/main/data/dashboard.cljs b/frontend/src/app/main/data/dashboard.cljs index 4aedd2ad77..23625cb41d 100644 --- a/frontend/src/app/main/data/dashboard.cljs +++ b/frontend/src/app/main/data/dashboard.cljs @@ -12,6 +12,7 @@ [app.common.files.helpers :as cfh] [app.common.logging :as log] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.uuid :as uuid] [app.main.data.common :as dcm] [app.main.data.event :as ev] @@ -22,7 +23,6 @@ [app.main.repo :as rp] [app.util.i18n :as i18n :refer [tr]] [app.util.sse :as sse] - [app.util.time :as dt] [beicon.v2.core :as rx] [clojure.set :as set] [cuerdas.core :as str] @@ -549,7 +549,7 @@ update-project (fn [project delta op] (-> project (update :count #(op % (count ids))) - (assoc :modified-at (dt/plus (dt/now) {:milliseconds delta}))))] + (assoc :modified-at (ct/in-future {:milliseconds delta}))))] (-> state (d/update-in-when [:projects origin-project] update-project 0 -) (d/update-in-when [:projects project-id] update-project 10 +)))) diff --git a/frontend/src/app/main/data/event.cljs b/frontend/src/app/main/data/event.cljs index 934a0f4757..21033b9cd9 100644 --- a/frontend/src/app/main/data/event.cljs +++ b/frontend/src/app/main/data/event.cljs @@ -10,6 +10,7 @@ [app.common.data :as d] [app.common.json :as json] [app.common.logging :as l] + [app.common.time :as ct] [app.config :as cf] [app.main.repo :as rp] [app.util.globals :as g] @@ -17,7 +18,6 @@ [app.util.i18n :as i18n] [app.util.object :as obj] [app.util.storage :as storage] - [app.util.time :as dt] [beicon.v2.core :as rx] [beicon.v2.operators :as rxo] [lambdaisland.uri :as u] @@ -33,7 +33,7 @@ ;; Defines the time window within events belong to the same session. (def session-timeout - (dt/duration {:minutes 30})) + (ct/duration {:minutes 30})) ;; --- CONTEXT @@ -218,7 +218,7 @@ (update :profile-id #(or % profile-id)))))) (rx/filter :profile-id) (rx/map (fn [event] - (let [session* (or @session (dt/now)) + (let [session* (or @session (ct/now)) context (-> @context (merge (:context event)) (assoc :session session*) @@ -226,7 +226,7 @@ (d/without-nils))] (reset! session session*) (-> event - (assoc :timestamp (dt/now)) + (assoc :timestamp (ct/now)) (assoc :context context))))) (rx/tap (fn [event] diff --git a/frontend/src/app/main/data/exports/assets.cljs b/frontend/src/app/main/data/exports/assets.cljs index b1f0293993..dcd6cc1f6f 100644 --- a/frontend/src/app/main/data/exports/assets.cljs +++ b/frontend/src/app/main/data/exports/assets.cljs @@ -6,6 +6,7 @@ (ns app.main.data.exports.assets (:require + [app.common.time :as ct] [app.common.uuid :as uuid] [app.main.data.event :as ev] [app.main.data.helpers :as dsh] @@ -15,7 +16,6 @@ [app.main.repo :as rp] [app.main.store :as st] [app.util.dom :as dom] - [app.util.time :as dt] [app.util.websocket :as ws] [beicon.v2.core :as rx] [potok.v2.core :as ptk])) @@ -123,7 +123,7 @@ :widget-visible true :detail-visible true :exports exports - :last-update (dt/now) + :last-update (ct/now) :cmd cmd})))) (defn- update-export-status @@ -131,18 +131,17 @@ (ptk/reify ::update-export-status ptk/UpdateEvent (update [_ state] - (let [time-diff (dt/diff (dt/now) - (get-in state [:export :last-update])) - healthy? (< time-diff (dt/duration {:seconds 6}))] + (let [time-diff (ct/diff-ms (get-in state [:export :last-update]) (ct/now)) + healthy? (< time-diff 6000)] (cond-> state (= status "running") - (update :export assoc :progress done :last-update (dt/now) :healthy? healthy?) + (update :export assoc :progress done :last-update (ct/now) :healthy? healthy?) (= status "error") - (update :export assoc :in-progress false :error (:cause data) :last-update (dt/now) :healthy? healthy?) + (update :export assoc :in-progress false :error (:cause data) :last-update (ct/now) :healthy? healthy?) (= status "ended") - (update :export assoc :in-progress false :last-update (dt/now) :healthy? healthy?)))) + (update :export assoc :in-progress false :last-update (ct/now) :healthy? healthy?)))) ptk/WatchEvent (watch [_ _ _] diff --git a/frontend/src/app/main/data/plugins.cljs b/frontend/src/app/main/data/plugins.cljs index f45bb6cb6c..b9969ead6e 100644 --- a/frontend/src/app/main/data/plugins.cljs +++ b/frontend/src/app/main/data/plugins.cljs @@ -8,6 +8,7 @@ (:require [app.common.data.macros :as dm] [app.common.files.changes-builder :as pcb] + [app.common.time :as ct] [app.main.data.changes :as dch] [app.main.data.event :as ev] [app.main.data.modal :as modal] @@ -17,7 +18,6 @@ [app.util.globals :as ug] [app.util.http :as http] [app.util.i18n :as i18n :refer [tr]] - [app.util.time :as dt] [beicon.v2.core :as rx] [potok.v2.core :as ptk])) @@ -173,8 +173,8 @@ (ptk/reify ::update-plugins-permissions-peek ptk/UpdateEvent (update [_ state] - (let [now (dt/now) - expiration (dt/minus now (dt/duration {:days 1})) + (let [now (ct/now) + expiration (ct/in-past {:days 1}) updated-at (dm/get-in state [:plugins-permissions-peek :updated-at] 0) expired? (> expiration updated-at)] diff --git a/frontend/src/app/main/data/style_dictionary.cljs b/frontend/src/app/main/data/style_dictionary.cljs index 56e4406653..9363419c30 100644 --- a/frontend/src/app/main/data/style_dictionary.cljs +++ b/frontend/src/app/main/data/style_dictionary.cljs @@ -11,11 +11,11 @@ [app.common.files.tokens :as cft] [app.common.logging :as l] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.types.tokens-lib :as ctob] [app.main.data.tinycolor :as tinycolor] [app.main.data.workspace.tokens.errors :as wte] [app.main.data.workspace.tokens.warnings :as wtw] - [app.util.time :as dt] [beicon.v2.core :as rx] [cuerdas.core :as str] [promesa.core :as p] @@ -347,7 +347,7 @@ (let [state* (mf/use-state tokens)] (mf/with-effect [tokens interactive?] (if (seq tokens) - (let [tpoint (dt/tpoint-ms) + (let [tpoint (ct/tpoint-ms) tokens-s (if interactive? (resolve-tokens-interactive tokens) (resolve-tokens tokens))] diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index ee71a3e73b..b67756b0ce 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -17,6 +17,7 @@ [app.common.logic.libraries :as cll] [app.common.logic.shapes :as cls] [app.common.logic.variants :as clv] + [app.common.time :as ct] [app.common.types.color :as ctc] [app.common.types.component :as ctk] [app.common.types.components-list :as ctkl] @@ -52,7 +53,6 @@ [app.main.store :as st] [app.util.color :as uc] [app.util.i18n :refer [tr]] - [app.util.time :as dt] [beicon.v2.core :as rx] [cuerdas.core :as str] [potok.v2.core :as ptk])) @@ -1054,7 +1054,7 @@ (update [_ state] (if (and (not= library-id (:current-file-id state)) (nil? asset-id)) - (d/assoc-in-when state [:files library-id :synced-at] (dt/now)) + (d/assoc-in-when state [:files library-id :synced-at] (ct/now)) state)) ptk/WatchEvent @@ -1139,14 +1139,14 @@ ptk/UpdateEvent (update [_ state] (let [file-id (:current-file-id state)] - (assoc-in state [:files file-id :ignore-sync-until] (dt/now)))) + (assoc-in state [:files file-id :ignore-sync-until] (ct/now)))) ptk/WatchEvent (watch [_ state _] (let [file-id (:current-file-id state)] (->> (rp/cmd! :ignore-file-library-sync-status {:file-id file-id - :date (dt/now)}) + :date (ct/now)}) (rx/ignore)))))) (defn assets-need-sync @@ -1216,7 +1216,7 @@ (let [data (dsh/lookup-file-data state) changes (-> (pcb/empty-changes it) (pcb/with-library-data data) - (pcb/update-component id #(assoc % :modified-at (dt/now))))] + (pcb/update-component id #(assoc % :modified-at (ct/now))))] (rx/of (dch/commit-changes {:origin it :redo-changes (:redo-changes changes) :undo-changes [] diff --git a/frontend/src/app/main/data/workspace/notifications.cljs b/frontend/src/app/main/data/workspace/notifications.cljs index dc46e6e355..ba94f46604 100644 --- a/frontend/src/app/main/data/workspace/notifications.cljs +++ b/frontend/src/app/main/data/workspace/notifications.cljs @@ -10,6 +10,7 @@ [app.common.data.macros :as dm] [app.common.files.changes :as cpc] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.uuid :as uuid] [app.main.data.changes :as dch] [app.main.data.common :as dc] @@ -27,7 +28,6 @@ [app.util.mouse :as mse] [app.util.object :as obj] [app.util.rxops :as rxs] - [app.util.time :as dt] [beicon.v2.core :as rx] [clojure.set :as set] [potok.v2.core :as ptk])) @@ -194,7 +194,7 @@ (-> session (assoc :id session-id) (assoc :profile-id profile-id) - (assoc :updated-at (dt/now)) + (assoc :updated-at (ct/now)) (assoc :version version) (update :color update-color presence) (assoc :text-color "#000000"))) @@ -224,7 +224,7 @@ :vbox vbox :vport vport :point position - :updated-at (dt/now) + :updated-at (ct/now) :page-id page-id)))))) (def ^:private @@ -301,7 +301,7 @@ [:file-id ::sm/uuid] [:session-id ::sm/uuid] [:revn :int] - [:modified-at ::sm/inst] + [:modified-at ::ct/inst] [:changes ::cpc/changes]]) (def ^:private check-library-change-params! diff --git a/frontend/src/app/main/data/workspace/shape_layout.cljs b/frontend/src/app/main/data/workspace/shape_layout.cljs index ac7217c0b7..2279000ae0 100644 --- a/frontend/src/app/main/data/workspace/shape_layout.cljs +++ b/frontend/src/app/main/data/workspace/shape_layout.cljs @@ -342,32 +342,32 @@ (ptk/reify ::remove-layout-track ptk/WatchEvent (watch [_ state _] - (let [undo-id (js/Symbol)] - (let [objects (dsh/lookup-page-objects state) + (let [objects (dsh/lookup-page-objects state) + undo-id (js/Symbol) - shapes-to-delete - (when with-shapes? - (->> ids - (mapcat - (fn [id] - (let [shape (get objects id)] - (if (= type :column) - (ctl/shapes-by-column shape index) - (ctl/shapes-by-row shape index))))) - (into #{})))] - (rx/of (dwu/start-undo-transaction undo-id) - (if shapes-to-delete - (dwsh/delete-shapes shapes-to-delete) - (rx/empty)) - (dwsh/update-shapes - ids - (fn [shape objects] - (case type - :row (ctl/remove-grid-row shape index objects) - :column (ctl/remove-grid-column shape index objects))) - {:with-objects? true}) - (ptk/data-event :layout/update {:ids ids}) - (dwu/commit-undo-transaction undo-id))))))) + shapes-to-delete + (when with-shapes? + (->> ids + (mapcat + (fn [id] + (let [shape (get objects id)] + (if (= type :column) + (ctl/shapes-by-column shape index) + (ctl/shapes-by-row shape index))))) + (into #{})))] + (rx/of (dwu/start-undo-transaction undo-id) + (if shapes-to-delete + (dwsh/delete-shapes shapes-to-delete) + (rx/empty)) + (dwsh/update-shapes + ids + (fn [shape objects] + (case type + :row (ctl/remove-grid-row shape index objects) + :column (ctl/remove-grid-column shape index objects))) + {:with-objects? true}) + (ptk/data-event :layout/update {:ids ids}) + (dwu/commit-undo-transaction undo-id)))))) (defn duplicate-layout-track [ids type index] diff --git a/frontend/src/app/main/data/workspace/texts.cljs b/frontend/src/app/main/data/workspace/texts.cljs index e8ed5288cb..eb6a3bf496 100644 --- a/frontend/src/app/main/data/workspace/texts.cljs +++ b/frontend/src/app/main/data/workspace/texts.cljs @@ -669,9 +669,9 @@ (ptk/reify ::commit-update-text-modifier ptk/WatchEvent (watch [_ state _] - (let [ids (::update-text-modifier-debounce-ids state)] - (let [modif-tree (dwm/create-modif-tree ids (ctm/reflow-modifiers))] - (rx/of (dwm/update-modifiers modif-tree false true))))))) + (let [ids (::update-text-modifier-debounce-ids state) + modif-tree (dwm/create-modif-tree ids (ctm/reflow-modifiers))] + (rx/of (dwm/update-modifiers modif-tree false true)))))) (defn update-text-modifier [id props] @@ -965,17 +965,15 @@ modifiers (get-in state [:workspace-text-modifier id]) new-shape? (nil? (:content shape))] (rx/of - (dwsh/update-shapes - [id] - (fn [shape] - (let [{:keys [width height position-data]} modifiers] - (let [new-shape (-> shape - (assoc :content content) - (cond-> position-data - (assoc :position-data position-data)) - (cond-> (and update-name? (some? name)) - (assoc :name name)) - (cond-> (or (some? width) (some? height)) - (gsh/transform-shape (ctm/change-size shape width height))))] - new-shape))) - {:undo-group (when new-shape? id)}))))))) + (dwsh/update-shapes [id] + (fn [shape] + (let [{:keys [width height position-data]} modifiers] + (-> shape + (assoc :content content) + (cond-> position-data + (assoc :position-data position-data)) + (cond-> (and update-name? (some? name)) + (assoc :name name)) + (cond-> (or (some? width) (some? height)) + (gsh/transform-shape (ctm/change-size shape width height)))))) + {:undo-group (when new-shape? id)}))))))) diff --git a/frontend/src/app/main/data/workspace/thumbnails.cljs b/frontend/src/app/main/data/workspace/thumbnails.cljs index 11d1503bdc..3e330baa0a 100644 --- a/frontend/src/app/main/data/workspace/thumbnails.cljs +++ b/frontend/src/app/main/data/workspace/thumbnails.cljs @@ -10,6 +10,7 @@ [app.common.files.helpers :as cfh] [app.common.logging :as l] [app.common.thumbnails :as thc] + [app.common.time :as ct] [app.common.types.component :as ctc] [app.common.uuid :as uuid] [app.main.data.changes :as dch] @@ -21,7 +22,6 @@ [app.main.render :as render] [app.main.repo :as rp] [app.util.queue :as q] - [app.util.time :as tp] [app.util.timers :as tm] [app.util.webapi :as wapi] [beicon.v2.core :as rx] @@ -65,7 +65,7 @@ "Returns the thumbnail for the given ids" [state file-id page-id frame-id tag] (let [object-id (thc/fmt-object-id file-id page-id frame-id tag) - tp (tp/tpoint-ms) + tp (ct/tpoint-ms) objects (-> (dsh/lookup-file-data state file-id) (dsh/get-page page-id) :objects) @@ -156,7 +156,7 @@ ptk/WatchEvent (watch [_ state stream] (l/dbg :hint "update thumbnail" :requester requester :object-id object-id :tag tag) - (let [tp (tp/tpoint-ms)] + (let [tp (ct/tpoint-ms)] ;; Send the update to the back-end (->> (request-thumbnail state file-id page-id frame-id tag) (rx/mapcat (fn [blob] diff --git a/frontend/src/app/main/data/workspace/tokens/propagation.cljs b/frontend/src/app/main/data/workspace/tokens/propagation.cljs index fa9174d171..d892404de2 100644 --- a/frontend/src/app/main/data/workspace/tokens/propagation.cljs +++ b/frontend/src/app/main/data/workspace/tokens/propagation.cljs @@ -8,6 +8,7 @@ (:require [app.common.files.helpers :as cfh] [app.common.logging :as l] + [app.common.time :as ct] [app.common.types.token :as ctt] [app.common.types.tokens-lib :as ctob] [app.main.data.helpers :as dsh] @@ -16,7 +17,6 @@ [app.main.data.workspace.thumbnails :as dwt] [app.main.data.workspace.tokens.application :as dwta] [app.main.data.workspace.undo :as dwu] - [app.util.time :as dt] [beicon.v2.core :as rx] [clojure.data :as data] [clojure.set :as set] @@ -143,7 +143,7 @@ (let [file-id (get state :current-file-id) current-page-id (get state :current-page-id) fdata (dsh/lookup-file-data state file-id) - tpoint (dt/tpoint-ms)] + tpoint (ct/tpoint-ms)] (l/inf :status "START" :hint "propagate-tokens") (->> (rx/concat diff --git a/frontend/src/app/main/data/workspace/undo.cljs b/frontend/src/app/main/data/workspace/undo.cljs index 492c9ee131..724ae94309 100644 --- a/frontend/src/app/main/data/workspace/undo.cljs +++ b/frontend/src/app/main/data/workspace/undo.cljs @@ -40,11 +40,11 @@ [app.common.files.changes :as cpc] [app.common.logging :as log] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.types.shape.layout :as ctl] [app.main.data.changes :as dch] [app.main.data.common :as dcm] [app.main.data.helpers :as dsh] - [app.util.time :as dt] [beicon.v2.core :as rx] [potok.v2.core :as ptk])) @@ -169,7 +169,7 @@ (nil? current-tx) (assoc-in [:workspace-undo :transaction] empty-tx) (nil? pending-tx) (assoc-in [:workspace-undo :transactions-pending] #{id}) (some? pending-tx) (update-in [:workspace-undo :transactions-pending] conj id) - :always (update-in [:workspace-undo :transactions-pending-ts] assoc id (dt/now))))) + :always (update-in [:workspace-undo :transactions-pending-ts] assoc id (ct/now))))) ptk/WatchEvent (watch [_ _ _] @@ -216,7 +216,7 @@ (watch [_ state _] (log/info :hint "check-open-transactions" :timeout timeout) (let [pending-ts (-> (dm/get-in state [:workspace-undo :transactions-pending-ts]) - (update-vals #(inst-ms (dt/diff (dt/now) %))))] + (update-vals #(ct/diff-ms % (ct/now))))] (->> pending-ts (filter (fn [[_ ts]] (>= ts timeout))) (rx/from) diff --git a/frontend/src/app/main/data/workspace/versions.cljs b/frontend/src/app/main/data/workspace/versions.cljs index f2ae3bb3a7..c3373bcba9 100644 --- a/frontend/src/app/main/data/workspace/versions.cljs +++ b/frontend/src/app/main/data/workspace/versions.cljs @@ -9,6 +9,7 @@ [app.common.data :as d] [app.common.data.macros :as dm] [app.common.schema :as sm] + [app.common.time :as ct] [app.main.data.event :as ev] [app.main.data.helpers :as dsh] [app.main.data.persistence :as dwp] @@ -16,7 +17,6 @@ [app.main.data.workspace.thumbnails :as th] [app.main.refs :as refs] [app.main.repo :as rp] - [app.util.time :as dt] [beicon.v2.core :as rx] [potok.v2.core :as ptk])) @@ -59,7 +59,7 @@ (ptk/reify ::create-version ptk/WatchEvent (watch [_ state _] - (let [label (dt/format (dt/now) :date-full) + (let [label (ct/format-inst (ct/now) :localized-date) file-id (:current-file-id state)] ;; Force persist before creating snapshot, otherwise we could loss changes @@ -140,7 +140,7 @@ (let [version (->> (dm/get-in state [:workspace-versions :data]) (d/seek #(= (:id %) id))) params {:id id - :label (dt/format (:created-at version) :date-full)}] + :label (ct/format-inst (:created-at version) :localized-date)}] (->> (rp/cmd! :update-file-snapshot params) (rx/mapcat (fn [_] diff --git a/frontend/src/app/main/ui/comments.cljs b/frontend/src/app/main/ui/comments.cljs index 479c810cd4..8b3f34013a 100644 --- a/frontend/src/app/main/ui/comments.cljs +++ b/frontend/src/app/main/ui/comments.cljs @@ -12,6 +12,7 @@ [app.common.files.helpers :as cfh] [app.common.geom.point :as gpt] [app.common.math :as mth] + [app.common.time :as ct] [app.common.uuid :as uuid] [app.config :as cfg] [app.main.data.comments :as dcm] @@ -31,7 +32,6 @@ [app.util.i18n :as i18n :refer [tr]] [app.util.keyboard :as kbd] [app.util.object :as obj] - [app.util.time :as dt] [app.util.webapi :as wapi] [beicon.v2.core :as rx] [clojure.math :refer [floor]] @@ -622,7 +622,8 @@ :else "read")}] [:div {:class (stl/css :author-identity)} [:div {:class (stl/css :author-fullname)} (:fullname profile)] - [:div {:class (stl/css :author-timeago)} (dt/timeago (:modified-at item))]]] + [:div {:class (stl/css :author-timeago)} + (ct/timeago (:modified-at item))]]] [:div {:class (stl/css :item)} [:> comment-content* {:content (:content item)}]] @@ -631,7 +632,9 @@ (let [total-comments (:count-comments item) unread-comments (:count-unread-comments item) total-replies (dec total-comments) - unread-replies (if (= unread-comments total-comments) (dec unread-comments) unread-comments)] + unread-replies (if (= unread-comments total-comments) + (dec unread-comments) + unread-comments)] [:* (when (> total-replies 0) (if (= total-replies 1) @@ -977,7 +980,8 @@ [:> comment-avatar* {:image (cfg/resolve-profile-photo-url owner)}] [:div {:class (stl/css :author-identity)} [:div {:class (stl/css :author-fullname)} (:fullname owner)] - [:div {:class (stl/css :author-timeago)} (dt/timeago (:modified-at comment))]] + [:div {:class (stl/css :author-timeago)} + (ct/timeago (:modified-at comment))]] (when (= (:id profile) (:id owner)) [:> icon-button* {:variant "ghost" diff --git a/frontend/src/app/main/ui/dashboard/grid.cljs b/frontend/src/app/main/ui/dashboard/grid.cljs index b59807c62d..fd1f92fdf0 100644 --- a/frontend/src/app/main/ui/dashboard/grid.cljs +++ b/frontend/src/app/main/ui/dashboard/grid.cljs @@ -11,6 +11,7 @@ [app.common.data.macros :as dm] [app.common.geom.point :as gpt] [app.common.logging :as log] + [app.common.time :as ct] [app.config :as cf] [app.main.data.common :as dcm] [app.main.data.dashboard :as dd] @@ -38,7 +39,6 @@ [app.util.dom.dnd :as dnd] [app.util.i18n :as i18n :refer [tr]] [app.util.keyboard :as kbd] - [app.util.time :as dt] [app.util.timers :as ts] [beicon.v2.core :as rx] [cuerdas.core :as str] @@ -229,9 +229,7 @@ (mf/defc grid-item-metadata [{:keys [modified-at]}] - - (let [locale (mf/deref i18n/locale) - time (dt/timeago modified-at {:locale locale})] + (let [time (ct/timeago modified-at)] [:span {:class (stl/css :date)} time])) (defn create-counter-element diff --git a/frontend/src/app/main/ui/dashboard/projects.cljs b/frontend/src/app/main/ui/dashboard/projects.cljs index 5f2c830fa3..538bf9c472 100644 --- a/frontend/src/app/main/ui/dashboard/projects.cljs +++ b/frontend/src/app/main/ui/dashboard/projects.cljs @@ -8,6 +8,7 @@ (:require-macros [app.main.style :as stl]) (:require [app.common.geom.point :as gpt] + [app.common.time :as ct] [app.main.data.common :as dcm] [app.main.data.dashboard :as dd] [app.main.data.event :as ev] @@ -26,7 +27,6 @@ [app.util.i18n :as i18n :refer [tr]] [app.util.keyboard :as kbd] [app.util.storage :as storage] - [app.util.time :as dt] [cuerdas.core :as str] [okulary.core :as l] [potok.v2.core :as ptk] @@ -103,9 +103,7 @@ {::mf/props :obj ::mf/private true} [{:keys [project is-first team files can-edit]}] - (let [locale (mf/deref i18n/locale) - - project-id (get project :id) + (let [project-id (get project :id) team-id (get team :id) file-count (or (:count project) 0) @@ -240,7 +238,7 @@ [:span {:class (stl/css :info)} (str (tr "labels.num-of-files" (i18n/c file-count)))] (let [time (-> (:modified-at project) - (dt/timeago {:locale locale}))] + (ct/timeago))] [:span {:class (stl/css :recent-files-row-title-info)} (str ", " time)])] [:div {:class (stl/css-case :project-actions true diff --git a/frontend/src/app/main/ui/ds/product/user_milestone.cljs b/frontend/src/app/main/ui/ds/product/user_milestone.cljs index df8e28fa1b..3ce0a73ad3 100644 --- a/frontend/src/app/main/ui/ds/product/user_milestone.cljs +++ b/frontend/src/app/main/ui/ds/product/user_milestone.cljs @@ -8,7 +8,7 @@ (:require-macros [app.main.style :as stl]) (:require - [app.common.data :as d] + [app.common.time :as ct] [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] [app.main.ui.ds.controls.input :refer [input*]] [app.main.ui.ds.foundations.typography :as t] @@ -17,7 +17,6 @@ [app.main.ui.ds.utilities.date :refer [valid-date?]] [app.util.i18n :as i18n :refer [tr]] [app.util.object :as obj] - [app.util.time :as dt] [rumext.v2 :as mf])) (def ^:private schema:milestone @@ -41,15 +40,20 @@ {::mf/schema schema:milestone} [{:keys [class active editing user label date onOpenMenu onFocusInput onBlurInput onKeyDownInput] :rest props}] - (let [class (d/append-class class (stl/css-case :milestone true :is-selected active)) - props (mf/spread-props props {:class class :data-testid "milestone"}) - date (cond-> date (not (dt/datetime? date)) dt/datetime) - time (dt/timeago date)] - [:> "div" props + (let [class' (stl/css-case :milestone true + :is-selected active) + props (mf/spread-props props {:class [class class'] + :data-testid "milestone"}) + date (if (ct/inst? date) + date + (ct/inst date))] + + [:> :div props [:> avatar* {:name (obj/get user "name") :url (obj/get user "avatar") :color (obj/get user "color") - :variant "S" :class (stl/css :avatar)}] + :variant "S" + :class (stl/css :avatar)}] (if editing [:> input* @@ -60,11 +64,15 @@ :on-focus onFocusInput :on-blur onBlurInput :on-key-down onKeyDownInput}] - [:> text* {:as "span" :typography t/body-small :class (stl/css :name)} label]) + [:> text* {:as "span" + :typography t/body-small + :class (stl/css :name)} + label]) [:* - [:time {:dateTime (dt/format date :iso) - :class (stl/css :date)} time] + [:time {:date-time (ct/format-inst date :iso) + :class (stl/css :date)} + (ct/timeago date)] [:div {:class (stl/css :milestone-buttons)} [:> icon-button* {:class (stl/css :menu-button) diff --git a/frontend/src/app/main/ui/ds/utilities/date.cljs b/frontend/src/app/main/ui/ds/utilities/date.cljs index 716d465e42..f24cd11ac6 100644 --- a/frontend/src/app/main/ui/ds/utilities/date.cljs +++ b/frontend/src/app/main/ui/ds/utilities/date.cljs @@ -10,14 +10,14 @@ [app.main.style :as stl]) (:require [app.common.data :as d] + [app.common.time :as ct] [app.main.ui.ds.foundations.typography :as t] [app.main.ui.ds.foundations.typography.text :refer [text*]] - [app.util.time :as dt] [rumext.v2 :as mf])) (defn valid-date? [date] - (or (dt/datetime? date) (number? date))) + (or (ct/inst? date) (number? date))) (def ^:private schema:date [:map @@ -31,11 +31,14 @@ {::mf/schema schema:date} [{:keys [class date selected typography] :rest props}] (let [class (d/append-class class (stl/css-case :date true :is-selected selected)) - date (cond-> date (not (dt/datetime? date)) dt/datetime) + date (cond-> date (not (ct/inst? date)) ct/inst) typography (or typography t/body-medium)] - [:> text* {:as "time" :typography typography :class class :dateTime (dt/format date :iso)} + [:> text* {:as "time" + :typography typography + :class class + :date-time (ct/format-inst date :iso)} (dm/str - (dt/format date :date-full) + (ct/format-inst date :localized-date) " . " - (dt/format date :time-24-simple) + (ct/format-inst date :localized-time) "h")])) diff --git a/frontend/src/app/main/ui/settings/access_tokens.cljs b/frontend/src/app/main/ui/settings/access_tokens.cljs index 6e8eb42529..07de421de2 100644 --- a/frontend/src/app/main/ui/settings/access_tokens.cljs +++ b/frontend/src/app/main/ui/settings/access_tokens.cljs @@ -8,6 +8,7 @@ (:require-macros [app.main.style :as stl]) (:require [app.common.schema :as sm] + [app.common.time :as ct] [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] [app.main.data.profile :as du] @@ -18,7 +19,6 @@ [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [app.util.keyboard :as kbd] - [app.util.time :as dt] [app.util.webapi :as wapi] [okulary.core :as l] [rumext.v2 :as mf])) @@ -56,7 +56,6 @@ created (mf/deref token-created-ref) created? (mf/use-state false) - locale (mf/deref i18n/locale) on-success (mf/use-fn @@ -140,7 +139,7 @@ (when @created? [:span {:class (stl/css :token-created-info)} (if (:expires-at created) - (tr "dashboard.access-tokens.token-will-expire" (dt/format-date-locale (:expires-at created) {:locale locale})) + (tr "dashboard.access-tokens.token-will-expire" (ct/format-inst (:expires-at created) "PPP")) (tr "dashboard.access-tokens.token-will-not-expire"))])] [:div {:class (stl/css :fields-row)} @@ -235,10 +234,9 @@ (mf/defc access-token-item {::mf/wrap [mf/memo]} [{:keys [token] :as props}] - (let [locale (mf/deref i18n/locale) - expires-at (:expires-at token) - expires-txt (some-> expires-at (dt/format-date-locale {:locale locale})) - expired? (and (some? expires-at) (> (dt/now) expires-at)) + (let [expires-at (:expires-at token) + expires-txt (some-> expires-at (ct/format-inst "PPP")) + expired? (and (some? expires-at) (> (ct/now) expires-at)) delete-fn (mf/use-fn diff --git a/frontend/src/app/main/ui/settings/subscription.cljs b/frontend/src/app/main/ui/settings/subscription.cljs index e693da8f33..982b88c214 100644 --- a/frontend/src/app/main/ui/settings/subscription.cljs +++ b/frontend/src/app/main/ui/settings/subscription.cljs @@ -3,6 +3,7 @@ (:require [app.common.data.macros :as dm] [app.common.schema :as sm] + [app.common.time :as ct] [app.main.data.auth :as da] [app.main.data.event :as ev] [app.main.data.modal :as modal] @@ -15,7 +16,6 @@ [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] - [app.util.time :as dt] [beicon.v2.core :as rx] [potok.v2.core :as ptk] [rumext.v2 :as mf])) @@ -223,8 +223,6 @@ teams* (mf/use-state nil) teams (deref teams*) - locale (mf/deref i18n/locale) - params-subscription (-> route :params :query :subscription) @@ -249,10 +247,10 @@ (= (:status subscription) "trialing") member-since - (dt/format-date-locale-short (:created-at profile) {:locale locale}) + (ct/format-inst (:created-at profile) "d MMMM, yyyy") subscribed-since - (dt/format-date-locale-short (:start-date subscription) {:locale locale}) + (ct/format-inst (:start-date profile) "d MMMM, yyyy") go-to-pricing-page (mf/use-fn diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs index 9da2318017..3901b56866 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs @@ -48,10 +48,9 @@ (mf/use-fn (mf/deps ids change-radius) (fn [value] - (let [] - (st/emit! - (change-radius (fn [shape] - (ctsr/set-radius-to-all-corners shape value))))))) + (st/emit! + (change-radius (fn [shape] + (ctsr/set-radius-to-all-corners shape value)))))) on-radius-4-change (mf/use-fn diff --git a/frontend/src/app/main/ui/workspace/sidebar/versions.cljs b/frontend/src/app/main/ui/workspace/sidebar/versions.cljs index edd74d3a63..0cf83cbda0 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/versions.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/versions.cljs @@ -26,7 +26,6 @@ [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [app.util.keyboard :as kbd] - [app.util.time :as dt] [cuerdas.core :as str] [lambdaisland.uri :as u] [okulary.core :as l] @@ -67,10 +66,10 @@ (map #(assoc % :type :version))) (->> data (filterv #(= "system" (:created-by %))) - (group-by #(.toISODate ^js (:created-at %))) + (group-by #(ct/format-inst (:created-at %) :iso-date)) (map (fn [[day entries]] {:type :snapshot - :created-at (ct/parse-instant day) + :created-at (ct/inst day) :snapshots entries})))) (sort-by :created-at) (reverse))) @@ -204,7 +203,7 @@ [:li {:ref entry-ref :class (stl/css :version-entry-wrap)} [:> autosaved-milestone* {:label (tr "workspace.versions.autosaved.version" - (dt/format (:created-at entry) :date-full)) + (ct/format-inst (:created-at entry) :localized-date)) :autosavedMessage (tr "workspace.versions.autosaved.entry" (count (:snapshots entry))) :snapshots (mapv :created-at (:snapshots entry)) :versionToggled is-expanded diff --git a/frontend/src/app/main/ui/workspace/viewport/presence.cljs b/frontend/src/app/main/ui/workspace/viewport/presence.cljs index 68e508ee6b..0d359cd01b 100644 --- a/frontend/src/app/main/ui/workspace/viewport/presence.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/presence.cljs @@ -8,8 +8,8 @@ (:require-macros [app.main.style :as stl]) (:require [app.common.data.macros :as dm] + [app.common.time :as ct] [app.main.refs :as refs] - [app.util.time :as dt] [app.util.timers :as ts] [beicon.v2.core :as rx] [cuerdas.core :as str] @@ -63,7 +63,7 @@ sessions (->> (vals sessions) (filter :point) (filter #(= page-id (:page-id %))) - (filter #(>= 5000 (- (inst-ms (dt/now)) + (filter #(>= 5000 (- (inst-ms (ct/now)) (inst-ms (:updated-at %))))))] (mf/with-effect nil (let [sem (ts/schedule 1000 #(swap! counter inc))] diff --git a/frontend/src/app/plugins/file.cljs b/frontend/src/app/plugins/file.cljs index 1044bf3f86..54fcb2f8c3 100644 --- a/frontend/src/app/plugins/file.cljs +++ b/frontend/src/app/plugins/file.cljs @@ -8,6 +8,7 @@ (:require [app.common.data :as d] [app.common.data.macros :as dm] + [app.common.time :as ct] [app.common.uuid :as uuid] [app.config :as cf] [app.main.data.exports.files :as exports.files] @@ -25,7 +26,6 @@ [app.plugins.utils :as u] [app.util.http :as http] [app.util.object :as obj] - [app.util.time :as dt] [beicon.v2.core :as rx])) (defn file-version-proxy? @@ -107,7 +107,7 @@ :else (let [params {:id (:id @data) - :label (dt/format (:created-at @data) :date-full)}] + :label (ct/format-inst (:created-at @data) :localized-date)}] (->> (rx/zip (rp/cmd! :get-team-users {:file-id file-id}) (rp/cmd! :update-file-snapshot params)) (rx/subs! (fn [[users data]] diff --git a/frontend/src/app/util/cache.cljs b/frontend/src/app/util/cache.cljs index 61cd08adcc..885a04e3f7 100644 --- a/frontend/src/app/util/cache.cljs +++ b/frontend/src/app/util/cache.cljs @@ -6,7 +6,7 @@ (ns app.util.cache (:require - [app.util.time :as dt] + [app.common.time :as ct] [beicon.v2.core :as rx])) (defonce cache (atom {})) @@ -16,10 +16,8 @@ [{:keys [key max-age]} observable] (let [entry (get @cache key) pending-entry (get @pending key) - age (when entry - (dt/diff (dt/now) - (:created-at entry)))] + (ct/diff-ms (:created-at entry) (ct/now)))] (cond (and (some? entry) (< age max-age)) (rx/of (:data entry)) @@ -36,7 +34,7 @@ observable (fn [data] - (let [entry {:created-at (dt/now) :data data}] + (let [entry {:created-at (ct/now) :data data}] (swap! cache assoc key entry)) (swap! pending dissoc key) (rx/push! subject data) diff --git a/frontend/src/app/util/http.cljs b/frontend/src/app/util/http.cljs index 98177688cd..cbf9ec1c7c 100644 --- a/frontend/src/app/util/http.cljs +++ b/frontend/src/app/util/http.cljs @@ -8,12 +8,12 @@ "A http client with rx streams interface." (:require [app.common.data :as d] + [app.common.time :as ct] [app.common.transit :as t] [app.common.uri :as u] [app.config :as cfg] [app.util.cache :as c] [app.util.globals :as globals] - [app.util.time :as dt] [app.util.webapi :as wapi] [beicon.v2.core :as rx] [cuerdas.core :as str] @@ -198,7 +198,7 @@ (rx/map :body) (rx/mapcat wapi/read-file-as-data-url) (rx/map #(hash-map uri %)) - (c/with-cache {:key uri :max-age (dt/duration {:hours 4})}))] + (c/with-cache {:key uri :max-age (* 1000 60 60 4)}))] ;; We need to check `throw-err?` after the cache is resolved otherwise we cannot cache request ;; with different values of throw-err. By default we throw always the exception and then we just @@ -215,4 +215,4 @@ :uri url :response-type :text}) (rx/map :body) - (c/with-cache {:key url :max-age (dt/duration {:hours 4})}))) + (c/with-cache {:key url :max-age (ct/duration {:hours 4})}))) diff --git a/frontend/src/app/util/i18n.cljs b/frontend/src/app/util/i18n.cljs index 66f262f0e9..0bd5f39f7d 100644 --- a/frontend/src/app/util/i18n.cljs +++ b/frontend/src/app/util/i18n.cljs @@ -9,6 +9,7 @@ (:require [app.common.data :as d] [app.common.logging :as log] + [app.common.time :as ct] [app.config :as cfg] [app.util.globals :as globals] [app.util.storage :as storage] @@ -77,8 +78,12 @@ cfg/default-language)))) (defonce translations #js {}) -(defonce locale (l/atom (or (get storage/global ::locale) - (autodetect)))) +(defonce locale (l/atom nil)) + +(add-watch locale "common.time" + (fn [_ _ pv cv] + (when (not= pv cv) + (ct/set-default-locale! cv)))) (defn init! "Initialize the i18n module with translations. @@ -87,6 +92,7 @@ is executed in the critical part (application bootstrap) and used in many parts of the application." [data] + (reset! locale (or (get storage/global ::locale) (autodetect))) (set! translations data)) (defn set-locale! @@ -96,7 +102,7 @@ (let [lname (autodetect)] (swap! storage/global dissoc ::locale) (reset! locale lname)) - (let [supported (into #{} (map :value supported-locales)) + (let [supported (into #{} (map :value) supported-locales) lname (loop [locales (seq (parse-locale lname))] (if-let [locale (first locales)] (if (contains? supported locale) diff --git a/frontend/src/app/util/queue.cljs b/frontend/src/app/util/queue.cljs index 4fbc72b302..8fbecb9a57 100644 --- a/frontend/src/app/util/queue.cljs +++ b/frontend/src/app/util/queue.cljs @@ -9,8 +9,8 @@ (:require [app.common.logging :as l] [app.common.math :as mth] + [app.common.time :as ct] [app.util.object :as obj] - [app.util.time :as t] [beicon.v2.core :as rx])) (l/set-level! :info) @@ -65,7 +65,7 @@ item (.shift ^js items)] (when (some? item) - (let [tp (t/tpoint-ms) + (let [tp (ct/tpoint-ms) f (unchecked-get item "f") res (unchecked-get item "result")] (rx/subscribe (f) diff --git a/frontend/src/app/util/storage.cljs b/frontend/src/app/util/storage.cljs index 3cc29e39b9..b67c805d25 100644 --- a/frontend/src/app/util/storage.cljs +++ b/frontend/src/app/util/storage.cljs @@ -7,10 +7,10 @@ (ns app.util.storage (:require [app.common.exceptions :as ex] + [app.common.time :as ct] [app.common.transit :as t] [app.util.functions :as fns] [app.util.globals :as g] - [app.util.time :as dt] [cuerdas.core :as str] [okulary.util :as ou])) @@ -162,7 +162,7 @@ (defonce before-unload (letfn [(on-before-unload [_] (binding [*sync* true] - (swap! global assoc ::last-refresh (dt/now)) - (swap! user assoc ::last-refresh (dt/now))))] + (swap! global assoc ::last-refresh (ct/now)) + (swap! user assoc ::last-refresh (ct/now))))] (.addEventListener g/window "beforeunload" on-before-unload) on-before-unload)) diff --git a/frontend/src/app/util/time.cljs b/frontend/src/app/util/time.cljs deleted file mode 100644 index 2498d50469..0000000000 --- a/frontend/src/app/util/time.cljs +++ /dev/null @@ -1,307 +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.util.time - (:require - ["date-fns/format$default" :as dfn-format] - ["date-fns/formatDistanceToNowStrict$default" :as dfn-distance-to-now] - ["date-fns/locale/ar-SA$default" :as dfn-ar] - ["date-fns/locale/ca$default" :as dfn-ca] - ["date-fns/locale/cs$default" :as dfn-cs] - ["date-fns/locale/de$default" :as dfn-de] - ["date-fns/locale/el$default" :as dfn-el] - ["date-fns/locale/en-US$default" :as df-en-us] - ["date-fns/locale/es$default" :as dfn-es] - ["date-fns/locale/eu$default" :as dfn-eu] - ["date-fns/locale/fa-IR$default" :as dfn-fa-ir] - ["date-fns/locale/fr$default" :as dfn-fr] - ["date-fns/locale/gl$default" :as dfn-gl] - ["date-fns/locale/he$default" :as dfn-he] - ["date-fns/locale/hr$default" :as dfn-hr] - ["date-fns/locale/id$default" :as dfn-id] - ["date-fns/locale/it$default" :as dfn-it] - ["date-fns/locale/ja$default" :as dfn-ja] - ["date-fns/locale/ko$default" :as dfn-ko] - ["date-fns/locale/lv$default" :as dfn-lv] - ["date-fns/locale/nb$default" :as dfn-nb] - ["date-fns/locale/nl$default" :as dfn-nl] - ["date-fns/locale/pl$default" :as dfn-pl] - ["date-fns/locale/pt$default" :as dfn-pt] - ["date-fns/locale/pt-BR$default" :as dfn-pt-br] - ["date-fns/locale/ro$default" :as dfn-ro] - ["date-fns/locale/ru$default" :as dfn-ru] - ["date-fns/locale/tr$default" :as dfn-tr] - ["date-fns/locale/uk$default" :as dfn-uk] - ["date-fns/locale/zh-CN$default" :as dfn-zh-cn] - [app.common.data.macros :as dm] - [app.common.time :as common-time] - [app.util.object :as obj] - [cuerdas.core :as str])) - -(dm/export common-time/DateTime) -(dm/export common-time/Duration) - -(def locales - #js {:ar dfn-ar - :ca dfn-ca - :de dfn-de - :el dfn-el - :en df-en-us - :en_us df-en-us - :es dfn-es - :es_es dfn-es - :fa dfn-fa-ir - :fa_ir dfn-fa-ir - :fr dfn-fr - :he dfn-he - :pt dfn-pt - :pt_pt dfn-pt - :pt_br dfn-pt-br - :ro dfn-ro - :ru dfn-ru - :tr dfn-tr - :zh-cn dfn-zh-cn - :nl dfn-nl - :eu dfn-eu - :gl dfn-gl - :hr dfn-hr - :it dfn-it - :nb dfn-nb - :nb_no dfn-nb - :pl dfn-pl - :id dfn-id - :uk dfn-uk - :cs dfn-cs - :lv dfn-lv - :ko dfn-ko - :ja dfn-ja - :ja_jp dfn-ja}) - -(defprotocol ITimeMath - (plus [_ o]) - (minus [_ o])) - -(defprotocol ITimeFormat - (format [_ fmt])) - -(defn duration? - [o] - (instance? Duration o)) - -(defn datetime? - [o] - (instance? DateTime o)) - -(defn duration - [o] - (cond - (number? o) (.fromMillis Duration o) - (duration? o) o - (string? o) (.fromISO Duration o) - (map? o) (.fromObject Duration (clj->js o)) - :else (throw (js/Error. "unexpected arguments")))) - -(defn datetime - ([s] (datetime s nil)) - ([s {:keys [zone force-zone] :or {zone "local" force-zone false}}] - (cond - (integer? s) - (.fromMillis ^js DateTime s #js {:zone zone :setZone force-zone}) - - (map? s) - (.fromObject ^js DateTime (-> (clj->js s) - (obj/set! "zone" zone) - (obj/set! "setZone" force-zone))) - - :else - (throw (js/Error. "invalid arguments"))))) - -(defn epoch->datetime - ([seconds] (epoch->datetime seconds nil)) - ([seconds {:keys [zone force-zone] :or {zone "local" force-zone false}}] - (.fromSeconds ^js DateTime seconds #js {:zone zone :setZone force-zone}))) - -(defn iso->datetime - "A faster option for transit date parsing." - [s] - (.fromISO ^js DateTime s #js {:zone "local"})) - -(defn parse-datetime - ([s] (parse-datetime s :iso nil)) - ([s fmt] (parse-datetime s fmt nil)) - ([s fmt {:keys [zone force-zone] :or {zone "local" force-zone false}}] - (if (string? fmt) - (.fromFormat ^js DateTime s fmt #js {:zone zone :setZone force-zone}) - (case fmt - :iso (.fromISO ^js DateTime s #js {:zone zone :setZone force-zone}) - :rfc2822 (.fromRFC2822 ^js DateTime s #js {:zone zone :setZone force-zone}) - :http (.fromHTTP ^js DateTime s #js {:zone zone :setZone force-zone}))))) - -(dm/export common-time/now) - -(defn utc-now - [] - (.utc ^js DateTime)) - -(defn ->utc - [dt] - (.toUTC ^js dt)) - -(defn diff - [dt1 dt2] - (.diff ^js dt1 dt2)) - -(extend-protocol IEquiv - DateTime - (-equiv [it other] - (if other - (.equals it other) - false)) - - Duration - (-equiv [it other] - (if other - (.equals it other) - false))) - -(extend-protocol Inst - DateTime - (inst-ms* [inst] (.toMillis ^js inst)) - - Duration - (inst-ms* [inst] (.toMillis ^js inst))) - -(extend-protocol IComparable - DateTime - (-compare [it other] - (if ^boolean (.equals it other) - 0 - (if (< (inst-ms it) (inst-ms other)) -1 1))) - - Duration - (-compare [it other] - (if ^boolean (.equals it other) - 0 - (if (< (inst-ms it) (inst-ms other)) -1 1)))) - -(extend-protocol ITimeMath - DateTime - (plus [it o] - (if (map? o) - (.plus ^js it (clj->js o)) - (.plus ^js it o))) - - (minus [it o] - (if (map? o) - (.minus ^js it (clj->js o)) - (.minus ^js it o))) - - Duration - (plus [it o] - (if (map? o) - (.plus ^js it (clj->js o)) - (.plus ^js it o))) - - (minus [it o] - (if (map? o) - (.minus ^js it (clj->js o)) - (.minus ^js it o)))) - -(extend-protocol IPrintWithWriter - DateTime - (-pr-writer [p writer _] - (-write writer (str/fmt "#app/instant \"%s\"" (format p :iso)))) - - Duration - (-pr-writer [p writer _] - (-write writer (str/fmt "#app/duration \"%s\"" (format p :iso))))) - -(defn- resolve-format - [v] - (case v - :time-24-simple (.-TIME_24_SIMPLE ^js DateTime) - :datetime-short (.-DATETIME_SHORT ^js DateTime) - :datetime-med (.-DATETIME_MED ^js DateTime) - :datetime-full (.-DATETIME_FULL ^js DateTime) - :date-full (.-DATE_FULL ^js DateTime) - :date-med-with-weekday (.-DATE_MED_WITH_WEEKDAY ^js DateTime) - v)) - -(defn- format-datetime - [dt fmt] - (case fmt - :iso (.toISO ^js dt) - :rfc2822 (.toRFC2822 ^js dt) - :http (.toHTTP ^js dt) - :json (.toJSON ^js dt) - :date (.toJSDate ^js dt) - :epoch (js/Math.floor (.toSeconds ^js dt)) - :millis (.toMillis ^js dt) - (let [f (resolve-format fmt)] - (if (string? f) - (.toFormat ^js dt f) - (.toLocaleString ^js dt f))))) - -(extend-protocol ITimeFormat - DateTime - (format [it fmt] - (format-datetime it fmt)) - - Duration - (format [it fmt] - (case fmt - :iso (.toISO it) - :json (.toJSON it) - (.toFormat ^js it fmt)))) - -(defn timeago - ([v] (timeago v nil)) - ([v {:keys [locale] :or {locale "en"}}] - (when v - (let [v (if (datetime? v) (format v :date) v)] - (->> #js {:includeSeconds true - :addSuffix true - :locale (obj/get locales locale)} - (dfn-distance-to-now v)))))) - -(defn format-date-locale - ([v] (format-date-locale v nil)) - ([v {:keys [locale] :or {locale "en"}}] - (when v - (let [v (if (datetime? v) (format v :date) v) - locale (obj/get locales locale) - f (.date (.-formatLong ^js locale) v)] - (->> #js {:locale locale} - (dfn-format v f)))))) - -(defn format-date-locale-short - ([v] (format-date-locale-short v nil)) - ([v {:keys [locale] :or {locale "en"}}] - (when v - (let [v (if (datetime? v) (format v :date) v) - locale-obj (obj/get locales locale) - format-str (case locale - ("es" "es_es" "es-ES") "d MMMM, yyyy" - "MMMM do, yyyy")] - (dfn-format (js/Date. v) format-str #js {:locale locale-obj}))))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Measurement Helpers -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defn tpoint - "Create a measurement checkpoint for time measurement of potentially - asynchronous flow." - [] - (let [p1 (.now js/performance)] - #(duration (- (.now js/performance) p1)))) - -(defn tpoint-ms - "Create a measurement checkpoint for time measurement of potentially - asynchronous flow." - [] - (let [p1 (.now js/performance)] - #(- (.now js/performance) p1))) diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 82b74d3b73..cf273005f2 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -5532,7 +5532,6 @@ __metadata: jsdom: "npm:^26.1.0" lodash: "npm:^4.17.21" lodash.debounce: "npm:^4.0.8" - luxon: "npm:^3.6.1" map-stream: "npm:0.0.7" marked: "npm:^15.0.12" mkdirp: "npm:^3.0.1" @@ -7740,13 +7739,6 @@ __metadata: languageName: node linkType: hard -"luxon@npm:^3.6.1": - version: 3.6.1 - resolution: "luxon@npm:3.6.1" - checksum: 10c0/906d57a9dc4d1de9383f2e9223e378c298607c1b4d17b6657b836a3cd120feb1c1de3b5d06d846a3417e1ca764de8476e8c23b3cd4083b5cdb870adcb06a99d5 - languageName: node - linkType: hard - "lz-string@npm:^1.5.0": version: 1.5.0 resolution: "lz-string@npm:1.5.0" diff --git a/library/package.json b/library/package.json index fa14bf7d46..622e739252 100644 --- a/library/package.json +++ b/library/package.json @@ -35,14 +35,14 @@ "watch:test": "node --test --watch", "watch": "yarn run clear:shadow-cache && clojure -M:dev:shadow-cljs watch library" }, + "dependencies": { + "@zip.js/zip.js": "patch:@zip.js/zip.js@npm%3A2.7.60#~/.yarn/patches/@zip.js-zip.js-npm-2.7.60-b6b814410b.patch", + "date-fns": "^4.1.0" + }, "devDependencies": { "@types/node": "^22.12.0", - "@zip.js/zip.js": "patch:@zip.js/zip.js@npm%3A2.7.60#~/.yarn/patches/@zip.js-zip.js-npm-2.7.60-b6b814410b.patch", "concurrently": "^9.1.2", - "luxon": "^3.6.1", - "nodemon": "^3.1.9" - }, - "dependencies": { + "nodemon": "^3.1.9", "source-map-support": "^0.5.21" } } diff --git a/library/yarn.lock b/library/yarn.lock index 1a2ddfc338..6a1bde8ed5 100644 --- a/library/yarn.lock +++ b/library/yarn.lock @@ -57,9 +57,8 @@ __metadata: "@types/node": "npm:^22.12.0" "@zip.js/zip.js": "patch:@zip.js/zip.js@npm%3A2.7.60#~/.yarn/patches/@zip.js-zip.js-npm-2.7.60-b6b814410b.patch" concurrently: "npm:^9.1.2" - luxon: "npm:^3.6.1" + date-fns: "npm:^4.1.0" nodemon: "npm:^3.1.9" - shadow-cljs: "npm:3.1.4" source-map-support: "npm:^0.5.21" languageName: unknown linkType: soft @@ -155,13 +154,6 @@ __metadata: languageName: node linkType: hard -"base64-js@npm:^1.3.1": - version: 1.5.1 - resolution: "base64-js@npm:1.5.1" - checksum: 10c0/f23823513b63173a001030fae4f2dabe283b99a9d324ade3ad3d148e218134676f1ee8568c877cd79ec1c53158dcf2d2ba527a97c606618928ba99dd930102bf - languageName: node - linkType: hard - "binary-extensions@npm:^2.0.0": version: 2.3.0 resolution: "binary-extensions@npm:2.3.0" @@ -204,16 +196,6 @@ __metadata: languageName: node linkType: hard -"buffer@npm:^6.0.3": - version: 6.0.3 - resolution: "buffer@npm:6.0.3" - dependencies: - base64-js: "npm:^1.3.1" - ieee754: "npm:^1.2.1" - checksum: 10c0/2a905fbbcde73cc5d8bd18d1caa23715d5f83a5935867c2329f0ac06104204ba7947be098fe1317fbd8830e26090ff8e764f08cd14fefc977bb248c3487bcbd0 - languageName: node - linkType: hard - "cacache@npm:^19.0.1": version: 19.0.1 resolution: "cacache@npm:19.0.1" @@ -333,6 +315,13 @@ __metadata: languageName: node linkType: hard +"date-fns@npm:^4.1.0": + version: 4.1.0 + resolution: "date-fns@npm:4.1.0" + checksum: 10c0/b79ff32830e6b7faa009590af6ae0fb8c3fd9ffad46d930548fbb5acf473773b4712ae887e156ba91a7b3dc30591ce0f517d69fd83bd9c38650fdc03b4e0bac8 + languageName: node + linkType: hard + "debug@npm:4, debug@npm:^4, debug@npm:^4.3.4": version: 4.4.1 resolution: "debug@npm:4.4.1" @@ -551,13 +540,6 @@ __metadata: languageName: node linkType: hard -"ieee754@npm:^1.2.1": - version: 1.2.1 - resolution: "ieee754@npm:1.2.1" - checksum: 10c0/b0782ef5e0935b9f12883a2e2aa37baa75da6e66ce6515c168697b42160807d9330de9a32ec1ed73149aea02e0d822e572bca6f1e22bdcbd2149e13b050b17bb - languageName: node - linkType: hard - "ignore-by-default@npm:^1.0.1": version: 1.0.1 resolution: "ignore-by-default@npm:1.0.1" @@ -669,13 +651,6 @@ __metadata: languageName: node linkType: hard -"luxon@npm:^3.6.1": - version: 3.6.1 - resolution: "luxon@npm:3.6.1" - checksum: 10c0/906d57a9dc4d1de9383f2e9223e378c298607c1b4d17b6657b836a3cd120feb1c1de3b5d06d846a3417e1ca764de8476e8c23b3cd4083b5cdb870adcb06a99d5 - languageName: node - linkType: hard - "make-fetch-happen@npm:^14.0.3": version: 14.0.3 resolution: "make-fetch-happen@npm:14.0.3" @@ -922,13 +897,6 @@ __metadata: languageName: node linkType: hard -"process@npm:^0.11.10": - version: 0.11.10 - resolution: "process@npm:0.11.10" - checksum: 10c0/40c3ce4b7e6d4b8c3355479df77aeed46f81b279818ccdc500124e6a5ab882c0cc81ff7ea16384873a95a74c4570b01b120f287abbdd4c877931460eca6084b3 - languageName: node - linkType: hard - "promise-retry@npm:^2.0.1": version: 2.0.1 resolution: "promise-retry@npm:2.0.1" @@ -955,13 +923,6 @@ __metadata: languageName: node linkType: hard -"readline-sync@npm:^1.4.10": - version: 1.4.10 - resolution: "readline-sync@npm:1.4.10" - checksum: 10c0/0a4d0fe4ad501f8f005a3c9cbf3cc0ae6ca2ced93e9a1c7c46f226bdfcb6ef5d3f437ae7e9d2e1098ee13524a3739c830e4c8dbc7f543a693eecd293e41093a3 - languageName: node - linkType: hard - "require-directory@npm:^2.1.1": version: 2.1.1 resolution: "require-directory@npm:2.1.1" @@ -1001,30 +962,6 @@ __metadata: languageName: node linkType: hard -"shadow-cljs-jar@npm:1.3.4": - version: 1.3.4 - resolution: "shadow-cljs-jar@npm:1.3.4" - checksum: 10c0/c5548bb5f2bda5e0a90df6f42e4ec3a07ed4c72cdebb87619e8d9a2167bb3d4b60d6f6a305a3e15cbfb379d5fdbe2a989a0e7059b667cfb3911bc198a4489e94 - languageName: node - linkType: hard - -"shadow-cljs@npm:3.1.4": - version: 3.1.4 - resolution: "shadow-cljs@npm:3.1.4" - dependencies: - buffer: "npm:^6.0.3" - process: "npm:^0.11.10" - readline-sync: "npm:^1.4.10" - shadow-cljs-jar: "npm:1.3.4" - source-map-support: "npm:^0.5.21" - which: "npm:^5.0.0" - ws: "npm:^8.18.1" - bin: - shadow-cljs: cli/runner.js - checksum: 10c0/f80e088d73b645411171d58d7b7dc757bc6c4bbd7032292ddf6866f366ef2cd847c9518c18ecc393106b8906ff0570047ff0d5df5c513938e1b95a59f536eea4 - languageName: node - linkType: hard - "shebang-command@npm:^2.0.0": version: 2.0.0 resolution: "shebang-command@npm:2.0.0" @@ -1326,21 +1263,6 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.18.1": - version: 8.18.2 - resolution: "ws@npm:8.18.2" - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ">=5.0.2" - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - checksum: 10c0/4b50f67931b8c6943c893f59c524f0e4905bbd183016cfb0f2b8653aa7f28dad4e456b9d99d285bbb67cca4fedd9ce90dfdfaa82b898a11414ebd66ee99141e4 - languageName: node - linkType: hard - "y18n@npm:^5.0.5": version: 5.0.8 resolution: "y18n@npm:5.0.8"