This commit is contained in:
Andrey Antukh 2026-05-09 10:47:55 +02:00
parent a04a6dd34c
commit 32627efb96
9 changed files with 328 additions and 340 deletions

View File

@ -804,12 +804,12 @@
props (audit/profile->props profile)
context (d/without-nils {:external-session-id (:external-session-id info)})]
(audit/submit! cfg {::audit/type "action"
::audit/name "login-with-oidc"
::audit/profile-id (:id profile)
::audit/ip-addr (inet/parse-request request)
::audit/props props
::audit/context context})
(audit/submit cfg {:type "action"
:name "login-with-oidc"
:profile-id (:id profile)
:ip-addr (inet/parse-request request)
:props props
:context context})
(->> (redirect-to-verify-token token)
(sxf request)))))

View File

@ -34,6 +34,31 @@
;; HELPERS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def ^:private safe-backend-context-keys
#{:version
:initiator
:client-version
:client-user-agent})
(def ^:privarte safe-frontend-context-keys
#{:version
:locale
:browser
:browser-version
:engine
:engine-version
:os
:os-version
:device-type
:device-arch
:screen-width
:screen-height
:screen-color-depth
:screen-orientation
:event-origin
:event-namespace
:event-symbol})
(def profile-props
[:id
:is-active
@ -122,15 +147,15 @@
(def ^:private schema:event
[:map {:title "AuditEvent"}
[::type ::sm/text]
[::name ::sm/text]
[::profile-id ::sm/uuid]
[::ip-addr {:optional true} ::sm/text]
[::props {:optional true} [:map-of :keyword :any]]
[::context {:optional true} [:map-of :keyword :any]]
[::tracked-at {:optional true} ::ct/inst]
[::created-at {:optional true} ::ct/inst]
[::source {:optional true} ::sm/text]
[:type ::sm/text]
[:name ::sm/text]
[:profile-id ::sm/uuid]
[:props [:map-of :keyword :any]]
[:context [:map-of :keyword :any]]
[:tracked-at ::ct/inst]
[:created-at ::ct/inst]
[:source ::sm/text]
[:ip-addr {:optional true} ::sm/text]
[::webhooks/event? {:optional true} ::sm/boolean]
[::webhooks/batch-timeout {:optional true} ::ct/duration]
[::webhooks/batch-key {:optional true}
@ -142,55 +167,6 @@
(def valid-event?
(sm/validator schema:event))
(defn prepare-event
[cfg mdata params result]
(let [resultm (meta result)
request (-> params meta ::http/request)
profile-id (or (::profile-id resultm)
(:profile-id result)
(::rpc/profile-id params)
uuid/zero)
props (-> (or (::replace-props resultm)
(merge params (::props resultm)))
(clean-props))
context (merge (::context resultm)
(prepare-context-from-request request))
ip-addr (inet/parse-request request)
module (get cfg ::rpc/module)]
{::type (or (::type resultm)
(::rpc/type cfg))
::name (or (::name resultm)
(let [sname (::sv/name mdata)]
(if (not= module "main")
(str module "-" sname)
sname)))
::profile-id profile-id
::ip-addr ip-addr
::props props
::context context
;; NOTE: for batch-key lookup we need the params as-is
;; because the rpc api does not need to know the
;; audit/webhook specific object layout.
::rpc/params params
::webhooks/batch-key
(or (::webhooks/batch-key mdata)
(::webhooks/batch-key resultm))
::webhooks/batch-timeout
(or (::webhooks/batch-timeout mdata)
(::webhooks/batch-timeout resultm))
::webhooks/event?
(or (::webhooks/event? mdata)
(::webhooks/event? resultm)
false)}))
(defn- prepare-context-from-request
"Prepare backend event context from request"
[request]
@ -211,33 +187,27 @@
:client-version client-version
:version (:full cf/version)})))
(defn event-from-rpc-params
"Create a base event skeleton with pre-filled some important
data that can be extracted from RPC params object"
[params]
(let [context (some-> params meta ::http/request prepare-context-from-request)
event {::type "action"
::profile-id (or (::rpc/profile-id params) uuid/zero)
::ip-addr (::rpc/ip-addr params)}]
(cond-> event
(some? context)
(assoc ::context context))))
(def ^:private event-keys
#{:id
:name
:type
:profile-id
:ip-addr
:context
:props
:context
:source
:tracked-at
:created-at})
(defn- event->params
[event]
(let [params {:id (uuid/next)
:name (::name event)
:type (::type event)
:profile-id (::profile-id event)
:ip-addr (::ip-addr event)
:context (::context event {})
:props (::props event {})
:source "backend"}
tnow (::tracked-at event)]
(cond-> params
(some? tnow)
(assoc :tracked-at tnow))))
(defn- append-audit-entry
[cfg params]
(let [params (-> params
(update :props db/tjson)
(update :context db/tjson)
(update :ip-addr db/inet))
params (select-keys params event-keys)]
(db/insert! cfg :audit-log params)))
(def ^:private xf:filter-telemetry-props
"Transducer that keeps only map entries whose values are UUIDs."
@ -245,68 +215,54 @@
(and (simple-keyword? k)
(or (uuid? v) (boolean? v) (number? v))))))
(defn filter-telemetry-props
"Return only UUID-valued keys from a props map. This preserves
object relations while maintaining anonymity."
[props]
(into {} xf:filter-telemetry-props props))
(defn- append-audit-entry
[cfg params]
(let [params (-> params
(update :props db/tjson)
(update :context db/tjson)
(update :ip-addr db/inet))]
(db/insert! cfg :audit-log params)))
(defn- get-telemetry-context
"Get a telemetry ready safe context"
[{:keys [context]}]
(select-keys context [:initiator
:version
:client-version
:client-user-agent]))
(defn- get-telemetry-props
"A telemetry specific safe props"
[{:keys [name props] :as params}]
(defn- filter-telemetry-props
[{:keys [source name props] :as params}]
(cond
(or (= name "login-with-oidc")
(= name "login-with-password")
(= name "register-profile")
(= name "update-profile"))
(-> (select-keys props profile-props)
(filter-telemetry-props)
(assoc :lang (:lang props))
(assoc :auth-backend (:auth-backend props))
(assoc :email-domain (email/get-domain (:email props)))
(d/without-nils))
(and (= source "backend")
(or (= name "login-with-oidc")
(= name "login-with-password")
(= name "register-profile")
(= name "update-profile")))
(let [props (select-keys props profile-props)
props (into {} xf:filter-telemetry-props props)
props (-> props
(assoc :lang (:lang props))
(assoc :auth-backend (:auth-backend props))
(assoc :email-domain (email/get-domain (:email props)))
(d/without-nils))]
(assoc params :props props))
:else
(filter-telemetry-props props)))
(let [props (into {} xf:filter-telemetry-props props)]
(assoc params :props props))))
(defn- handle-event!
(defn filter-telemetry-context
[{:keys [source context] :as params}]
(let [context (case source
"backend" (select-keys context safe-backend-context-keys)
"frontend" (select-keys context safe-frontend-context-keys)
{})]
(assoc params :context context)))
(defn- process-event
[cfg event]
(let [tnow (ct/now)
params (-> (event->params event)
(assoc :created-at tnow)
(update :tracked-at #(or % tnow)))]
(let [params (normalize-event event)]
(when (contains? cf/flags :audit-log-logger)
(l/log! ::l/logger "app.audit"
::l/level :info
:profile-id (str (::profile-id event))
:ip-addr (str (::ip-addr event))
:type (::type event)
:name (::name event)
:props (json/encode (::props event) :key-fn json/write-camel-key)
:context (json/encode (::context event) :key-fn json/write-camel-key)))
:profile-id (str (:profile-id event))
:ip-addr (str (:ip-addr event))
:type (:type event)
:name (:name event)
:props (json/encode (:props event) :key-fn json/write-camel-key)
:context (json/encode (:context event) :key-fn json/write-camel-key)))
(if (contains? cf/flags :audit-log)
;; NOTE: this operation may cause primary key conflicts on inserts
;; because of the timestamp precission (two concurrent requests), in
;; this case we just retry the operation.
(append-audit-entry cfg params)
(append-audit-entry cfg event)
(when cf/telemetry-enabled?
;; NOTE: this operation may cause primary key conflicts on inserts
;; because of the timestamp precission (two concurrent requests), in
@ -320,15 +276,14 @@
;; kept: initiator, version, client-version and client-user-agent.
;; Timestamps are truncated to day precision to avoid leaking exact
;; event timing.
(let [event-name (get params :name)
(let [event-name (get event :name)
tday (ct/truncate tnow :days)
params (-> params
(assoc :source "telemetry:backend")
(assoc :props (get-telemetry-props params))
(assoc :context (get-telemetry-context params))
(assoc :ip-addr (db/inet "0.0.0.0"))
(assoc :created-at tday)
(assoc :tracked-at tday))]
event (-> event
(filter-telemetry-props)
(filter-telemetry-context)
(update :created-at ct/truncate :days)
(update :tracked-at ct/truncate :days)
(assoc :ip-addr "0.0.0.0"))]
(append-audit-entry cfg params))))
(when (and (contains? cf/flags :webhooks)
@ -356,31 +311,117 @@
(dissoc :type)))))))
params))
(defn submit!
"Submit audit event to the collector."
(defn- or-ts-now
[o]
(if (inst? o) o (ct/now)))
(defn submit*
"A public API, lower-leve lhan submit, assumes all required fields are filled"
[cfg event]
(try
(let [event (-> (d/without-nils event)
(check-event))
cfg (-> cfg
(assoc ::rtry/when rtry/conflict-exception?)
(assoc ::rtry/max-retries 6)
(assoc ::rtry/label "persist-audit-log"))]
(rtry/invoke! cfg db/tx-run! handle-event! event))
(let [event (check-event event)
cfg (-> cfg
(assoc ::rtry/when rtry/conflict-exception?)
(assoc ::rtry/max-retries 6)
(assoc ::rtry/label "persist-audit-log"))]
(rtry/invoke! cfg db/tx-run! process-event event))
(catch Throwable cause
(l/error :hint "unexpected error processing event" :cause cause))))
(defn insert!
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; PUBLIC API
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn prepare-rpc-event
[cfg mdata params result]
(let [resultm (meta result)
request (-> params meta ::http/request)
profile-id (or (::profile-id resultm)
(:profile-id result)
(::rpc/profile-id params)
uuid/zero)
props (-> (or (::replace-props resultm)
(merge params (::props resultm)))
(clean-props))
context (merge (::context resultm)
(prepare-context-from-request request))
ip-addr (inet/parse-request request)
module (get cfg ::rpc/module)]
{:type (or (::type resultm)
(::rpc/type cfg))
:name (or (::name resultm)
(let [sname (::sv/name mdata)]
(if (not= module "main")
(str module "-" sname)
sname)))
:profile-id profile-id
:ip-addr ip-addr
:props props
:context context
:created-at (::rpc/request-at params)
:tracked-at (::rpc/request-at params)
;; NOTE: for batch-key lookup we need the params as-is
;; because the rpc api does not need to know the
;; audit/webhook specific object layout.
::rpc/params params
::webhooks/batch-key
(or (::webhooks/batch-key mdata)
(::webhooks/batch-key resultm))
::webhooks/batch-timeout
(or (::webhooks/batch-timeout mdata)
(::webhooks/batch-timeout resultm))
::webhooks/event?
(or (::webhooks/event? mdata)
(::webhooks/event? resultm)
false)}))
(defn event-from-rpc-params
"Create a base event skeleton with pre-filled some important
data that can be extracted from RPC params object"
[params]
(let [context (some-> params meta ::http/request prepare-context-from-request)
event {:type "action"
:profile-id (::rpc/profile-id params)
:created-at (::rpc/request-at params)
:tracked-at (::rpc/request-at params)
:ip-addr (::rpc/ip-addr params)}]
(cond-> event
(some? context)
(assoc :context context))))
(defn submit
"Submit an event to be registered under audit-log subsystem"
[cfg event]
(let [tnow (ct/now)
event (-> event
(assoc :created-at tnow)
(update :tracked-at or tnow)
(update :ip-addr or "0.0.0.0")
(assoc :source "backend")
(d/without-nils))]
(submit* cfg event)))
(defn insert
"Submit audit event to the collector, intended to be used only from
command line helpers because this skips all webhooks and telemetry
logic."
[cfg event]
(when (contains? cf/flags :audit-log)
(let [event (-> (d/without-nils event)
(let [tnow (ct/now)
event (-> event
(assoc :created-at tnow)
(update :tracked-at or tnow)
(update :profile-id or uuid/zero)
(assoc :source "backend")
(select-keys event-keys)
(check-event))]
(db/run! cfg (fn [cfg]
(let [tnow (ct/now)
params (-> (event->params event)
(assoc :created-at tnow)
(update :tracked-at #(or % tnow)))]
(append-audit-entry cfg params)))))))
(db/run! cfg append-audit-entry params))))

View File

@ -70,14 +70,14 @@
(fn [{:keys [props] :as task}]
(let [items (lookup-webhooks cfg props)
event {::audit/profile-id (:profile-id props)
::audit/name "webhook"
::audit/type "trigger"
::audit/props {:name (get props :name)
:event-id (get props :id)
:total-affected (count items)}}]
event {:profile-id (:profile-id props)
:name "webhook"
:type "trigger"
:props {:name (get props :name)
:event-id (get props :id)
:total-affected (count items)}}]
(audit/insert! cfg event)
(audit/insert cfg event)
(when items
(l/trc :hint "webhooks found for event" :total (count items))

View File

@ -161,12 +161,13 @@
(defn- wrap-audit
[_ f mdata]
(if (or (contains? cf/flags :webhooks)
(contains? cf/flags :audit-log))
(contains? cf/flags :audit-log)
(contains? cf/flags :telemetry))
(if-not (::audit/skip mdata)
(fn [cfg params]
(let [result (f cfg params)]
(->> (audit/prepare-event cfg mdata params result)
(audit/submit! cfg))
(->> (audit/prepare-rpc-event cfg mdata params result)
(audit/submit* cfg))
result))
f)
f))

View File

@ -38,31 +38,31 @@
:context])
(defn- event->row [event]
[(::audit/id event)
(::audit/name event)
(::audit/source event)
(::audit/type event)
(::audit/tracked-at event)
(::audit/created-at event)
(::audit/profile-id event)
(db/inet (::audit/ip-addr event))
(db/tjson (::audit/props event))
(db/tjson (d/without-nils (::audit/context event)))])
[(:id event)
(:name event)
(:source event)
(:type event)
(:tracked-at event)
(:created-at event)
(:profile-id event)
(db/inet (:ip-addr event))
(db/tjson (:props event))
(db/tjson (d/without-nils (:context event)))])
(defn- adjust-timestamp
[{:keys [::audit/tracked-at ::audit/created-at] :as event}]
[{:keys [tracked-at created-at] :as event}]
(let [margin (inst-ms (ct/diff tracked-at created-at))]
(if (or (neg? margin)
(> margin 3600000))
;; If event is in future or lags more than 1 hour, we reasign
;; tracked-at to the server creation date
(-> event
(assoc ::audit/tracked-at created-at)
(update ::audit/context assoc :original-tracked-at tracked-at))
(assoc :tracked-at created-at)
(update :context assoc :original-tracked-at tracked-at))
event)))
(defn- exception-event?
[{:keys [::audit/type ::audit/name] :as ev}]
[{:keys [type name] :as ev}]
(and (= "action" type)
(or (= "unhandled-exception" name)
(= "exception-page" name))))
@ -72,78 +72,39 @@
(map adjust-timestamp)
(map event->row)))
(defn- get-events
(defn- prepare-events
[{:keys [::rpc/request-at ::rpc/profile-id events] :as params}]
(let [request (-> params meta ::http/request)
ip-addr (inet/parse-request request)
xform (map (fn [event]
{::audit/id (uuid/next)
::audit/type (:type event)
::audit/name (:name event)
::audit/props (:props event)
::audit/context (:context event)
::audit/profile-id profile-id
::audit/ip-addr ip-addr
::audit/source "frontend"
::audit/tracked-at (:timestamp event)
::audit/created-at request-at}))]
{:id (uuid/next)
:type (:type event)
:name (:name event)
:props (:props event)
:context (:context event)
:profile-id profile-id
:ip-addr ip-addr
:source "frontend"
:tracked-at (:timestamp event)
:created-at request-at}))]
(sequence xform events)))
;; Context keys from the frontend that are safe to retain for telemetry:
;; they describe the browser/OS environment but cannot identify a user.
;; Session-linking keys (session, external-session-id) and file-specific
;; stats (file-stats) are intentionally excluded.
(def ^:private safe-context-keys
#{:version
:locale
:browser
:browser-version
:engine
:engine-version
:os
:os-version
:device-type
:device-arch
:screen-width
:screen-height
:screen-color-depth
:screen-orientation
:event-origin
:event-namespace
:event-symbol})
(defn- filter-safe-context
"Return only the anonymous, non-identifying context fields from an
event context map. Any key not in `safe-context-keys` is dropped."
[ctx]
(select-keys ctx safe-context-keys))
(def ^:private xf:map-telemetry-event-row
(comp
(map adjust-timestamp)
(map (fn [event]
(let [tday (-> (::audit/created-at event)
(ct/truncate :days))
safe-props (-> (::audit/props event {})
(audit/filter-telemetry-props))
safe-context (-> (::audit/context event)
(select-keys safe-context-keys))]
[(::audit/id event)
(::audit/name event)
"telemetry:frontend"
(::audit/type event)
tday
tday
(::audit/profile-id event)
(db/inet "0.0.0.0")
(db/tjson safe-props)
(db/tjson safe-context)])))))
(-> event
(update :created-at ct/truncate :days)
(update :tracked-at ct/truncate :days)
(assoc :ip-addr "0.0.0.0")
(audit/filter-telemetry-props)
(audit/filter-telemetry-context))))
(map event->row)))
(defn- handle-events
[{:keys [::db/pool] :as cfg} params]
(let [events (get-events params)]
(let [events (prepare-events params)]
;; Look for error reports and save them on internal reports table
(when-let [events (->> events

View File

@ -425,10 +425,10 @@
(doseq [file-id result]
(let [props (assoc props :id file-id)
event (-> (audit/event-from-rpc-params params)
(assoc ::audit/profile-id profile-id)
(assoc ::audit/name "create-file")
(assoc ::audit/props props))]
(audit/submit! cfg event))))))
(assoc :profile-id profile-id)
(assoc :name "create-file")
(assoc :props props))]
(audit/submit cfg event))))))
result))

