add swoosh and oban

This commit is contained in:
2022-02-08 19:59:23 -05:00
parent acf64cee18
commit a39c3da351
26 changed files with 365 additions and 84 deletions

View File

@ -5,7 +5,8 @@ defmodule Cannery.Accounts do
import Ecto.Query, warn: false
alias Cannery.Repo
alias Cannery.Accounts.{User, UserNotifier, UserToken}
alias Cannery.Accounts.{User, UserToken}
alias Cannery.Mailer
alias Ecto.{Changeset, Multi}
## Database getters
@ -208,7 +209,7 @@ defmodule Cannery.Accounts do
{encoded_token, user_token} = UserToken.build_email_token(user, "change:#{current_email}")
Repo.insert!(user_token)
UserNotifier.deliver_update_email_instructions(user, update_email_url_fun.(encoded_token))
Mailer.deliver_update_email_instructions(user, update_email_url_fun.(encoded_token))
end
@doc """
@ -319,7 +320,7 @@ defmodule Cannery.Accounts do
else
{encoded_token, user_token} = UserToken.build_email_token(user, "confirm")
Repo.insert!(user_token)
UserNotifier.deliver_confirmation_instructions(user, confirmation_url_fun.(encoded_token))
Mailer.deliver_confirmation_instructions(user, confirmation_url_fun.(encoded_token))
end
end
@ -364,7 +365,7 @@ defmodule Cannery.Accounts do
when is_function(reset_password_url_fun, 1) do
{encoded_token, user_token} = UserToken.build_email_token(user, "reset_password")
Repo.insert!(user_token)
UserNotifier.deliver_reset_password_instructions(user, reset_password_url_fun.(encoded_token))
Mailer.deliver_reset_password_instructions(user, reset_password_url_fun.(encoded_token))
end
@doc """

View File

@ -0,0 +1,54 @@
defmodule Cannery.Email do
@moduledoc """
Emails that can be sent using Swoosh.
You can find the base email templates at
`lib/cannery_web/templates/layout/email.html.heex` for html emails and
`lib/cannery_web/templates/layout/email.txt.heex` for text emails.
"""
use Phoenix.Swoosh, view: Cannery.EmailView, layout: {Cannery.LayoutView, :email}
import CanneryWeb.Gettext
alias Cannery.Accounts.User
alias CanneryWeb.EmailView
@typedoc """
Represents an HTML and text body email that can be sent
"""
@type t() :: Swoosh.Email.t()
@spec base_email(User.t(), String.t()) :: t()
defp base_email(%User{email: email}, subject) do
new()
|> to(email)
|> from({
Application.get_env(:cannery, Cannery.Mailer)[:email_name],
Application.get_env(:cannery, Cannery.Mailer)[:email_from]
})
|> subject(subject)
end
@spec welcome_email(User.t(), String.t()) :: t()
def welcome_email(user, url) do
user
|> base_email(dgettext("emails", "Confirm your %{name} account", name: "Cannery"))
|> render_body("confirm_email.html", %{user: user, url: url})
|> text_body(EmailView.render("confirm_email.txt", %{user: user, url: url}))
end
@spec reset_password_email(User.t(), String.t()) :: t()
def reset_password_email(user, url) do
user
|> base_email(dgettext("emails", "Reset your %{name} password", name: "Cannery"))
|> render_body("reset_password.html", %{user: user, url: url})
|> text_body(EmailView.render("reset_password.txt", %{user: user, url: url}))
end
@spec update_email(User.t(), String.t()) :: t()
def update_email(user, url) do
user
|> base_email(dgettext("emails", "Update your %{name} email", name: "Cannery"))
|> render_body("update_email.html", %{user: user, url: url})
|> text_body(EmailView.render("update_email.txt", %{user: user, url: url}))
end
end

View File

@ -0,0 +1,9 @@
defmodule Cannery.EmailWorker do
use Oban.Worker, queue: :mailers
alias Cannery.Mailer
@impl Oban.Worker
def perform(%Oban.Job{args: email}) do
email |> Mailer.deliver()
end
end

View File

@ -1,77 +0,0 @@
defmodule Cannery.Accounts.UserNotifier do
@moduledoc """
Contains all user emails and notifications
"""
# For simplicity, this module simply logs messages to the terminal.
# You should replace it by a proper email or notification tool, such as:
#
# * Swoosh - https://hexdocs.pm/swoosh
# * Bamboo - https://hexdocs.pm/bamboo
#
defp deliver(to, body) do
require Logger
Logger.debug(body)
{:ok, %{to: to, body: body}}
end
@doc """
Deliver instructions to confirm account.
"""
def deliver_confirmation_instructions(user, url) do
deliver(user.email, """
==============================
Hi #{user.email},
You can confirm your account by visiting the URL below:
#{url}
If you didn't create an account with us, please ignore this.
==============================
""")
end
@doc """
Deliver instructions to reset a user password.
"""
def deliver_reset_password_instructions(user, url) do
deliver(user.email, """
==============================
Hi #{user.email},
You can reset your password by visiting the URL below:
#{url}
If you didn't request this change, please ignore this.
==============================
""")
end
@doc """
Deliver instructions to update a user email.
"""
def deliver_update_email_instructions(user, url) do
deliver(user.email, """
==============================
Hi #{user.email},
You can change your email by visiting the URL below:
#{url}
If you didn't request this change, please ignore this.
==============================
""")
end
end

