PhoenixKitDb (PhoenixKitDb v0.1.1)

Copy Markdown View Source

Database explorer module for PhoenixKit.

Provides metadata, stats, and paginated previews for Postgres tables so the admin UI can browse data without exposing full SQL access. Live updates ride on Postgres LISTEN/NOTIFY via the PhoenixKitDb.Listener GenServer.

Live Updates

When a table is being viewed, changes to that table trigger automatic refreshes. This requires:

  1. The Listener GenServer running (started via the host's PhoenixKit.Supervisor from this module's children/0 callback).
  2. A notification trigger on the table being viewed — installed lazily by ensure_trigger/2 on first view.

Summary

Types

An identifier accepted by the schema/table-name validator.

Functions

Aggregated Postgres stats for all user tables.

Whether the DB module is enabled.

Ensures the notification function exists and creates a trigger on the table.

Fetches a single row by ID from a table.

Whether a table has a notification trigger installed.

Lists tables + stats with pagination and search.

Lists all tables that have notification triggers installed.

Removes all notification triggers from all tables.

Removes the notification trigger from a table.

Returns table metadata and a row preview window.

Types

identifier_string()

@type identifier_string() :: String.t()

An identifier accepted by the schema/table-name validator.

Functions

database_stats()

@spec database_stats() :: %{
  table_count: non_neg_integer(),
  approx_rows: non_neg_integer(),
  total_size_bytes: non_neg_integer(),
  database_size_bytes: non_neg_integer()
}

Aggregated Postgres stats for all user tables.

enabled?()

@spec enabled?() :: boolean()

Whether the DB module is enabled.

Reads from the DB-backed settings table. Defensive against three failure modes that can hit before/around DB availability:

  • rescue _: DB not running, table missing, schema mismatch, etc.
  • catch :exit, _: connection pool checkout EXIT (e.g. when a test sandbox owner has just stopped — test-environment artifact, but harmless to handle in production code too).

All branches return false so callers don't need to special-case startup ordering.

ensure_trigger(schema, table)

@spec ensure_trigger(identifier_string(), identifier_string()) ::
  :ok | {:error, :invalid_identifier | term()}

Ensures the notification function exists and creates a trigger on the table.

Idempotent. A concurrent caller racing to create the same trigger surfaces as :ok rather than an error — the win condition is "the trigger exists", and Postgres' duplicate_object reply on the loser is folded back into success.

Returns :ok on success, {:error, :invalid_identifier} if the schema/table contain unsafe characters, or {:error, reason} on a Postgres error.

fetch_row(schema, table, row_id)

@spec fetch_row(identifier_string() | nil, identifier_string(), term()) ::
  {:ok, map()}
  | {:error, :not_found | :invalid_id | :invalid_identifier | term()}

Fetches a single row by ID from a table.

Returns {:ok, row_map} or {:error, :not_found | :invalid_id | :invalid_identifier | term()}. :invalid_identifier covers the case where schema or table contains characters that can't be safely quoted into SQL — this surfaces graceful "table not found" UX instead of a 500.

has_trigger?(schema, table)

@spec has_trigger?(identifier_string(), identifier_string()) :: boolean()

Whether a table has a notification trigger installed.

list_tables(opts \\ %{})

@spec list_tables(map()) :: map()

Lists tables + stats with pagination and search.

list_triggered_tables()

@spec list_triggered_tables() :: [{String.t(), String.t()}]

Lists all tables that have notification triggers installed.

remove_all_triggers()

@spec remove_all_triggers() :: :ok

Removes all notification triggers from all tables.

remove_trigger(schema, table)

@spec remove_trigger(identifier_string(), identifier_string()) ::
  :ok | {:error, :invalid_identifier | term()}

Removes the notification trigger from a table.

table_preview(schema, table, opts \\ %{})

@spec table_preview(identifier_string() | nil, identifier_string(), map()) :: map()

Returns table metadata and a row preview window.

When schema or table is malformed, returns the empty preview shape rather than crashing — the LV uses this signal to render a graceful "table not found" message.