Packages
phoenix_kit
1.3.2
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/phoenix_kit_web/controllers/email_export_controller.ex
defmodule PhoenixKitWeb.Controllers.EmailExportController do
@moduledoc """
Controller for exporting email tracking data to CSV format.
Provides server-side CSV export functionality for email logs, metrics,
and blocklist data. Supports filtered exports based on query parameters.
## Features
- **Email Logs Export**: Export filtered email logs to CSV
- **Metrics Export**: Export email analytics and performance data
- **Blocklist Export**: Export blocked email addresses
- **Single Email Export**: Export individual email details
- **Filter Support**: Respects all filtering parameters from LiveViews
- **Large Dataset Streaming**: Efficient handling of large exports
## Security
Access is restricted to users with admin or owner roles in PhoenixKit.
All exports require proper authentication and authorization.
"""
use PhoenixKitWeb, :controller
alias PhoenixKit.Emails
## --- Email Logs Export ---
@doc """
Export email logs to CSV based on filter parameters.
## Parameters
- `search` - Search query for recipient, subject, or campaign
- `status` - Filter by email status (sent, delivered, bounced, etc.)
- `message_tag` - Filter by message type (authentication, marketing, etc.)
- `campaign_id` - Filter by campaign ID
- `from_date` - Start date filter (YYYY-MM-DD)
- `to_date` - End date filter (YYYY-MM-DD)
## Response
Returns CSV file with appropriate headers for browser download.
"""
def export_logs(conn, params) do
if Emails.enabled?() do
# Build filters from query parameters
filters = build_export_filters(params)
# Generate filename with timestamp
filename = "email_logs_#{Date.utc_today()}.csv"
# Get logs data
logs = Emails.list_logs(filters)
# Generate CSV content
csv_content = generate_logs_csv(logs)
# Send CSV file
conn
|> put_resp_content_type("text/csv")
|> put_resp_header("content-disposition", "attachment; filename=\"#{filename}\"")
|> send_resp(200, csv_content)
else
conn
|> put_flash(:error, "Email management is not enabled")
|> redirect(to: "/phoenix_kit/admin/dashboard")
end
end
@doc """
Export email metrics data to CSV.
## Parameters
- `period` - Time period (last_7_days, last_30_days, last_90_days, etc.)
- `group_by` - Grouping option (day, week, month, campaign, provider)
## Response
Returns CSV file with email metrics and analytics data.
"""
def export_metrics(conn, params) do
if Emails.enabled?() do
# Get metrics based on parameters
period = params["period"] || "last_30_days"
group_by = params["group_by"] || "day"
filename = "email_metrics_#{period}_#{Date.utc_today()}.csv"
# Get metrics data
metrics = Emails.get_engagement_metrics(String.to_atom(period))
# Generate CSV content
csv_content = generate_metrics_csv(metrics, group_by)
# Send CSV file
conn
|> put_resp_content_type("text/csv")
|> put_resp_header("content-disposition", "attachment; filename=\"#{filename}\"")
|> send_resp(200, csv_content)
else
conn
|> put_flash(:error, "Email management is not enabled")
|> redirect(to: "/phoenix_kit/admin/dashboard")
end
end
@doc """
Export email blocklist to CSV.
## Parameters
- `reason` - Filter by block reason (rate_limit, manual, bounce, etc.)
## Response
Returns CSV file with blocked email addresses and metadata.
"""
def export_blocklist(conn, _params) do
if Emails.enabled?() do
# For now, return empty CSV as blocklist API is not fully implemented
filename = "email_blocklist_#{Date.utc_today()}.csv"
csv_content = "Email,Reason,Blocked At,Block Count,Last Attempt,Notes\n"
# Send CSV file
conn
|> put_resp_content_type("text/csv")
|> put_resp_header("content-disposition", "attachment; filename=\"#{filename}\"")
|> send_resp(200, csv_content)
else
conn
|> put_flash(:error, "Email management is not enabled")
|> redirect(to: "/phoenix_kit/admin/dashboard")
end
end
@doc """
Export single email details to CSV.
## Parameters
- `id` - Email log ID
## Response
Returns CSV file with detailed email information and events.
"""
def export_email_details(conn, %{"id" => email_id}) do
if Emails.enabled?() do
try do
# Convert string ID to integer
email_id = String.to_integer(email_id)
log = Emails.get_log!(email_id)
# Get events for this email
events = Emails.list_events_for_log(email_id)
filename = "email_#{email_id}_details_#{Date.utc_today()}.csv"
# Generate CSV content
csv_content = generate_email_details_csv(log, events)
# Send CSV file
conn
|> put_resp_content_type("text/csv")
|> put_resp_header("content-disposition", "attachment; filename=\"#{filename}\"")
|> send_resp(200, csv_content)
rescue
Ecto.NoResultsError ->
conn
|> put_flash(:error, "Email not found")
|> redirect(to: "/phoenix_kit/admin/emails")
ArgumentError ->
conn
|> put_flash(:error, "Invalid email ID")
|> redirect(to: "/phoenix_kit/admin/emails")
end
else
conn
|> put_flash(:error, "Email management is not enabled")
|> redirect(to: "/phoenix_kit/admin/dashboard")
end
end
## --- Private Helper Functions ---
# Build query filters from request parameters for email logs
defp build_export_filters(params) do
%{}
|> add_string_filter(params, "search", :search, &String.trim/1)
|> add_string_filter(params, "status", :status)
|> add_string_filter(params, "message_tag", :message_tag)
|> add_string_filter(params, "campaign_id", :campaign_id)
|> add_date_filter(params, "from_date", :from_date, ~T[00:00:00])
|> add_date_filter(params, "to_date", :to_date, ~T[23:59:59])
end
# Helper to add string filter
defp add_string_filter(
filters,
params,
param_key,
filter_key,
transform_fn \\ &Function.identity/1
) do
case params[param_key] do
value when is_binary(value) and value != "" ->
Map.put(filters, filter_key, transform_fn.(value))
_ ->
filters
end
end
# Helper to add date filter
defp add_date_filter(filters, params, param_key, filter_key, time) do
case params[param_key] do
date_str when is_binary(date_str) and date_str != "" ->
case Date.from_iso8601(date_str) do
{:ok, date} -> Map.put(filters, filter_key, DateTime.new!(date, time))
_ -> filters
end
_ ->
filters
end
end
# Generate CSV content for email logs
defp generate_logs_csv(logs) do
headers = get_logs_csv_headers()
rows = Enum.map(logs, &log_to_csv_row/1)
format_csv_output(headers, rows)
end
# Get CSV headers for email logs
defp get_logs_csv_headers do
[
"ID",
"Message ID",
"To",
"From",
"Subject",
"Status",
"Message Type",
"Provider",
"Sent At",
"Delivered At",
"Campaign",
"Template",
"Size (bytes)",
"Retry Count",
"Error Message"
]
end
# Convert email log to CSV row
defp log_to_csv_row(log) do
basic_fields = get_log_basic_fields(log)
datetime_fields = get_log_datetime_fields(log)
metadata_fields = get_log_metadata_fields(log)
basic_fields ++ datetime_fields ++ metadata_fields
end
# Get basic log fields
defp get_log_basic_fields(log) do
[
log.id,
log.message_id || "",
log.to || "",
log.from || "",
escape_csv_field(log.subject || ""),
log.status || "",
get_message_tag(log.message_tags) || "",
log.provider || ""
]
end
# Get datetime fields
defp get_log_datetime_fields(log) do
[
format_datetime_for_csv(log.sent_at),
format_datetime_for_csv(log.delivered_at)
]
end
# Get metadata fields
defp get_log_metadata_fields(log) do
[
log.campaign_id || "",
log.template_name || "",
log.size_bytes || "",
log.retry_count || 0,
escape_csv_field(log.error_message || "")
]
end
# Generate CSV content for metrics
defp generate_metrics_csv(metrics, group_by) do
headers = get_metrics_csv_headers(group_by)
rows = Enum.map(metrics, &metric_to_csv_row/1)
format_csv_output(headers, rows)
end
# Get CSV headers for metrics based on grouping
defp get_metrics_csv_headers("campaign") do
["Campaign" | get_base_metrics_headers()]
end
defp get_metrics_csv_headers("provider") do
["Provider" | get_base_metrics_headers()]
end
defp get_metrics_csv_headers(_) do
["Date" | get_base_metrics_headers()]
end
# Base metrics headers
defp get_base_metrics_headers do
[
"Total Sent",
"Delivered",
"Bounced",
"Opened",
"Clicked",
"Delivery Rate",
"Open Rate",
"Click Rate"
]
end
# Convert metric to CSV row
defp metric_to_csv_row(metric) do
[
metric[:label] || metric[:date] || "",
metric[:total_sent] || 0,
metric[:delivered] || 0,
metric[:bounced] || 0,
metric[:opened] || 0,
metric[:clicked] || 0,
"#{metric[:delivery_rate] || 0}%",
"#{metric[:open_rate] || 0}%",
"#{metric[:click_rate] || 0}%"
]
end
# Generate CSV content for email details
defp generate_email_details_csv(log, events) do
email_section = build_email_details_section(log)
events_section = build_events_section(events)
combine_csv_sections(email_section, events_section)
end
# Build email details section
defp build_email_details_section(log) do
headers = ["Field", "Value"]
rows = get_email_detail_rows(log)
[headers | rows]
end
# Get email detail rows
defp get_email_detail_rows(log) do
[
["ID", log.id],
["Message ID", log.message_id || ""],
["To", log.to || ""],
["From", log.from || ""],
["Subject", escape_csv_field(log.subject || "")],
["Status", log.status || ""],
["Provider", log.provider || ""],
["Sent At", format_datetime_for_csv(log.sent_at)],
["Delivered At", format_datetime_for_csv(log.delivered_at)],
["Campaign", log.campaign_id || ""],
["Template", log.template_name || ""],
["Size (bytes)", log.size_bytes || ""],
["Retry Count", log.retry_count || 0]
]
end
# Build events section
defp build_events_section([]), do: []
defp build_events_section(events) do
headers = ["Event Type", "Occurred At", "Details"]
rows = Enum.map(events, &event_to_csv_row/1)
[[], ["EVENTS"], headers | rows]
end
# Convert event to CSV row
defp event_to_csv_row(event) do
[
event.event_type || "",
format_datetime_for_csv(event.occurred_at),
escape_csv_field(event.event_data || "")
]
end
# Combine CSV sections
defp combine_csv_sections(email_section, events_section) do
(email_section ++ events_section)
|> Enum.map_join("\n", fn row ->
Enum.map_join(row, ",", &to_string/1)
end)
end
# Format CSV output from headers and rows
defp format_csv_output(headers, rows) do
[headers | rows]
|> Enum.map_join("\n", fn row ->
Enum.map_join(row, ",", &to_string/1)
end)
end
# Format datetime for CSV export
defp format_datetime_for_csv(nil), do: ""
defp format_datetime_for_csv(datetime) do
DateTime.to_iso8601(datetime)
end
# Escape CSV field value
defp escape_csv_field(nil), do: ""
defp escape_csv_field(value) when is_binary(value) do
if String.contains?(value, [",", "\"", "\n", "\r"]) do
"\"#{String.replace(value, "\"", "\"\"")}\""
else
value
end
end
defp escape_csv_field(value), do: to_string(value)
# Extract message tag from message_tags map
defp get_message_tag(message_tags) when is_map(message_tags) do
Map.get(message_tags, "email_type")
end
defp get_message_tag(_), do: nil
end