Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .formatter.exs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[
import_deps: [:ecto, :ecto_sql, :phoenix],
import_deps: [:oban, :ecto, :ecto_sql, :phoenix],
subdirectories: ["priv/*/migrations"],
plugins: [Phoenix.LiveView.HTMLFormatter],
inputs: ["*.{heex,ex,exs}", "{config,lib,test}/**/*.{heex,ex,exs}", "priv/*/seeds.exs"]
Expand Down
12 changes: 12 additions & 0 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@
# General application configuration
import Config

config :admin, Oban,
plugins: [
# retain jobs for 7 days
{Oban.Plugins.Pruner, max_age: 60 * 60 * 24 * 7},
# rescue orphan jobs after 2h
{Oban.Plugins.Lifeline, rescue_after: :timer.hours(2)}
],
engine: Oban.Engines.Basic,
notifier: Oban.Notifiers.Postgres,
queues: [default: 10, mailers: 1],
repo: Admin.Repo

config :admin, :scopes,
user: [
default: true,
Expand Down
1 change: 1 addition & 0 deletions config/test.exs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Config
config :admin, Oban, testing: :manual

# Only in tests, remove the complexity from the password hashing algorithm
config :bcrypt_elixir, :log_rounds, 1
Expand Down
30 changes: 30 additions & 0 deletions lib/admin/accounts.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ defmodule Admin.Accounts do
"""
require Logger
import Ecto.Query, warn: false
alias Admin.Accounts.Account
alias Admin.Repo

alias Admin.Accounts.{User, UserNotifier, UserToken}
Expand Down Expand Up @@ -352,4 +353,33 @@ defmodule Admin.Accounts do
confirmed: Admin.Repo.aggregate(from(u in User, where: not is_nil(u.confirmed_at)), :count)
}
end

# Graasp Members

def get_member!(id) do
Repo.get!(Account, id)
end

def get_member_by_email(email) do
case Repo.get_by(Account, email: email) do
%Account{} = user -> {:ok, user}
nil -> {:error, :member_not_found}
end
end

def get_active_members do
Repo.all(
from(m in Account,
where:
not is_nil(m.last_authenticated_at) and m.last_authenticated_at > ago(90, "day") and
m.type == "individual"
)
)
end

def create_member(attrs \\ %{}) do
%Account{}
|> Account.changeset(attrs)
|> Repo.insert()
end
end
19 changes: 18 additions & 1 deletion lib/admin/accounts/account.ex
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
defmodule Admin.Accounts.Account do
@moduledoc """
This represents a graasp user.
This represents a graasp user.
"""
use Admin.Schema
import Ecto.Changeset

schema "account" do
field :name, :string
Expand All @@ -11,4 +12,20 @@ defmodule Admin.Accounts.Account do

timestamps(type: :utc_datetime)
end

@doc false
def changeset(account, attrs) do
account
|> cast(attrs, [:name, :email, :type])
|> validate_required([:name, :email, :type])
|> validate_email()
end

defp validate_email(changeset) do
changeset
|> validate_format(:email, ~r/^[^@,;\s]+@[^@,;\s]+$/,
message: "must have the @ sign and no spaces"
)
|> validate_length(:email, max: 160)
end
end
36 changes: 36 additions & 0 deletions lib/admin/accounts/user_notifier.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,31 @@ defmodule Admin.Accounts.UserNotifier do
alias Admin.Accounts.Account
alias Admin.Accounts.User
alias Admin.Mailer
alias Admin.Notifications.MailingTemplates

@footer "Graasp.org is a learning experience platform."

def test_email(scope) do
html =
MailingTemplates.simple_call_to_action(%{
user: scope,
message: "This is a test email.",
button_text: "Click here",
button_url: "https://example.com"
})

email =
new()
|> to("basile@graasp.org")
|> from({"Admin", "admin@graasp.org"})
|> subject("test email")
|> html_body(html)

with {:ok, _metadata} <- Mailer.deliver(email) do
{:ok, email}
end
end

# Delivers the email using the application mailer.
defp deliver(recipient, subject, body) do
email =
Expand All @@ -24,6 +46,20 @@ defmodule Admin.Accounts.UserNotifier do
end
end

def deliver_notification(user, subject, message_text) do
deliver(user.email, subject, """

==============================

Hi #{user.name},

#{message_text}

==============================
#{@footer}
""")
end

@doc """
Deliver publication removal information.
"""
Expand Down
1 change: 1 addition & 0 deletions lib/admin/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ defmodule Admin.Application do
AdminWeb.Telemetry,
Admin.Repo,
{DNSCluster, query: Application.get_env(:admin, :dns_cluster_query) || :ignore},
{Oban, Application.fetch_env!(:admin, Oban)},
{Phoenix.PubSub, name: Admin.PubSub},
# Start a worker by calling: Admin.Worker.start_link(arg)
# {Admin.Worker, arg},
Expand Down
5 changes: 0 additions & 5 deletions lib/admin/file_size.ex
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,6 @@ defmodule Admin.Utils.FileSize do
"#{format_number(value, 2)} #{unit}"
end

# Formats numbers with fixed precision, trimming trailing zeros neatly.
defp format_number(number, precision) when precision <= 0 do
Integer.to_string(trunc(number))
end

defp format_number(number, precision) do
formatted =
number
Expand Down
62 changes: 62 additions & 0 deletions lib/admin/mailer_worker.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
defmodule Admin.MailerWorker do
@moduledoc """
Worker for sending notifications via email.
"""

use Oban.Worker, queue: :mailers

alias Admin.Accounts
alias Admin.Accounts.Scope
alias Admin.Accounts.UserNotifier
alias Admin.Notifications
alias Admin.Notifications.Notification

@impl Oban.Worker
def perform(%Oban.Job{
args:
%{
"user_id" => user_id,
"member_email" => member_email,
"notification_id" => notification_id
} =
_args
}) do
user = Accounts.get_user!(user_id)
scope = Scope.for_user(user)

with {:ok, member} <- Accounts.get_member_by_email(member_email),
{:ok, notification} <- Notifications.get_notification(scope, notification_id),
{:ok, _} <-
UserNotifier.deliver_notification(
member,
notification.title,
notification.message
) do
Notifications.save_log(
scope,
%{
email: member.email,
status: "sent"
},
notification
)

:ok
else
{:error, reason} when reason in [:member_not_found, :notification_not_found] ->
Notifications.save_log(
scope,
%{
email: member_email,
status: "failed"
},
%Notification{id: notification_id}
)

{:cancel, reason}

{:error, _} ->
{:error, "Failed to send notification"}
end
end
end
Loading
Loading