View File

@ -15,7 +15,9 @@ defmodule Cannery.Application do
# Start the PubSub system
{Phoenix.PubSub, name: Cannery.PubSub},
# Start the Endpoint (http/https)
CanneryWeb.Endpoint
CanneryWeb.Endpoint,
# Add Oban
{Oban, oban_config()}
# Start a worker by calling: Cannery.Worker.start_link(arg)
# {Cannery.Worker, arg}
]
@ -39,4 +41,8 @@ defmodule Cannery.Application do
CanneryWeb.Endpoint.config_change(changed, removed)
:ok
end
defp oban_config() do
Application.fetch_env!(:cannery, Oban)
end
end

View File

@ -4,4 +4,30 @@ defmodule Cannery.Mailer do
"""
use Swoosh.Mailer, otp_app: :cannery
alias Cannery.{Accounts.User, Email, EmailWorker}
alias Oban.Job
@doc """
Deliver instructions to confirm account.
"""
@spec deliver_confirmation_instructions(User.t(), String.t()) :: {:ok, Job.t()}
def deliver_confirmation_instructions(user, url) do
{:ok, Email.welcome_email(user, url) |> EmailWorker.new() |> Oban.insert!()}
end
@doc """
Deliver instructions to reset a user password.
"""
@spec deliver_reset_password_instructions(User.t(), String.t()) :: {:ok, Job.t()}
def deliver_reset_password_instructions(user, url) do
{:ok, Email.reset_password_email(user, url) |> EmailWorker.new() |> Oban.insert!()}
end
@doc """
Deliver instructions to update a user email.
"""
@spec deliver_update_email_instructions(User.t(), String.t()) :: {:ok, Job.t()}
def deliver_update_email_instructions(user, url) do
{:ok, Email.update_email(user, url) |> EmailWorker.new() |> Oban.insert!()}
end
end

View File

@ -0,0 +1,12 @@
<%= dgettext("emails", "Hi %{email},", email: @user.email) %>
<%= dgettext("emails", "Welcome to %{name}!", name: "Cannery") %>
<%= dgettext("emails", "You can confirm your account by visiting the URL below:") %>
<%= @url %>
<%= dgettext("emails",
"If you didn't create an account at %{url}, please ignore this.",
url: Routes.live_url(Endpoint, HomeLive)) %>

View File

@ -0,0 +1,12 @@
<%= dgettext("emails", "Hi %{email},", email: @user.email) %>
<%= dgettext("emails", "Welcome to %{name}%!", name: "Cannery") %>
<%= dgettext("emails", "You can confirm your account by visiting the URL below:") %>
<%= @url %>
<%= dgettext("emails",
"If you didn't create an account at %{url}, please ignore this.",
url: Routes.live_url(Endpoint, HomeLive)) %>

View File

@ -0,0 +1,10 @@
<%= dgettext("emails", "Hi %{email},", email: @user.email) %>
<%= dgettext("emails", "You can reset your password by visiting the URL below:") %>
<%= @url %>
<%= dgettext("emails",
"If you didn't request this change from %{url}, please ignore this.",
url: Routes.live_url(Endpoint, HomeLive)) %>

View File

@ -0,0 +1,10 @@
<%= dgettext("emails", "Hi %{email},", email: @user.email) %>
<%= dgettext("emails", "You can reset your password by visiting the URL below:") %>
<%= @url %>
<%= dgettext("emails",
"If you didn't request this change from %{url}, please ignore this.",
url: Routes.live_url(Endpoint, HomeLive)) %>

View File

@ -0,0 +1,10 @@
<%= dgettext("emails", "Hi %{email},", email: @user.email) %>
<%= dgettext("emails", "You can change your email by visiting the URL below:") %>
<%= @url %>
<%= dgettext("emails",
"If you didn't request this change from %{url}, please ignore this.",
url: Routes.live_url(Endpoint, HomeLive)) %>

View File

@ -0,0 +1,10 @@
<%= dgettext("emails", "Hi %{email},", email: @user.email) %>
<%= dgettext("emails", "You can change your email by visiting the URL below:") %>
<%= @url %>
<%= dgettext("emails",
"If you didn't request this change from %{url}, please ignore this.",
url: Routes.live_url(Endpoint, HomeLive)) %>

View File

@ -0,0 +1,16 @@
<html>
<head>
<title>
<%= @email.subject %>
</title>
</head>
<body>
<%= @inner_content %>
<hr style="border-width: 1px; border-color: rgb(212, 212, 216); width: 100%; max-width: 42rem;">
<a href={Routes.live_url(Endpoint, HomeLive)}>
<%= dgettext("emails", "This email was sent from %{name}", name: "Cannery") %>
</a>
</body>
</html>

View File

@ -0,0 +1,13 @@
<%= @email.subject %>
====================
<%= @inner_content %>
=====================
<%= dgettext("emails",
"This email was sent from %{name} at %{url}",
name: "Cannery",
url: Routes.live_url(Endpoint, HomeLive)) %>

View File

@ -0,0 +1,8 @@
defmodule CanneryWeb.EmailView do
@moduledoc """
A view for email-related helper functions
"""
alias CanneryWeb.{Endpoint, HomeLive}
use CanneryWeb, :view
end