Skip to content

Commit

Permalink
Switch to SendGrid for email sending
Browse files Browse the repository at this point in the history
  • Loading branch information
ericmj committed Apr 21, 2022
1 parent 6b6e1f4 commit 04ba6d4
Show file tree
Hide file tree
Showing 16 changed files with 18 additions and 359 deletions.
2 changes: 1 addition & 1 deletion config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ config :hexpm, Hexpm.RepoBase,

config :sasl, sasl_error_logger: false

config :hexpm, Hexpm.Emails.Mailer, adapter: Hexpm.Emails.Bamboo.SESAdapter
config :hexpm, Hexpm.Emails.Mailer, adapter: Bamboo.SendGridAdapter

config :phoenix, :template_engines, md: HexpmWeb.MarkdownEngine

Expand Down
2 changes: 2 additions & 0 deletions config/runtime.exs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ if config_env() == :prod do
dashboard_user: System.fetch_env!("HEXPM_DASHBOARD_USER"),
dashboard_password: System.fetch_env!("HEXPM_DASHBOARD_PASSWORD")

config :hexpm, Hexpm.Emails.Mailer, api_key: System.fetch_env!("HEXPM_SENDGRID_API_KEY")

config :ex_aws,
access_key_id: System.fetch_env!("HEXPM_AWS_ACCESS_KEY_ID"),
secret_access_key: System.fetch_env!("HEXPM_AWS_ACCESS_KEY_SECRET")
Expand Down
2 changes: 1 addition & 1 deletion lib/hexpm/accounts/organizations.ex
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,6 @@ defmodule Hexpm.Accounts.Organizations do

defp send_invite_email(organization, user) do
Emails.organization_invite(organization, user)
|> Mailer.deliver_now_throttled()
|> Mailer.deliver_now!()
end
end
12 changes: 6 additions & 6 deletions lib/hexpm/accounts/users.ex
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ defmodule Hexpm.Accounts.Users do
case Repo.transaction(multi) do
{:ok, %{user: %{emails: [email]} = user}} ->
Emails.verification(user, email)
|> Mailer.deliver_now_throttled()
|> Mailer.deliver_now!()

{:ok, user}

Expand All @@ -77,7 +77,7 @@ defmodule Hexpm.Accounts.Users do
|> Repo.update!()

Emails.verification(user, email)
|> Mailer.deliver_now_throttled()
|> Mailer.deliver_now!()

email
end
Expand Down Expand Up @@ -153,7 +153,7 @@ defmodule Hexpm.Accounts.Users do
{:ok, %{user: user}} ->
user
|> Emails.password_changed()
|> Mailer.deliver_now_throttled()
|> Mailer.deliver_now!()

{:ok, user}

Expand Down Expand Up @@ -250,7 +250,7 @@ defmodule Hexpm.Accounts.Users do
|> Repo.transaction()

Emails.password_reset_request(user, reset)
|> Mailer.deliver_now_throttled()
|> Mailer.deliver_now!()

:ok
else
Expand Down Expand Up @@ -307,7 +307,7 @@ defmodule Hexpm.Accounts.Users do
user = Repo.preload(user, :emails, force: true)

Emails.verification(user, email)
|> Mailer.deliver_now_throttled()
|> Mailer.deliver_now!()

{:ok, user}

Expand Down Expand Up @@ -506,7 +506,7 @@ defmodule Hexpm.Accounts.Users do

true ->
Emails.verification(user, email)
|> Mailer.deliver_now_throttled()
|> Mailer.deliver_now!()

:ok
end
Expand Down
1 change: 0 additions & 1 deletion lib/hexpm/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ defmodule Hexpm.Application do
{Phoenix.PubSub, name: Hexpm.PubSub, adapter: Phoenix.PubSub.PG2},
HexpmWeb.RateLimitPubSub,
{PlugAttack.Storage.Ets, name: HexpmWeb.Plugs.Attack.Storage, clean_period: 60_000},
{Hexpm.Throttle, name: Hexpm.SESThrottle, rate: ses_rate(), unit: 1000},
{Hexpm.Billing.Report, name: Hexpm.Billing.Report, interval: 60_000},
goth_spec(),
HexpmWeb.Telemetry,
Expand Down
1 change: 1 addition & 0 deletions lib/hexpm/block_address/block_address.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ defmodule Hexpm.BlockAddress do
|> Hexpm.Repo.all()
|> Enum.map(&Hexpm.Utils.parse_ip_mask(&1.ip))
|> Enum.reject(fn {ip, _mask} -> ip == nil end)
|> Enum.uniq()

