PhoenixGenApi.Relay (PhoenixGenApi v2.17.1)

Copy Markdown View Source

Relay message feature for PhoenixGenApi.

Provides group-based message relaying where a user sends a message to a group and all members (including the sender) receive it.

Group Types

:public

  • Anyone can join immediately as :active.
  • All members can send and receive messages.

:private

  • New members join with :pending status.
  • Any existing active member can accept pending members.
  • Only :active members can send and receive messages.

:strict_private

  • New members join with :pending status.
  • Only :admin members can accept pending members.
  • Admins can :mute and :unmute members.
  • Muted members can receive but cannot send messages.

Architecture

  • ETS table (:phoenix_gen_api_relay_groups) stores group metadata: {group_id, group_type, members_map} where members_map is %{user_id => %{roles: MapSet, status: atom, joined_at: DateTime}}.

  • Registry (PhoenixGenApi.RelayRegistry) with :duplicate keys maps group_id to {user_id, channel_pid} for dispatching messages to channel processes via send/2.

Summary

Functions

Accepts a pending member into a group.

Creates a new group. The creator becomes the admin with :active status.

Deletes a group from ETS. Registry entries are cleaned up when channel processes terminate.

Returns group info including type and members map.

Handles a relay message request. This is the MFA target configured in FunConfig for request_type: "relay_msg".

Removes a user from a group and unregisters them from the Registry.

Mutes a member in a strict_private group. Only admins can mute. Muted members can receive but cannot send messages.

Returns the ETS table name for group metadata storage.

Unmutes a member in a strict_private group. Only admins can unmute.

Types

group_type()

@type group_type() :: :public | :private | :strict_private

member_info()

@type member_info() :: %{
  roles: MapSet.t(atom()),
  status: member_status(),
  joined_at: DateTime.t()
}

member_status()

@type member_status() :: :active | :pending | :muted

Functions

accept_member(group_id, actor_user_id, target_user_id)

@spec accept_member(String.t(), String.t(), String.t()) ::
  :ok
  | {:error, :not_found | :user_not_in_group | :not_admin | :user_not_pending}

Accepts a pending member into a group.

  • private: any active member can accept.
  • strict_private: only admin can accept.

Returns :ok or {:error, reason}.

create_group(group_id, group_type, creator_user_id, channel_pid)

@spec create_group(String.t(), group_type(), String.t(), pid()) ::
  :ok | {:error, :already_exists}

Creates a new group. The creator becomes the admin with :active status.

Returns :ok or {:error, :already_exists}.

delete_group(group_id)

@spec delete_group(String.t()) :: :ok | {:error, :not_found}

Deletes a group from ETS. Registry entries are cleaned up when channel processes terminate.

Returns :ok or {:error, :not_found}.

get_group_info(group_id)

@spec get_group_info(String.t()) :: {:ok, map()} | {:error, :not_found}

Returns group info including type and members map.

handle_relay(request, fun_config)

Handles a relay message request. This is the MFA target configured in FunConfig for request_type: "relay_msg".

Extracts group_id and message from request.args, validates the sender's membership and permissions, then sends {:relay_message, response} to all group members' channel pids via the Registry.

Returns a Response with relay status.

join_group(group_id, user_id, channel_pid)

@spec join_group(String.t(), String.t(), pid()) ::
  {:ok, :active | :pending} | {:error, :not_found | :already_member}

Joins a user to a group.

  • public: user becomes :active immediately.
  • private / strict_private: user becomes :pending, needs acceptance.

Returns {:ok, :active} or {:ok, :pending} depending on group type, or {:error, reason}.

leave_group(group_id, user_id)

@spec leave_group(String.t(), String.t()) ::
  :ok | {:error, :not_found | :user_not_in_group}

Removes a user from a group and unregisters them from the Registry.

Returns :ok or {:error, reason}.

mute_member(group_id, actor_user_id, target_user_id)

@spec mute_member(String.t(), String.t(), String.t()) ::
  :ok
  | {:error,
     :not_found
     | :not_strict_private
     | :user_not_in_group
     | :not_admin
     | :cannot_mute}

Mutes a member in a strict_private group. Only admins can mute. Muted members can receive but cannot send messages.

Returns :ok or {:error, reason}.

table()

@spec table() :: atom()

Returns the ETS table name for group metadata storage.

unmute_member(group_id, actor_user_id, target_user_id)

@spec unmute_member(String.t(), String.t(), String.t()) ::
  :ok
  | {:error,
     :not_found
     | :not_strict_private
     | :user_not_in_group
     | :not_admin
     | :not_muted}

Unmutes a member in a strict_private group. Only admins can unmute.

Returns :ok or {:error, reason}.