mobilizon.chapril.org-mobil.../lib/mobilizon/users/users.ex

342 lines
7.4 KiB
Elixir

defmodule Mobilizon.Users do
@moduledoc """
The Users context.
"""
import Ecto.Query, warn: false
alias Mobilizon.Repo
import Mobilizon.Ecto
alias Mobilizon.Actors.Actor
alias Mobilizon.Users.User
@doc false
def data() do
Dataloader.Ecto.new(Repo, query: &query/2)
end
@doc false
def query(queryable, _params) do
queryable
end
@doc """
Register user
"""
@spec register(map()) :: {:ok, User.t()} | {:error, String.t()}
def register(%{email: _email, password: _password} = args) do
with {:ok, %User{} = user} <-
%User{}
|> User.registration_changeset(args)
|> Mobilizon.Repo.insert() do
Mobilizon.Events.create_feed_token(%{"user_id" => user.id})
{:ok, user}
end
end
@doc """
Gets an user by it's email
## Examples
iex> get_user_by_email("test@test.tld", true)
{:ok, %Mobilizon.Users.User{}}
iex> get_user_by_email("test@notfound.tld", false)
{:error, :user_not_found}
"""
def get_user_by_email(email, activated \\ nil) do
query =
case activated do
nil ->
from(u in User, where: u.email == ^email, preload: :default_actor)
true ->
from(
u in User,
where: u.email == ^email and not is_nil(u.confirmed_at),
preload: :default_actor
)
false ->
from(
u in User,
where: u.email == ^email and is_nil(u.confirmed_at),
preload: :default_actor
)
end
case Repo.one(query) do
nil -> {:error, :user_not_found}
user -> {:ok, user}
end
end
@doc """
Get an user by it's activation token
"""
@spec get_user_by_activation_token(String.t()) :: Actor.t()
def get_user_by_activation_token(token) do
Repo.one(
from(
u in User,
where: u.confirmation_token == ^token,
preload: [:default_actor]
)
)
end
@doc """
Get an user by it's reset password token
"""
@spec get_user_by_reset_password_token(String.t()) :: Actor.t()
def get_user_by_reset_password_token(token) do
Repo.one(
from(
u in User,
where: u.reset_password_token == ^token,
preload: [:default_actor]
)
)
end
@doc """
Updates a user.
## Examples
iex> update_user(User{}, %{password: "coucou"})
{:ok, %Mobilizon.Users.User{}}
iex> update_user(User{}, %{password: nil})
{:error, %Ecto.Changeset{}}
"""
def update_user(%User{} = user, attrs) do
with {:ok, %User{} = user} <-
user
|> User.changeset(attrs)
|> Repo.update() do
{:ok, Repo.preload(user, [:default_actor])}
end
end
@doc """
Deletes a User.
## Examples
iex> delete_user(%User{email: "test@test.tld"})
{:ok, %Mobilizon.Users.User{}}
iex> delete_user(%User{})
{:error, %Ecto.Changeset{}}
"""
def delete_user(%User{} = user) do
Repo.delete(user)
end
# @doc """
# Returns an `%Ecto.Changeset{}` for tracking user changes.
# ## Examples
# iex> change_user(%Mobilizon.Users.User{})
# %Ecto.Changeset{data: %Mobilizon.Users.User{}}
# """
# def change_user(%User{} = user) do
# User.changeset(user, %{})
# end
@doc """
Gets a single user.
Raises `Ecto.NoResultsError` if the User does not exist.
## Examples
iex> get_user!(123)
%Mobilizon.Users.User{}
iex> get_user!(456)
** (Ecto.NoResultsError)
"""
def get_user!(id), do: Repo.get!(User, id)
@doc """
Get an user with it's actors
Raises `Ecto.NoResultsError` if the User does not exist.
"""
@spec get_user_with_actors!(integer()) :: User.t()
def get_user_with_actors!(id) do
user = Repo.get!(User, id)
Repo.preload(user, [:actors, :default_actor])
end
@doc """
Get user with it's actors by ID
"""
@spec get_user_with_actors(integer()) :: User.t()
def get_user_with_actors(id) do
case Repo.get(User, id) do
nil ->
{:error, "User with ID #{id} not found"}
user ->
user =
user
|> Repo.preload([:actors, :default_actor])
|> Map.put(:actors, get_actors_for_user(user))
{:ok, user}
end
end
@doc """
Returns the associated actor for an user, either the default set one or the first found
"""
@spec get_actor_for_user(Mobilizon.Users.User.t()) :: Mobilizon.Actors.Actor.t()
def get_actor_for_user(%Mobilizon.Users.User{} = user) do
case Repo.one(
from(
a in Actor,
join: u in User,
on: u.default_actor_id == a.id,
where: u.id == ^user.id
)
) do
nil ->
case user
|> get_actors_for_user() do
[] -> nil
actors -> hd(actors)
end
actor ->
actor
end
end
def get_actors_for_user(%User{id: user_id}) do
Repo.all(from(a in Actor, where: a.user_id == ^user_id))
end
@doc """
Authenticate user
"""
def authenticate(%{user: user, password: password}) do
# Does password match the one stored in the database?
with true <- Argon2.verify_pass(password, user.password_hash),
# Yes, create and return the token
{:ok, tokens} <- generate_tokens(user) do
{:ok, tokens}
else
_ ->
# No, return an error
{:error, :unauthorized}
end
end
@doc """
Generate access token and refresh token
"""
def generate_tokens(user) do
with {:ok, access_token} <- generate_access_token(user),
{:ok, refresh_token} <- generate_refresh_token(user) do
{:ok, %{access_token: access_token, refresh_token: refresh_token}}
end
end
defp generate_access_token(user) do
with {:ok, access_token, _claims} <-
MobilizonWeb.Guardian.encode_and_sign(user, %{}, token_type: "access") do
{:ok, access_token}
end
end
def generate_refresh_token(user) do
with {:ok, refresh_token, _claims} <-
MobilizonWeb.Guardian.encode_and_sign(user, %{}, token_type: "refresh") do
{:ok, refresh_token}
end
end
def update_user_default_actor(user_id, actor_id) do
with _ <-
from(
u in User,
where: u.id == ^user_id,
update: [
set: [
default_actor_id: ^actor_id
]
]
)
|> Repo.update_all([]) do
Repo.get!(User, user_id)
|> Repo.preload([:default_actor])
end
end
@doc """
Returns the list of users.
## Examples
iex> list_users()
[%Mobilizon.Users.User{}]
"""
def list_users(page \\ nil, limit \\ nil, sort \\ nil, direction \\ nil) do
Repo.all(
User
|> paginate(page, limit)
|> sort(sort, direction)
)
end
@doc """
Returns the list of administrators.
## Examples
iex> list_admins()
[%Mobilizon.Users.User{role: :administrator}]
"""
def list_admins() do
User
|> where([u], u.role == ^:administrator)
|> Repo.all()
end
@doc """
Returns the list of moderators.
## Examples
iex> list_moderators()
[%Mobilizon.Users.User{role: :moderator}, %Mobilizon.Users.User{role: :administrator}]
"""
def list_moderators() do
User
|> where([u], u.role in ^[:administrator, :moderator])
|> Repo.all()
end
def count_users() do
Repo.one(
from(
u in User,
select: count(u.id)
)
)
end
end