Skip to content

Commit

Permalink
Switch from Bamboo to Swoosh for email delivery
Browse files Browse the repository at this point in the history
  • Loading branch information
ku1ik committed May 21, 2024
1 parent 3f3356d commit ac895d2
Show file tree
Hide file tree
Showing 20 changed files with 125 additions and 115 deletions.
4 changes: 2 additions & 2 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ config :mime, :types, %{
"application/x-asciicast" => ["cast"]
}

config :asciinema, Asciinema.Emails.Mailer, adapter: Swoosh.Adapters.Local

config :sentry,
dsn: "https://public:[email protected]/1",
environment_name: config_env(),
Expand All @@ -85,8 +87,6 @@ config :asciinema, Asciinema.FileStore.Local, path: "uploads/"

config :asciinema, Asciinema.FileCache, path: "cache/"

config :asciinema, Asciinema.Emails.Mailer, adapter: Bamboo.LocalAdapter

config :asciinema, :png_generator, Asciinema.PngGenerator.Rsvg

config :asciinema, Asciinema.PngGenerator.Rsvg,
Expand Down
4 changes: 2 additions & 2 deletions config/runtime.exs
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,8 @@ if config_env() in [:prod, :dev] do

if smtp_host = env.("SMTP_HOST") do
config :asciinema, Asciinema.Emails.Mailer,
adapter: Bamboo.SMTPAdapter,
server: smtp_host,
adapter: Swoosh.Adapters.SMTP,
relay: smtp_host,
port: String.to_integer(env.("SMTP_PORT") || "587")

if username = env.("SMTP_USERNAME") do
Expand Down
5 changes: 3 additions & 2 deletions config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ if db_url = System.get_env("TEST_DATABASE_URL") do
System.put_env("DATABASE_URL", db_url)
end

# In test we don't send emails.
config :asciinema, Asciinema.Emails.Mailer, adapter: Swoosh.Adapters.Test

# Print only errors during test
config :logger, level: :error

Expand All @@ -37,6 +40,4 @@ config :asciinema, :snapshot_updater, Asciinema.Recordings.SnapshotUpdater.Noop

config :asciinema, Oban, testing: :manual

config :asciinema, Asciinema.Emails.Mailer, adapter: Bamboo.TestAdapter

config :asciinema, Asciinema.Telemetry, enabled: false
2 changes: 1 addition & 1 deletion lib/asciinema/emails.ex
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ defmodule Asciinema.Emails do
end

defp deliver(email) do
with {:ok, _email} <- Mailer.deliver_now(email) do
with {:ok, _metadata} <- Mailer.deliver(email) do
:ok
end
end
Expand Down
114 changes: 92 additions & 22 deletions lib/asciinema/emails/email.ex
Original file line number Diff line number Diff line change
@@ -1,32 +1,80 @@
defmodule Asciinema.Emails.Email do
use Bamboo.Phoenix, view: AsciinemaWeb.EmailView
import Bamboo.Email
use AsciinemaWeb, :html
import Swoosh.Email

def signup_email(email_address, token) do
hostname = instance_hostname()

base_email()
|> to(email_address)
|> subject("Welcome to #{instance_hostname()}")
|> render("signup.text", token: token)
|> render("signup.html", token: token)
|> fix_text_body()
|> subject("Welcome to #{hostname}")
|> body(signup_email_html(%{token: token, hostname: hostname}))
end

defp signup_email_html(assigns) do
~H"""
<.layout>
<p>Welcome to <%= @hostname %>!</p>
<p>Open the following link to setup your account:</p>
<p><a href={url(~p"/users/new?t=#{@token}")}><%= url(~p"/users/new?t=#{@token}") %></a></p>
<p>
<br />
If you did not initiate this request, just ignore this email. The request will expire shortly.
</p>
</.layout>
"""
end

def login_email(email_address, token) do
hostname = instance_hostname()

base_email()
|> to(email_address)
|> subject("Login to #{instance_hostname()}")
|> render("login.text", token: token)
|> render("login.html", token: token)
|> fix_text_body()
|> subject("Login to #{hostname}")
|> body(login_email_html(%{token: token, hostname: hostname}))
end

