PhoenixKit.Activity (phoenix_kit v2.6.0)

Copy Markdown View Source

Activity feed for tracking business-level actions across the platform.

Provides a simple API for logging and querying activities. Any module can call Activity.log/1 to record an action. The admin dashboard shows a real-time activity stream.

Usage

PhoenixKit.Activity.log(%{
  action: "post.created",
  actor_uuid: user.uuid,
  resource_type: "post",
  resource_uuid: post.uuid,
  metadata: %{"title" => post.title}
})

Action naming convention

Use dotted format: resource.verb — e.g., "post.created", "comment.liked", "user.registered", "password.changed", "role.assigned".

Summary

Functions

Returns a CSS badge class based on the action verb.

Counts activities matching the given filters.

Whether scope may read the WHOLE activity log — every user's actions — rather than only its own.

Gets a single activity entry by UUID with preloaded associations.

Gets a single activity entry by UUID. Raises if not found.

Renders an activity-metadata VALUE as human-readable text, tolerant of the shapes host apps store.

Lists activities with filtering and pagination.

Returns distinct action types that have been logged.

Lists activities for a specific user (as actor).

Returns distinct modes that have been logged.

Returns distinct modules that have been logged.

Returns distinct resource types that have been logged.

Logs an activity.

Logs a user change with automatic from/to diff extraction from a changeset.

Returns a CSS badge class based on the mode.

Whether entry is scope's OWN activity.

Deletes activities older than the given number of days.

PubSub topic for activity events.

Returns the N most recent activities.

Resolves resource info for entries where resource_type is "user".

Returns the configured retention period in days.

Functions

action_badge_color(action)

Returns a CSS badge class based on the action verb.

count(opts \\ [])

Counts activities matching the given filters.

full_log_access?(scope)

@spec full_log_access?(PhoenixKit.Users.Auth.Scope.t() | nil) :: boolean()

Whether scope may read the WHOLE activity log — every user's actions — rather than only its own.

Administrators qualify: the Admin or Owner role, or any "*" superadmin role. Everyone else (a custom role that merely holds dashboard) is scoped to their own actions. The activity LiveViews share this as the single gate for both the list and the single-entry page.

get_entry(uuid)

Gets a single activity entry by UUID with preloaded associations.

get_entry!(uuid)

Gets a single activity entry by UUID. Raises if not found.

humanize_metadata_value(value)

Renders an activity-metadata VALUE as human-readable text, tolerant of the shapes host apps store.

The admin feed and detail page display arbitrary per-module metadata, so this must never raise Protocol.UndefinedError (String.Chars/to_string on a Map): a field-change diff carries a nested %{"from" => _, "to" => _} map, which is rendered as "1 → 2"; any other map is rendered as "key: value, ...". Legacy entries whose scalar was serialised via inspect ("Decimal.new(\"1\")") are unwrapped to their inner value so old rows read cleanly instead of leaking Elixir syntax.

list(opts \\ [])

Lists activities with filtering and pagination.

Options

  • :action — filter by action string (exact match or prefix with "post.*")
  • :actor_uuid — filter by who performed the action
  • :resource_type — filter by resource type
  • :resource_uuid — filter by the specific resource's UUID (scope to one resource)
  • :target_uuid — filter by who was affected
  • :since — filter activities after this datetime
  • :until — filter activities before this datetime
  • :page — page number (default: 1)
  • :per_page — items per page (default: 50)
  • :preload — associations to preload (default: [:actor])

list_action_types()

Returns distinct action types that have been logged.

list_for_user(user_uuid, opts \\ [])

Lists activities for a specific user (as actor).

list_modes()

Returns distinct modes that have been logged.

list_modules()

Returns distinct modules that have been logged.

list_resource_types()

Returns distinct resource types that have been logged.

log(attrs)

Logs an activity.

Required fields

  • :action — dotted action string (e.g., "post.created")

Optional fields

  • :actor_uuid — who performed the action
  • :resource_type — type of resource acted on
  • :resource_uuid — UUID of the resource
  • :target_uuid — who was affected (e.g., follow target)
  • :metadata — map of additional context

Returns {:ok, entry} or {:error, changeset}. Failures are logged but never crash.

log_user_change(action, user, changeset, opts \\ [])

Logs a user change with automatic from/to diff extraction from a changeset.

Extracts changed fields from the changeset and builds field_from / field_to metadata pairs. Skips logging if nothing actually changed.

Options

  • :actor_uuid — who performed the action (default: the user's own UUID)
  • :target_uuid — who was affected (default: nil)
  • :mode — "auto" or "manual" (default: "auto")
  • :actor_role — "user" or "admin" (default: "user")
  • :extra_metadata — additional metadata to merge in (default: %{})
  • :skip_fields — fields to exclude from diff (default: [:custom_fields])

mode_badge_color(arg1)

Returns a CSS badge class based on the mode.

own_entry?(scope, entry)

Whether entry is scope's OWN activity.

"Own" is defined by AUTHORSHIP: the scope's user is the entry's actor_uuid (the account that performed the action). A record where the user is only the target_uuid — someone else acted on or for them — is NOT their own and stays hidden from a non-administrator.

This is the single definition the audit log enforces in two places, and they must not drift: the list pins actor_uuid to the user (Activity.Index) and the single-entry page gates on this predicate (Activity.Show). If "own" ever needs to include target-side records, change it HERE and switch the list's filter in lock-step — do not fork the rule per view.

prune(days)

Deletes activities older than the given number of days.

pubsub_topic()

PubSub topic for activity events.

recent(limit \\ 20)

Returns the N most recent activities.

resolve_resource_users(entries)

Resolves resource info for entries where resource_type is "user".

Returns a map of resource_uuid => %{email: ..., first_name: ..., last_name: ...}. Batch-queries to avoid N+1.

retention_days()

Returns the configured retention period in days.