View File

@ -156,9 +156,9 @@
"update-team-invitation"
"create-team-invitation")
event (-> (audit/event-from-rpc-params params)
(assoc ::audit/name evname)
(assoc ::audit/props props))]
(audit/submit! cfg event))
(assoc :name evname)
(assoc :props props))]
(audit/submit cfg event))
(when (allow-invitation-emails? member)
(eml/send! {::eml/conn conn
@ -410,9 +410,9 @@
(let [props {:name name :features features}
event (-> (audit/event-from-rpc-params params)
(assoc ::audit/name "create-team")
(assoc ::audit/props props))]
(audit/submit! cfg event))
(assoc :name "create-team")
(assoc :props props))]
(audit/submit cfg event))
;; Create invitations for all provided emails.
(let [profile (db/get-by-id conn :profile profile-id)

View File

@ -173,24 +173,22 @@
:role (:role claims)
:invitation-id (:id invitation)}]
(audit/submit!
cfg
(-> (audit/event-from-rpc-params params)
(assoc ::audit/name "accept-team-invitation")
(assoc ::audit/props props)))
(audit/submit cfg
(-> (audit/event-from-rpc-params params)
(assoc :name "accept-team-invitation")
(assoc :props props)))
;; NOTE: Backward compatibility; old invitations can
;; have the `created-by` to be nil; so in this case we
;; don't submit this event to the audit-log
(when-let [created-by (:created-by invitation)]
(audit/submit!
cfg
(-> (audit/event-from-rpc-params params)
(assoc ::audit/profile-id created-by)
(assoc ::audit/name "accept-team-invitation-from")
(assoc ::audit/props (assoc props
:profile-id (:id profile)
:email (:email profile))))))
(audit/submit cfg
(-> (audit/event-from-rpc-params params)
(assoc :profile-id created-by)
(assoc :name "accept-team-invitation-from")
(assoc :props (assoc props
:profile-id (:id profile)
:email (:email profile))))))
(accept-invitation cfg claims invitation profile)
(assoc claims :state :created))