defp login_email_html(assigns) do
~H"""
<.layout>
<p>Welcome back!</p>
<p>Open the following link to log in to your <%= @hostname %> account:</p>
<p><a href={url(~p"/session/new?t=#{@token}")}><%= url(~p"/session/new?t=#{@token}") %></a></p>
<p>
<br />
If you did not initiate this request, just ignore this email. The request will expire shortly.
</p>
</.layout>
"""
end

def account_deletion_email(email_address, token) do
base_email()
|> to(email_address)
|> subject("Account deletion")
|> render("account_deletion.text", token: token)
|> render("account_deletion.html", token: token)
|> fix_text_body()
|> body(account_deletion_email_html(%{token: token, hostname: instance_hostname()}))
end

defp account_deletion_email_html(assigns) do
~H"""
<.layout>
<p>It seems you have requested deletion of your <%= @hostname %> account.</p>
<p>If you wish to proceed, open the following link in your browser:</p>
<p><a href={url(~p"/user/delete?t=#{@token}")}><%= url(~p"/user/delete?t=#{@token}") %></a></p>
<p>
<br /> If you did not initiate this request, just ignore this email.
</p>
</.layout>
"""
end

def test_email(email_address) do
Expand All @@ -37,12 +85,38 @@ defmodule Asciinema.Emails.Email do
end

defp base_email do
new_email()
new()
|> from({"asciinema", from_address()})
|> put_header("Date", Timex.format!(Timex.now(), "{RFC1123}"))
|> put_header("Reply-To", reply_to_address())
|> put_html_layout({AsciinemaWeb.LayoutView, "email.html"})
|> assign(:hostname, instance_hostname())
|> header("Date", Timex.format!(Timex.now(), "{RFC1123}"))
|> reply_to(reply_to_address())
end

defp body(email, template) do
html =
template
|> Phoenix.HTML.Safe.to_iodata()
|> IO.iodata_to_binary()

html_body(email, html)
end

defp layout(assigns) do
~H"""
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<style>
body {
font-family: monospace;
}
</style>
</head>
<body>
<%= render_slot(@inner_block) %>
</body>
</html>
"""
end

defp from_address do
Expand All @@ -56,8 +130,4 @@ defmodule Asciinema.Emails.Email do
defp instance_hostname do
System.get_env("URL_HOST") || "localhost"
end

defp fix_text_body(email) do
%{email | text_body: String.replace(email.text_body, "\n", "\r\n")}
end
end
2 changes: 1 addition & 1 deletion lib/asciinema/emails/mailer.ex
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
defmodule Asciinema.Emails.Mailer do
use Bamboo.Mailer, otp_app: :asciinema
use Swoosh.Mailer, otp_app: :asciinema
end
7 changes: 4 additions & 3 deletions lib/asciinema_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,10 @@ defmodule AsciinemaWeb.Router do
end
end

scope "/dev" do
if Application.compile_env(:asciinema, :dev_routes) do
forward "/mailbox", Bamboo.SentEmailViewerPlug
if Application.compile_env(:asciinema, :dev_routes) do
scope "/dev" do
pipe_through :browser
forward "/mailbox", Plug.Swoosh.MailboxPreview
end
end
end
Expand Down
10 changes: 0 additions & 10 deletions lib/asciinema_web/templates/email/account_deletion.html.eex

This file was deleted.

8 changes: 0 additions & 8 deletions lib/asciinema_web/templates/email/account_deletion.text.eex

This file was deleted.

10 changes: 0 additions & 10 deletions lib/asciinema_web/templates/email/login.html.eex

This file was deleted.

8 changes: 0 additions & 8 deletions lib/asciinema_web/templates/email/login.text.eex

This file was deleted.

10 changes: 0 additions & 10 deletions lib/asciinema_web/templates/email/signup.html.eex

This file was deleted.

8 changes: 0 additions & 8 deletions lib/asciinema_web/templates/email/signup.text.eex

This file was deleted.

3 changes: 0 additions & 3 deletions lib/asciinema_web/views/email_view.ex

This file was deleted.