:ets.insert(@ets, {:allowed, Hexpm.CDN.public_ips()})
:ets.insert(@ets, {:disallowed, disallowed})
Expand Down
64 changes: 0 additions & 64 deletions lib/hexpm/emails/bamboo.ex
Original file line number Diff line number Diff line change
@@ -1,67 +1,3 @@
defmodule Hexpm.Emails.Bamboo.SESAdapter do
require Logger

@behaviour Bamboo.Adapter
@backoff 100
@backoff_times 5

def deliver(email, _config) do
if email.headers != %{} do
raise "headers not supported for Hexpm.Emails.Bamboo.SESAdapter"
end

destination = %{
to: emails(email.to),
cc: emails(email.cc),
bcc: emails(email.bcc)
}

message = ExAws.SES.build_message(email.html_body, email.text_body, email.subject)

request = ExAws.SES.send_email(destination, message, email(email.from), [])
send_email(request, email, 0)
end

def handle_config(config) do
config
end

def supports_attachments?() do
false
end

defp send_email(request, email, times) do
opts = [region: Application.fetch_env!(:hexpm, :ses_region)]

request
|> ExAws.request(opts)
|> maybe_retry(request, email, times)
end

defp maybe_retry({:error, {:http_error, 454, _body}} = error, request, email, times) do
if times > @backoff_times do
Logger.warn("AWS SES throttled ##{times}")
error
else
Process.sleep(@backoff * trunc(:math.pow(2, times)))
send_email(request, email, times + 1)
end
end

defp maybe_retry({:error, _} = error, _request, _email, _times) do
error
end

defp maybe_retry({:ok, _} = result, _request, _email, _times) do
result
end

defp emails(emails), do: emails |> List.wrap() |> Enum.map(&email/1)

defp email({name, email}), do: "#{name} <#{email}>"
defp email(email), do: email
end

defimpl Bamboo.Formatter, for: Hexpm.Accounts.User do
def format_email_address(user, _opts) do
{user.username, Hexpm.Accounts.User.email(user, :primary)}
Expand Down
33 changes: 0 additions & 33 deletions lib/hexpm/emails/mailer.ex
Original file line number Diff line number Diff line change
@@ -1,36 +1,3 @@
defmodule Hexpm.Emails.Mailer do
use Bamboo.Mailer, otp_app: :hexpm

def deliver_now_throttled(email) do
ses_rate = Hexpm.Application.ses_rate()

email
|> recipients()
|> recipient_chunks(ses_rate)
|> Enum.each(fn chunk ->
Hexpm.Throttle.wait(Hexpm.SESThrottle, length(chunk))

email
|> Bamboo.Email.to(chunk)
|> deliver_now!()
end)
end

defp recipient_chunks([], _limit) do
[]
end

defp recipient_chunks(recipients, :infinity) do
[recipients]
end

defp recipient_chunks(recipients, limit) do
Enum.chunk_every(recipients, limit)
end

defp recipients(email) do
email
|> Bamboo.Mailer.normalize_addresses()
|> Bamboo.Email.all_recipients()
end
end
2 changes: 1 addition & 1 deletion lib/hexpm/release_tasks/check_names.ex
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ defmodule Hexpm.ReleaseTasks.CheckNames do
defp send_email(candidates, threshold) do
candidates
|> Hexpm.Emails.typosquat_candidates(threshold)
|> Hexpm.Emails.Mailer.deliver_now_throttled()
|> Hexpm.Emails.Mailer.deliver_now!()
end

def find_candidates(threshold) do
Expand Down
4 changes: 2 additions & 2 deletions lib/hexpm/repository/owners.ex
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ defmodule Hexpm.Repository.Owners do
|> Repo.preload(organization: [organization_users: [user: :emails]])

Emails.owner_added(package, owners, user)
|> Mailer.deliver_now_throttled()
|> Mailer.deliver_now!()

{:ok, %{owner | user: user}}

Expand Down Expand Up @@ -127,7 +127,7 @@ defmodule Hexpm.Repository.Owners do
|> Repo.preload(organization: [users: :emails])

Emails.owner_removed(package, owners, owner.user)
|> Mailer.deliver_now_throttled()
|> Mailer.deliver_now!()