View File

@ -553,14 +553,13 @@
(let [file-id (h/parse-uuid file-id)
tnow (ct/now)]
(audit/insert! main/system
{::audit/name "delete-file"
::audit/type "action"
::audit/profile-id uuid/zero
::audit/props {:id file-id}
::audit/context {:triggered-by "srepl"
:cause "explicit call to delete-file!"}
::audit/tracked-at tnow})
(audit/insert main/system
{:name "delete-file"
:type "action"
:props {:id file-id}
:context {:triggered-by "srepl"
:cause "explicit call to delete-file!"}
:tracked-at tnow})
(wrk/invoke! (-> main/system
(assoc ::wrk/task :delete-object)
(assoc ::wrk/params {:object :file
@ -578,15 +577,12 @@
{:id file-id}
{::db/remove-deleted false
::sql/columns [:id :name]})]
(audit/insert! system
{::audit/name "restore-file"
::audit/type "action"
::audit/profile-id uuid/zero
::audit/props file
::audit/context {:triggered-by "srepl"
:cause "explicit call to restore-file!"}
::audit/tracked-at (ct/now)})
(audit/insert system
{:name "restore-file"
:type "action"
:props file
:context {:triggered-by "srepl"
:cause "explicit call to restore-file!"}})
(#'files/restore-file conn file-id))
:restored))))
@ -597,14 +593,13 @@
(let [project-id (h/parse-uuid project-id)
tnow (ct/now)]
(audit/insert! main/system
{::audit/name "delete-project"
::audit/type "action"
::audit/profile-id uuid/zero
::audit/props {:id project-id}
::audit/context {:triggered-by "srepl"
:cause "explicit call to delete-project!"}
::audit/tracked-at tnow})
(audit/insert main/system
{:name "delete-project"
:type "action"
:props {:id project-id}
:context {:triggered-by "srepl"
:cause "explicit call to delete-project!"}
:tracked-at tnow})
(wrk/invoke! (-> main/system
(assoc ::wrk/task :delete-object)
@ -635,14 +630,12 @@
(when-let [project (db/get* system :project
{:id project-id}
{::db/remove-deleted false})]
(audit/insert! system
{::audit/name "restore-project"
::audit/type "action"
::audit/profile-id uuid/zero
::audit/props project
::audit/context {:triggered-by "srepl"
:cause "explicit call to restore-team!"}
::audit/tracked-at (ct/now)})
(audit/insert system
{:name "restore-project"
:type "action"
:props project
:context {:triggered-by "srepl"
:cause "explicit call to restore-team!"}})
(restore-project* system project-id))))))
@ -652,14 +645,13 @@
(let [team-id (h/parse-uuid team-id)
tnow (ct/now)]
(audit/insert! main/system
{::audit/name "delete-team"
::audit/type "action"
::audit/profile-id uuid/zero
::audit/props {:id team-id}
::audit/context {:triggered-by "srepl"
:cause "explicit call to delete-profile!"}
::audit/tracked-at tnow})
(audit/insert main/system
{:name "delete-team"
:type "action"
:props {:id team-id}
:context {:triggered-by "srepl"
:cause "explicit call to delete-profile!"}
:tracked-at tnow})
(wrk/invoke! (-> main/system
(assoc ::wrk/task :delete-object)
@ -695,14 +687,12 @@
{:id team-id}
{::db/remove-deleted false})
(teams/decode-row))]
(audit/insert! system
{::audit/name "restore-team"
::audit/type "action"
::audit/profile-id uuid/zero
::audit/props team
::audit/context {:triggered-by "srepl"
:cause "explicit call to restore-team!"}
::audit/tracked-at (ct/now)})
(audit/insert system
{:name "restore-team"
:type "action"
:props team
:context {:triggered-by "srepl"
:cause "explicit call to restore-team!"}})
(restore-team* system team-id))))))
@ -712,13 +702,12 @@
(let [profile-id (h/parse-uuid profile-id)
tnow (ct/now)]
(audit/insert! main/system
{::audit/name "delete-profile"
::audit/type "action"
::audit/profile-id uuid/zero
::audit/context {:triggered-by "srepl"
:cause "explicit call to delete-profile!"}
::audit/tracked-at tnow})
(audit/insert main/system
{:name "delete-profile"
:type "action"
:context {:triggered-by "srepl"
:cause "explicit call to delete-profile!"}
:tracked-at tnow})
(wrk/invoke! (-> main/system
(assoc ::wrk/task :delete-object)
@ -737,14 +726,12 @@
{:id profile-id}
{::db/remove-deleted false})
(profile/decode-row))]
(audit/insert! system
{::audit/name "restore-profile"
::audit/type "action"
::audit/profile-id uuid/zero
::audit/props (audit/profile->props profile)
::audit/context {:triggered-by "srepl"
:cause "explicit call to restore-profile!"}
::audit/tracked-at (ct/now)})
(audit/insert system
{:name "restore-profile"
:type "action"
:props (audit/profile->props profile)
:context {:triggered-by "srepl"
:cause "explicit call to restore-profile!"}})
(db/update! system :profile
{:deleted-at nil}
@ -768,14 +755,14 @@
{::db/remove-deleted false})
(profile/decode-row))]
(do
(audit/insert! system
{::audit/name "delete-profile"
::audit/type "action"
::audit/profile-id (:id profile)
::audit/tracked-at deleted-at
::audit/props (audit/profile->props profile)
::audit/context {:triggered-by "srepl"
:cause "explicit call to delete-profiles-in-bulk!"}})
(audit/insert system
{:name "delete-profile"
:type "action"
:profile-id (:id profile)
:tracked-at deleted-at
:props (audit/profile->props profile)
:context {:triggered-by "srepl"
:cause "explicit call to delete-profiles-in-bulk!"}})
(wrk/invoke! (-> system
(assoc ::wrk/task :delete-object)
(assoc ::wrk/params {:object :profile