6 changes: 2 additions & 4 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,6 @@ defmodule Asciinema.MixProject do
# Type `mix help deps` for examples and options.
defp deps do
[
{:bamboo, "~> 2.2"},
{:bamboo_phoenix, "~> 1.0"},
{:bamboo_ses, "~> 0.4.2"},
{:bamboo_smtp, "~> 4.2"},
{:briefly, "~> 0.3"},
{:credo, "~> 1.7", only: [:dev, :test], runtime: false},
{:earmark, "~> 1.4"},
Expand All @@ -51,6 +47,7 @@ defmodule Asciinema.MixProject do
{:ex_aws, "~> 2.2"},
{:ex_aws_s3, "~> 2.1"},
{:ex_machina, "~> 2.4", only: :test},
{:gen_smtp, "~> 1.2"},
{:gettext, "~> 0.20"},
{:hackney, "~> 1.18"},
{:horde, "~> 0.8.7"},
Expand Down Expand Up @@ -80,6 +77,7 @@ defmodule Asciinema.MixProject do
{:scrivener_ecto, "~> 2.4"},
{:scrivener_html, "~> 1.8"},
{:sentry, "~> 8.0"},
{:swoosh, "~> 1.16"},
{:telemetry_metrics, "~> 0.6"},
{:telemetry_poller, "~> 1.0"},
{:timex, "~> 3.7"}
Expand Down
5 changes: 1 addition & 4 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
%{
"bamboo": {:hex, :bamboo, "2.2.0", "f10a406d2b7f5123eb1f02edfa043c259db04b47ab956041f279eaac776ef5ce", [:mix], [{:hackney, ">= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.4", [hex: :mime, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "8c3b14ba7d2f40cb4be04128ed1e2aff06d91d9413d38bafb4afccffa3ade4fc"},
"bamboo_phoenix": {:hex, :bamboo_phoenix, "1.0.0", "f3cc591ffb163ed0bf935d256f1f4645cd870cf436545601215745fb9cc9953f", [:mix], [{:bamboo, ">= 2.0.0", [hex: :bamboo, repo: "hexpm", optional: false]}, {:phoenix, ">= 1.3.0", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "6db88fbb26019c84a47994bb2bd879c0887c29ce6c559bc6385fd54eb8b37dee"},
"bamboo_ses": {:hex, :bamboo_ses, "0.4.2", "e148a0ae17f8223b830029c2e81b2ba18220aa7378531ef1f50c4212fbd9ddb1", [:mix], [{:bamboo, "~> 2.0", [hex: :bamboo, repo: "hexpm", optional: false]}, {:ex_aws, "~> 2.4.1", [hex: :ex_aws, repo: "hexpm", optional: false]}, {:gen_smtp, "~> 1.2.0", [hex: :gen_smtp, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "feb609b57316d335b217937f66cfc7c1ebe37ec481bebe97fcd5da5f31171808"},
"bamboo_smtp": {:hex, :bamboo_smtp, "4.2.2", "e9f57a2300df9cb496c48751bd7668a86a2b89aa2e79ccaa34e0c46a5f64c3ae", [:mix], [{:bamboo, "~> 2.2.0", [hex: :bamboo, repo: "hexpm", optional: false]}, {:gen_smtp, "~> 1.2.0", [hex: :gen_smtp, repo: "hexpm", optional: false]}], "hexpm", "28cac2ec8adaae02aed663bf68163992891a3b44cfd7ada0bebe3e09bed7207f"},
"briefly": {:hex, :briefly, "0.4.1", "c90c0511e64bde1fe8da7e244e14acf5bc78c3f6d033db778205e1fa2feafa5c", [:mix], [], "hexpm", "fc0cafcd19c4ed0d0906ae5cf627cc6ce76b8652a160c6bde0ab9d77304ebb0a"},
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
"castore": {:hex, :castore, "1.0.6", "ffc42f110ebfdafab0ea159cd43d31365fa0af0ce4a02ecebf1707ae619ee727", [:mix], [], "hexpm", "374c6e7ca752296be3d6780a6d5b922854ffcc74123da90f2f328996b962d33a"},
Expand Down Expand Up @@ -68,6 +64,7 @@
"scrivener_html": {:hex, :scrivener_html, "1.8.1", "d2d287cac72bd5405f1d1afe897fffd0f6ff2f2c8874384dda3bea74397e78d6", [:mix], [{:phoenix, ">= 1.0.0 and < 1.5.0", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.2", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.1", [hex: :plug, repo: "hexpm", optional: false]}, {:scrivener, "~> 1.2 or ~> 2.0", [hex: :scrivener, repo: "hexpm", optional: false]}], "hexpm", "04f1bdc463f770a046db0377891f4ee85022259bdb4a9142c64848f8714a3f38"},
"sentry": {:hex, :sentry, "8.0.6", "c8de1bf0523bc120ec37d596c55260901029ecb0994e7075b0973328779ceef7", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.6", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, "~> 2.3", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm", "051a2d0472162f3137787c7c9d6e6e4ef239de9329c8c45b1f1bf1e9379e1883"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"},
"swoosh": {:hex, :swoosh, "1.16.7", "9dd0c172b4519a023f58e94d3ea79480b469dd4c0cd5369fabfbfd2e39bf5545", [:mix], [{:bandit, ">= 1.0.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mua, "~> 0.1.0", [hex: :mua, repo: "hexpm", optional: true]}, {:multipart, "~> 0.4", [hex: :multipart, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:req, "~> 0.4 or ~> 1.0", [hex: :req, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "21073982816cff3410e90c0d80ebfd5a0bf4839c7b39db20bc69a6df123bbf35"},
"table_rex": {:hex, :table_rex, "3.1.1", "0c67164d1714b5e806d5067c1e96ff098ba7ae79413cc075973e17c38a587caa", [:mix], [], "hexpm", "678a23aba4d670419c23c17790f9dcd635a4a89022040df7d5d772cb21012490"},
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
"telemetry_metrics": {:hex, :telemetry_metrics, "0.6.1", "315d9163a1d4660aedc3fee73f33f1d355dcc76c5c3ab3d59e76e3edf80eef1f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7be9e0871c41732c233be71e4be11b96e56177bf15dde64a8ac9ce72ac9834c6"},
Expand Down
4 changes: 2 additions & 2 deletions rel/overlays/etc/custom.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import Config
## Use Mailgun for mail delivery:

# config :asciinema, Asciinema.Mailer,
# adapter: Bamboo.SMTPAdapter,
# server: "smtp.mailgun.org",
# adapter: Swoosh.Adapters.SMTP,
# relay: "smtp.mailgun.org",
# port: 587,
# username: "[email protected]",
# password: "mailgun-password",
Expand Down
14 changes: 7 additions & 7 deletions test/asciinema_test.exs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
defmodule AsciinemaTest do
import Asciinema.Factory
import Bamboo.Test
import Swoosh.TestAssertions
use Asciinema.DataCase
use Oban.Testing, repo: Asciinema.Repo
alias Asciinema.Recordings
Expand Down Expand Up @@ -29,15 +29,15 @@ defmodule AsciinemaTest do

assert Asciinema.send_login_email("[email protected]") == :ok

assert_email_delivered_with(to: [{nil, "[email protected]"}], subject: "Login to localhost")
assert_email_sent(to: [{nil, "[email protected]"}], subject: "Login to localhost")
end

test "existing user, by username" do
insert(:user, username: "foobar", email: "[email protected]")

assert Asciinema.send_login_email("foobar") == :ok

assert_email_delivered_with(
assert_email_sent(
to: [{nil, "[email protected]"}],
subject: "Login to localhost"
)
Expand All @@ -46,25 +46,25 @@ defmodule AsciinemaTest do
test "non-existing user, by email" do
assert Asciinema.send_login_email("[email protected]") == :ok

assert_email_delivered_with(to: [{nil, "[email protected]"}], subject: "Welcome to localhost")
assert_email_sent(to: [{nil, "[email protected]"}], subject: "Welcome to localhost")
end

test "non-existing user, by email, when sign up is disabled" do
assert Asciinema.send_login_email("[email protected]", false) == {:error, :user_not_found}

assert_no_emails_delivered()
assert_no_email_sent()
end

test "non-existing user, by email, when email is invalid" do
assert Asciinema.send_login_email("new@") == {:error, :email_invalid}

assert_no_emails_delivered()
assert_no_email_sent()
end

test "non-existing user, by username" do
assert Asciinema.send_login_email("idontexist") == {:error, :user_not_found}

assert_no_emails_delivered()
assert_no_email_sent()
end
end

Expand Down
Loading

0 comments on commit ac895d2

Please sign in to comment.