:ok
end
Expand Down
6 changes: 3 additions & 3 deletions lib/hexpm/repository/package_reports.ex
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ defmodule Hexpm.Repository.PackageReports do
package_report.id,
package_report.inserted_at
)
|> Mailer.deliver_now_throttled()
|> Mailer.deliver_now!()
end

defp email_new_comment(comment, report, user) do
Expand All @@ -129,7 +129,7 @@ defmodule Hexpm.Repository.PackageReports do
report.id,
comment.inserted_at
)
|> Mailer.deliver_now_throttled()
|> Mailer.deliver_now!()
end

defp email_state_change(package_report, user) do
Expand All @@ -140,6 +140,6 @@ defmodule Hexpm.Repository.PackageReports do
package_report.state,
package_report.updated_at
)
|> Mailer.deliver_now_throttled()
|> Mailer.deliver_now!()
end
end
2 changes: 1 addition & 1 deletion lib/hexpm/repository/releases.ex
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ defmodule Hexpm.Repository.Releases do
Hexpm.Repo.all(assoc(package, :owners))
|> Hexpm.Repo.preload([:emails, organization: [users: :emails]])
|> Emails.package_published(publisher, package.name, release.version)
|> Mailer.deliver_now_throttled()
|> Mailer.deliver_now!()
end

if Mix.env() == :test do
Expand Down
102 changes: 0 additions & 102 deletions lib/hexpm/throttle.ex

This file was deleted.

1 change: 0 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ defmodule Hexpm.MixProject do
{:ecto_sql, "~> 3.0"},
{:ecto, "~> 3.0"},
{:ex_aws_s3, "~> 2.0"},
{:ex_aws_ses, "~> 2.0"},
{:ex_aws, "~> 2.0"},
{:ex_machina, "~> 2.0"},
{:eqrcode, "~> 0.1.6"},
Expand Down
1 change: 0 additions & 1 deletion mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
"eqrcode": {:hex, :eqrcode, "0.1.10", "6294fece9d68ad64eef1c3c92cf111cfd6469f4fbf230a2d4cc905a682178f3f", [:mix], [], "hexpm", "da30e373c36a0fd37ab6f58664b16029919896d6c45a68a95cc4d713e81076f1"},
"ex_aws": {:hex, :ex_aws, "2.2.7", "a66277ea39b043931442cf37a3b44467e83ffb8743e9a8bc5be0ad793664bbf0", [:mix], [{:configparser_ex, "~> 4.0", [hex: :configparser_ex, repo: "hexpm", optional: true]}, {:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:jsx, "~> 2.8 or ~> 3.0", [hex: :jsx, repo: "hexpm", optional: true]}, {:mime, "~> 1.2 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:sweet_xml, "~> 0.7", [hex: :sweet_xml, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "85e08e0db506062171534626266de34804c1871f529b7d5d63267339ac30a601"},
"ex_aws_s3": {:hex, :ex_aws_s3, "2.3.1", "86061eee904edcdbbd3ecf19028ad96486f218e9ab000230644fe9dff57a2f0a", [:mix], [{:ex_aws, "~> 2.0", [hex: :ex_aws, repo: "hexpm", optional: false]}, {:sweet_xml, ">= 0.0.0", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm", "2585322026df4fede5e1b143b7cd990eef15851153b74ac4e4c6fc8058b5ba18"},
"ex_aws_ses": {:hex, :ex_aws_ses, "2.3.0", "90187c5e1c7cc11c5907e6c4b3d0683841115c6f8e14de6ac2e5bf4a48912fe1", [:mix], [{:ex_aws, "~> 2.0", [hex: :ex_aws, repo: "hexpm", optional: false]}], "hexpm", "b6765bb5ca1b0cbc928d0c8529f882e11de1b03983b4a7007557b59cb0b9ae79"},
"ex_machina": {:hex, :ex_machina, "2.7.0", "b792cc3127fd0680fecdb6299235b4727a4944a09ff0fa904cc639272cd92dc7", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm", "419aa7a39bde11894c87a615c4ecaa52d8f107bbdd81d810465186f783245bf8"},
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
"goth": {:hex, :goth, "1.3.0-rc.3", "734a0a381c29df82af33039874aac02ad292199e155a25ad4320872788209dee", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:jose, "~> 1.10", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "ab61d5ab6f59a5ea0404d332c9ad3f2463ae4072aabe4f9ef96d624e271254af"},
Expand Down
Loading

0 comments on commit 04ba6d4

Please sign in to comment.