fix registration email

This commit is contained in:
2022-02-11 22:37:41 -05:00
parent d685ca5c74
commit e5a5b4299c
11 changed files with 53 additions and 73 deletions

View File

@ -4,10 +4,10 @@ defmodule Cannery.Accounts do
"""
import Ecto.Query, warn: false
alias Cannery.Repo
alias Cannery.{Mailer, Repo}
alias Cannery.Accounts.{User, UserToken}
alias Cannery.Mailer
alias Ecto.{Changeset, Multi}
alias Oban.Job
## Database getters
@ -202,8 +202,7 @@ defmodule Cannery.Accounts do
{:ok, %{to: ..., body: ...}}
"""
@spec deliver_update_email_instructions(User.t(), String.t(), function) ::
{:ok, any()} | {:error, atom()}
@spec deliver_update_email_instructions(User.t(), String.t(), function) :: Job.t()
def deliver_update_email_instructions(user, current_email, update_email_url_fun)
when is_function(update_email_url_fun, 1) do
{encoded_token, user_token} = UserToken.build_email_token(user, "change:#{current_email}")
@ -319,8 +318,7 @@ defmodule Cannery.Accounts do
{:error, :already_confirmed}
"""
@spec deliver_user_confirmation_instructions(User.t(), function) ::
{:ok, any()} | {:error, atom()}
@spec deliver_user_confirmation_instructions(User.t(), function) :: Job.t()
def deliver_user_confirmation_instructions(user, confirmation_url_fun)
when is_function(confirmation_url_fun, 1) do
if user.confirmed_at do
@ -367,8 +365,7 @@ defmodule Cannery.Accounts do
{:ok, %{to: ..., body: ...}}
"""
@spec deliver_user_reset_password_instructions(User.t(), function()) ::
{:ok, any()} | {:error, atom()}
@spec deliver_user_reset_password_instructions(User.t(), function()) :: Job.t()
def deliver_user_reset_password_instructions(user, reset_password_url_fun)
when is_function(reset_password_url_fun, 1) do
{encoded_token, user_token} = UserToken.build_email_token(user, "reset_password")

View File

@ -7,7 +7,7 @@ defmodule Cannery.Email do
`lib/cannery_web/templates/layout/email.txt.heex` for text emails.
"""
use Phoenix.Swoosh, view: Cannery.EmailView, layout: {Cannery.LayoutView, :email}
use Phoenix.Swoosh, view: CanneryWeb.EmailView, layout: {CanneryWeb.LayoutView, :email}
import CanneryWeb.Gettext
alias Cannery.Accounts.User
alias CanneryWeb.EmailView
@ -19,33 +19,27 @@ defmodule Cannery.Email do
@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)
from = Application.get_env(:cannery, Cannery.Mailer)[:email_from] || "noreply@localhost"
name = Application.get_env(:cannery, Cannery.Mailer)[:email_name]
new() |> to(email) |> from({name, from}) |> subject(subject)
end
@spec welcome_email(User.t(), String.t()) :: t()
def welcome_email(user, url) do
@spec generate_email(String.t(), User.t(), attrs :: map()) :: t()
def generate_email("welcome", user, %{"url" => 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
def generate_email("reset_password", user, %{"url" => 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
def generate_email("update_email", user, %{"url" => url}) do
user
|> base_email(dgettext("emails", "Update your %{name} email", name: "Cannery"))
|> render_body("update_email.html", %{user: user, url: url})

View File

@ -4,10 +4,10 @@ defmodule Cannery.EmailWorker do
"""
use Oban.Worker, queue: :mailers
alias Cannery.Mailer
alias Cannery.{Accounts, Mailer, Email}
@impl Oban.Worker
def perform(%Oban.Job{args: email}) do
email |> Mailer.deliver()
def perform(%Oban.Job{args: %{"email" => email, "user_id" => user_id, "attrs" => attrs}}) do
Email.generate_email(email, user_id |> Accounts.get_user!(), attrs) |> Mailer.deliver()
end
end

View File

@ -1,33 +1,40 @@
defmodule Cannery.Mailer do
@moduledoc """
Mailer adapter for emails
Since emails are loaded as Oban jobs, the `:attrs` map must be serializable to
json with Jason, which restricts the use of structs.
"""
use Swoosh.Mailer, otp_app: :cannery
alias Cannery.{Accounts.User, Email, EmailWorker}
alias Cannery.{Accounts.User, 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!()}
@spec deliver_confirmation_instructions(User.t(), String.t()) :: Job.t()
def deliver_confirmation_instructions(%User{id: user_id}, url) do
%{email: :welcome, user_id: user_id, attrs: %{url: 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!()}
@spec deliver_reset_password_instructions(User.t(), String.t()) :: Job.t()
def deliver_reset_password_instructions(%User{id: user_id}, url) do
%{email: :reset_password, user_id: user_id, attrs: %{url: 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!()}
@spec deliver_update_email_instructions(User.t(), String.t()) :: Job.t()
def deliver_update_email_instructions(%User{id: user_id}, url) do
%{email: :update, user_id: user_id, attrs: %{url: url}} |> EmailWorker.new() |> Oban.insert!()
end
end

View File

@ -62,11 +62,10 @@ defmodule CanneryWeb.UserRegistrationController do
invite |> Invites.use_invite!()
end
{:ok, _} =
Accounts.deliver_user_confirmation_instructions(
user,
&Routes.user_confirmation_url(conn, :confirm, &1)
)
Accounts.deliver_user_confirmation_instructions(
user,
&Routes.user_confirmation_url(conn, :confirm, &1)
)
conn
|> put_flash(:info, dgettext("prompts", "User created successfully."))

View File

@ -1,7 +1,7 @@
defmodule CanneryWeb.LayoutView do
use CanneryWeb, :view
alias Cannery.Accounts
alias CanneryWeb.HomeLive
alias CanneryWeb.{Endpoint, HomeLive}
# Phoenix LiveDashboard is available only in development by default,
# so we instruct Elixir to not warn if the dashboard route is missing.