Packages
phoenix_kit
1.7.81
1.7.207
1.7.206
1.7.205
1.7.204
1.7.203
1.7.202
1.7.201
1.7.200
1.7.199
1.7.198
1.7.197
1.7.196
1.7.194
1.7.193
1.7.192
1.7.191
1.7.190
1.7.189
1.7.187
1.7.186
1.7.185
1.7.184
1.7.183
1.7.182
1.7.181
1.7.180
1.7.179
1.7.178
1.7.177
1.7.176
1.7.175
1.7.174
1.7.173
1.7.172
1.7.171
1.7.170
1.7.169
1.7.168
1.7.167
1.7.166
1.7.165
1.7.164
1.7.162
1.7.161
1.7.160
1.7.159
1.7.157
1.7.156
1.7.155
1.7.154
1.7.153
1.7.152
1.7.151
1.7.150
1.7.149
1.7.146
1.7.145
1.7.144
1.7.143
1.7.138
1.7.133
1.7.132
1.7.131
1.7.130
1.7.128
1.7.126
1.7.125
1.7.121
1.7.120
1.7.119
1.7.118
1.7.117
1.7.116
1.7.115
1.7.114
1.7.113
1.7.112
1.7.111
1.7.110
1.7.109
1.7.108
1.7.107
1.7.106
1.7.105
1.7.104
1.7.103
1.7.102
1.7.101
1.7.100
1.7.99
1.7.98
1.7.97
1.7.96
1.7.95
1.7.94
1.7.93
1.7.92
1.7.91
1.7.90
1.7.89
1.7.88
1.7.87
1.7.86
1.7.85
1.7.84
1.7.83
1.7.82
1.7.81
1.7.80
1.7.79
1.7.78
1.7.77
1.7.76
1.7.75
1.7.74
1.7.71
1.7.70
1.7.69
1.7.66
1.7.65
1.7.64
1.7.63
1.7.62
1.7.61
1.7.59
1.7.58
1.7.57
1.7.56
1.7.55
1.7.54
1.7.53
1.7.52
1.7.51
1.7.49
1.7.44
1.7.43
1.7.42
1.7.41
1.7.39
1.7.38
1.7.37
1.7.36
1.7.34
1.7.33
1.7.31
1.7.30
1.7.29
1.7.28
1.7.27
1.7.26
1.7.25
1.7.24
1.7.23
1.7.22
1.7.21
1.7.20
1.7.19
1.7.18
1.7.17
1.7.16
1.7.15
1.7.14
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.6.20
1.6.19
1.6.18
1.6.17
1.6.16
1.6.15
1.6.14
1.6.13
1.6.12
1.6.11
1.6.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.5.2
1.5.1
1.5.0
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.2
1.3.1
1.3.0
1.2.10
1.2.9
1.2.8
1.2.7
1.2.5
1.2.4
1.2.2
1.2.1
1.2.0
1.1.0
1.0.0
A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more
Current section
Files
Jump to
Current section
Files
lib/modules/emails/web/queue.ex
defmodule PhoenixKit.Modules.Emails.Web.Queue do
@moduledoc """
LiveView for email queue monitoring and rate limit management.
Provides real-time monitoring of email sending activity, rate limiting status,
and queue management functionality for the email system.
## Features
- **Real-time Activity**: Live updates of recent email sending activity
- **Rate Limit Monitoring**: Current rate limit status and usage
- **Failed Email Management**: Retry and management of failed emails
- **Bulk Operations**: Pause/resume email sending, bulk retry
- **Provider Status**: Monitor email provider health and performance
- **Alert Management**: Configure alerts for rate limits and failures
## Route
This LiveView is mounted at `{prefix}/admin/emails/queue` and requires
appropriate admin permissions.
Note: `{prefix}` is your configured PhoenixKit URL prefix (default: `/phoenix_kit`).
## Usage
# In your Phoenix router
live "/email-queue", PhoenixKitWeb.Live.Modules.Emails.EmailQueueLive, :index
## Permissions
Access is restricted to users with admin or owner roles in PhoenixKit.
"""
use PhoenixKitWeb, :live_view
require Logger
alias PhoenixKit.Modules.Emails
alias PhoenixKit.Modules.Emails.{Log, RateLimiter}
alias PhoenixKit.Settings
alias PhoenixKit.Utils.Date, as: UtilsDate
alias PhoenixKit.Utils.Number, as: UtilsNumber
alias PhoenixKit.Utils.Routes
# Auto-refresh every 10 seconds for real-time monitoring
@refresh_interval 10_000
## --- Lifecycle Callbacks ---
@impl true
def mount(_params, _session, socket) do
# Check if email is enabled
if Emails.enabled?() do
# Get project title from settings
project_title = Settings.get_project_title()
# Schedule periodic refresh for real-time updates
if connected?(socket) do
Process.send_after(self(), :refresh_queue, @refresh_interval)
end
socket =
socket
|> assign(:project_title, project_title)
|> assign(:url_path, Routes.path("/admin/emails/queue"))
|> assign(:loading, true)
|> assign(:recent_activity, [])
|> assign(:rate_limit_status, %{})
|> assign(:failed_emails, [])
|> assign(:system_status, %{})
|> assign(:selected_emails, [])
|> assign(:bulk_action, nil)
|> assign(:last_updated, UtilsDate.utc_now())
|> load_queue_data()
{:ok, socket}
else
{:ok,
socket
|> put_flash(:error, "Email is not enabled")
|> push_navigate(to: Routes.path("/admin"))}
end
end
## --- Event Handlers ---
@impl true
def handle_event("refresh", _params, socket) do
{:noreply,
socket
|> assign(:loading, true)
|> load_queue_data()}
end
@impl true
def handle_event("retry_email", %{"email_uuid" => email_uuid}, socket) do
case retry_failed_email(email_uuid) do
{:ok, _log} ->
{:noreply,
socket
|> put_flash(:info, "Email queued for retry")
|> load_queue_data()}
{:error, reason} ->
{:noreply,
socket
|> put_flash(:error, "Failed to retry email: #{reason}")}
end
end
@impl true
def handle_event("toggle_email_selection", %{"email_uuid" => email_uuid}, socket) do
selected = socket.assigns.selected_emails
new_selected =
if email_uuid in selected do
List.delete(selected, email_uuid)
else
[email_uuid | selected]
end
{:noreply, assign(socket, :selected_emails, new_selected)}
end
@impl true
def handle_event("select_all_failed", _params, socket) do
all_failed_ids = Enum.map(socket.assigns.failed_emails, & &1.uuid)
{:noreply,
socket
|> assign(:selected_emails, all_failed_ids)}
end
@impl true
def handle_event("clear_selection", _params, socket) do
{:noreply,
socket
|> assign(:selected_emails, [])
|> assign(:bulk_action, nil)}
end
@impl true
def handle_event("set_bulk_action", %{"action" => action}, socket) do
{:noreply,
socket
|> assign(:bulk_action, action)}
end
@impl true
def handle_event("execute_bulk_action", _params, socket) do
case socket.assigns.bulk_action do
"retry" ->
execute_bulk_retry(socket)
"delete" ->
execute_bulk_delete(socket)
_ ->
{:noreply,
socket
|> put_flash(:error, "Invalid bulk action")}
end
end
@impl true
def handle_event("reset_rate_limits", _params, socket) do
# This would reset rate limit counters (implementation would depend on storage)
{:noreply,
socket
|> put_flash(:info, "Rate limits reset")
|> load_queue_data()}
end
@impl true
def handle_info(:refresh_queue, socket) do
# Schedule next refresh
Process.send_after(self(), :refresh_queue, @refresh_interval)
{:noreply,
socket
|> assign(:last_updated, UtilsDate.utc_now())
|> load_queue_data()}
end
defp load_queue_data(socket) do
recent_activity = load_recent_activity()
rate_limit_status = RateLimiter.get_rate_limit_status()
failed_emails = load_failed_emails()
system_status = load_system_status()
socket
|> assign(:recent_activity, recent_activity)
|> assign(:rate_limit_status, rate_limit_status)
|> assign(:failed_emails, failed_emails)
|> assign(:system_status, system_status)
|> assign(:loading, false)
end
defp load_recent_activity do
# Get last 20 emails
Emails.list_logs(%{limit: 20, order_by: :sent_at, order_dir: :desc})
end
defp load_failed_emails do
# Get failed emails from last 24 hours
Emails.list_logs(%{
status: "failed",
since: DateTime.add(UtilsDate.utc_now(), -24, :hour),
limit: 50
})
end
defp load_system_status do
%{
system_enabled: Emails.enabled?(),
total_sent_today: get_today_count(),
retention_days: Emails.get_retention_days()
}
end
defp get_today_count do
today_start = UtilsDate.utc_now() |> DateTime.to_date() |> DateTime.new!(~T[00:00:00])
now = UtilsDate.utc_now()
case Emails.get_system_stats(
{:date_range, DateTime.to_date(today_start), DateTime.to_date(now)}
) do
%{total_sent: count} -> count
_ -> 0
end
end
defp retry_failed_email(email_uuid) do
# Get the email log
log = Emails.get_log!(email_uuid)
# Update status to "queued" for retry and increment retry_count
Emails.update_log_status(log, "queued")
# Also update retry count
Log.update_log(log, %{
retry_count: (log.retry_count || 0) + 1,
error_message: nil
})
rescue
Ecto.NoResultsError ->
{:error, :not_found}
error ->
Logger.error("Failed to retry email #{email_uuid}: #{inspect(error)}")
{:error, :retry_failed}
end
defp execute_bulk_retry(socket) do
selected_ids = socket.assigns.selected_emails
success_count =
Enum.reduce(selected_ids, 0, fn id, acc ->
case retry_failed_email(id) do
{:ok, _} -> acc + 1
_ -> acc
end
end)
message = "Retried #{success_count} of #{length(selected_ids)} emails"
{:noreply,
socket
|> assign(:selected_emails, [])
|> assign(:bulk_action, nil)
|> put_flash(:info, message)
|> load_queue_data()}
end
defp execute_bulk_delete(socket) do
selected_ids = socket.assigns.selected_emails
success_count =
Enum.reduce(selected_ids, 0, fn id, acc ->
try do
log = Emails.get_log!(id)
case Emails.delete_log(log) do
{:ok, _} ->
acc + 1
{:error, reason} ->
Logger.error("Failed to delete email #{id}: #{inspect(reason)}")
acc
end
rescue
Ecto.NoResultsError ->
Logger.warning("Email log #{id} not found for deletion")
acc
error ->
Logger.error("Error deleting email #{id}: #{inspect(error)}")
acc
end
end)
message = "Deleted #{success_count} of #{length(selected_ids)} emails"
{:noreply,
socket
|> assign(:selected_emails, [])
|> assign(:bulk_action, nil)
|> put_flash(:info, message)
|> load_queue_data()}
end
end