defmodule PhoenixKit.Users.Auth.UserNotifier do @moduledoc """ User notification system for PhoenixKit authentication workflows. This module handles email delivery for user authentication and account management workflows, including account confirmation, password reset, and email change notifications. ## Email Types - **Confirmation instructions**: Sent during user registration - **Password reset instructions**: Sent when user requests password reset - **Email update instructions**: Sent when user changes their email address ## Configuration Configure your mailer in your application config: config :phoenix_kit, PhoenixKit.Mailer, adapter: Swoosh.Adapters.SMTP, # ... other adapter configuration ## Customization Override this module in your application to customize email templates and delivery behavior while maintaining the same function signatures. """ import Swoosh.Email alias PhoenixKit.Mailer alias PhoenixKit.Modules.Emails.Templates # Delivers the email using the appropriate mailer. # Uses the configured parent application mailer if available, # otherwise falls back to PhoenixKit's built-in mailer. defp deliver(recipient, subject, text_body, html_body) do from_email = get_from_email() from_name = get_from_name() email = new() |> to(recipient) |> from({from_name, from_email}) |> subject(subject) |> text_body(text_body) |> html_body(html_body) with {:ok, _metadata} <- Mailer.deliver_email(email, user_uuid: nil, template_name: "user_notification", campaign_id: "authentication" ) do {:ok, email} end end # Get the from email address from configuration or use a default # Priority: Settings Database > Config file > Default defp get_from_email do # Priority 1: Settings Database (runtime) case PhoenixKit.Settings.get_setting("from_email") do nil -> # Priority 2: Config file (compile-time, fallback) case PhoenixKit.Config.get(:from_email) do {:ok, email} -> email # Priority 3: Default _ -> "noreply@localhost" end email -> email end end # Get the from name from configuration or use a default # Priority: Settings Database > Config file > Default defp get_from_name do # Priority 1: Settings Database (runtime) case PhoenixKit.Settings.get_setting("from_name") do nil -> # Priority 2: Config file (compile-time, fallback) case PhoenixKit.Config.get(:from_name) do {:ok, name} -> name # Priority 3: Default _ -> "PhoenixKit" end name -> name end end @doc """ Deliver instructions to confirm account. Uses the 'register' template from the database if available, falls back to hardcoded template if not found. """ def deliver_confirmation_instructions(user, url) do # Variables for template substitution template_variables = %{ "user_email" => user.email, "confirmation_url" => url } # Try to get template from database, fallback to hardcoded {subject, html_body, text_body} = case Templates.get_active_template_by_name("register") do nil -> # Fallback to hardcoded templates fallback_text = """ ============================== 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. ============================== """ { "Confirm your account", confirmation_html_body(user.email, url), fallback_text } template -> # Use database template with variable substitution rendered = Templates.render_template(template, template_variables) {rendered.subject, rendered.html_body, rendered.text_body} end # Track template usage if using database template case Templates.get_active_template_by_name("register") do # No template to track nil -> :ok template -> Templates.track_usage(template) end deliver(user.email, subject, text_body, html_body) end @doc """ Deliver instructions to reset a user password. Uses the 'reset_password' template from the database if available, falls back to hardcoded template if not found. """ def deliver_reset_password_instructions(user, url) do # Variables for template substitution template_variables = %{ "user_email" => user.email, "reset_url" => url } # Try to get template from database, fallback to hardcoded {subject, html_body, text_body} = case Templates.get_active_template_by_name("reset_password") do nil -> # Fallback to hardcoded templates fallback_text = """ ============================== Hi #{user.email}, You can reset your password by visiting the URL below: #{url} If you didn't request this change, please ignore this. ============================== """ { "Reset your password", reset_password_html_body(user.email, url), fallback_text } template -> # Use database template with variable substitution rendered = Templates.render_template(template, template_variables) {rendered.subject, rendered.html_body, rendered.text_body} end # Track template usage if using database template case Templates.get_active_template_by_name("reset_password") do # No template to track nil -> :ok template -> Templates.track_usage(template) end deliver(user.email, subject, text_body, html_body) end @doc """ Deliver instructions to update a user email. Uses the 'update_email' template from the database if available, falls back to hardcoded template if not found. """ def deliver_update_email_instructions(user, url) do # Variables for template substitution template_variables = %{ "user_email" => user.email, "update_url" => url } # Try to get template from database, fallback to hardcoded {subject, html_body, text_body} = case Templates.get_active_template_by_name("update_email") do nil -> # Fallback to hardcoded templates fallback_text = """ ============================== Hi #{user.email}, You can change your email by visiting the URL below: #{url} If you didn't request this change, please ignore this. ============================== """ { "Confirm your email change", update_email_html_body(user.email, url), fallback_text } template -> # Use database template with variable substitution rendered = Templates.render_template(template, template_variables) {rendered.subject, rendered.html_body, rendered.text_body} end # Track template usage if using database template case Templates.get_active_template_by_name("update_email") do # No template to track nil -> :ok template -> Templates.track_usage(template) end deliver(user.email, subject, text_body, html_body) end # HTML template for account confirmation email defp confirmation_html_body(email, url) do """
Hi #{email},
Thank you for creating an account! To complete your registration, please confirm your email address by clicking the button below:
If you didn't create an account with us, you can safely ignore this email.
Hi #{email},
We received a request to reset your password. Click the button below to create a new password:
If you didn't request this password reset, you can safely ignore this email. Your password will remain unchanged.
Hi #{email},
We received a request to change your email address. To complete this change, please confirm your new email address by clicking the button below:
If you didn't request this email change, you can safely ignore this message. Your current email address will remain unchanged.
Hi #{email},
Thank you for starting your registration! Click the button below to complete your account setup:
If you didn't request this registration, you can safely ignore this email.