@@ -312,8 +312,12 @@ defmodule Eventos.Actors do
|
||||
Get an user by email
|
||||
"""
|
||||
def find_by_email(email) do
|
||||
user = Repo.get_by(User, email: email)
|
||||
Repo.preload(user, :actor)
|
||||
case Repo.get_by(User, email: email) |> Repo.preload(:actor) do
|
||||
nil ->
|
||||
{:error, nil}
|
||||
user ->
|
||||
{:ok, user}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -366,8 +370,7 @@ defmodule Eventos.Actors do
|
||||
|
||||
try do
|
||||
Eventos.Repo.insert!(actor_with_user)
|
||||
user = find_by_email(email)
|
||||
{:ok, user}
|
||||
find_by_email(email)
|
||||
rescue
|
||||
e in Ecto.InvalidChangesetError ->
|
||||
{:error, e.changeset}
|
||||
|
||||
29
lib/eventos/actors/service/activation.ex
Normal file
29
lib/eventos/actors/service/activation.ex
Normal file
@@ -0,0 +1,29 @@
|
||||
defmodule Eventos.Actors.Service.Activation do
|
||||
@moduledoc false
|
||||
|
||||
alias Eventos.{Mailer, Repo, Actors.User, Actors}
|
||||
alias Eventos.Email.User, as: UserEmail
|
||||
|
||||
require Logger
|
||||
|
||||
@doc false
|
||||
def check_confirmation_token(token) when is_binary(token) do
|
||||
with %User{} = user <- Repo.get_by(User, confirmation_token: token) do
|
||||
Actors.update_user(user, %{"confirmed_at" => DateTime.utc_now(), "confirmation_sent_at" => nil, "confirmation_token" => nil})
|
||||
else
|
||||
_err ->
|
||||
{:error, "Invalid token"}
|
||||
end
|
||||
end
|
||||
|
||||
def resend_confirmation_email(%User{} = user, locale \\ "en") do
|
||||
{:ok, user} = Actors.update_user(user, %{"confirmation_sent_at" => DateTime.utc_now()})
|
||||
send_confirmation_email(user, locale)
|
||||
end
|
||||
|
||||
def send_confirmation_email(%User{} = user, locale \\ "en") do
|
||||
user
|
||||
|> UserEmail.confirmation_email(locale)
|
||||
|> Mailer.deliver_later()
|
||||
end
|
||||
end
|
||||
57
lib/eventos/actors/service/reset_password.ex
Normal file
57
lib/eventos/actors/service/reset_password.ex
Normal file
@@ -0,0 +1,57 @@
|
||||
defmodule Eventos.Actors.Service.ResetPassword do
|
||||
@moduledoc false
|
||||
|
||||
require Logger
|
||||
|
||||
alias Eventos.{Mailer, Repo, Actors.User}
|
||||
alias Eventos.Email.User, as: UserEmail
|
||||
|
||||
@doc """
|
||||
Check that the provided token is correct and update provided password
|
||||
"""
|
||||
@spec check_reset_password_token(String.t, String.t) :: tuple
|
||||
def check_reset_password_token(password, token) do
|
||||
with %User{} = user <- Repo.get_by(User, reset_password_token: token) do
|
||||
User.password_reset_changeset(user, %{"password" => password, "reset_password_sent_at" => nil, "reset_password_token" => nil}) |> Repo.update()
|
||||
else
|
||||
_err ->
|
||||
{:error, :invalid_token}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Send the email reset password, if it's not too soon since the last send
|
||||
"""
|
||||
@spec send_password_reset_email(User.t, String.t) :: tuple
|
||||
def send_password_reset_email(%User{} = user, locale \\ "en") do
|
||||
with :ok <- we_can_send_email(user),
|
||||
{:ok, %User{} = user_updated} <- User.send_password_reset_changeset(user, %{"reset_password_token" => random_string(30), "reset_password_sent_at" => DateTime.utc_now()}) |> Repo.update() do
|
||||
mail = user_updated
|
||||
|> UserEmail.reset_password_email(locale)
|
||||
|> Mailer.deliver_later()
|
||||
{:ok, mail}
|
||||
else
|
||||
{:error, reason} -> {:error, reason}
|
||||
end
|
||||
end
|
||||
|
||||
@spec random_string(integer) :: String.t
|
||||
defp random_string(length) do
|
||||
:crypto.strong_rand_bytes(length) |> Base.url_encode64
|
||||
end
|
||||
|
||||
@spec we_can_send_email(User.t) :: boolean
|
||||
defp we_can_send_email(%User{} = user) do
|
||||
case user.reset_password_sent_at do
|
||||
nil ->
|
||||
:ok
|
||||
_ ->
|
||||
case Timex.before?(Timex.shift(user.reset_password_sent_at, hours: 1), DateTime.utc_now()) do
|
||||
true ->
|
||||
:ok
|
||||
false ->
|
||||
{:error, :email_too_soon}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -12,6 +12,11 @@ defmodule Eventos.Actors.User do
|
||||
field :password, :string, virtual: true
|
||||
field :role, :integer, default: 0
|
||||
belongs_to :actor, Actor
|
||||
field :confirmed_at, :utc_datetime
|
||||
field :confirmation_sent_at, :utc_datetime
|
||||
field :confirmation_token, :string
|
||||
field :reset_password_sent_at, :utc_datetime
|
||||
field :reset_password_token, :string
|
||||
|
||||
timestamps()
|
||||
end
|
||||
@@ -19,18 +24,49 @@ defmodule Eventos.Actors.User do
|
||||
@doc false
|
||||
def changeset(%User{} = user, attrs) do
|
||||
user
|
||||
|> cast(attrs, [:email, :role, :password_hash, :actor_id])
|
||||
|> cast(attrs, [:email, :role, :password_hash, :actor_id, :confirmed_at, :confirmation_sent_at, :confirmation_token, :reset_password_sent_at, :reset_password_token])
|
||||
|> validate_required([:email])
|
||||
|> unique_constraint(:email)
|
||||
|> unique_constraint(:email, [message: "registration.error.email_already_used"])
|
||||
|> validate_format(:email, ~r/@/)
|
||||
|> validate_length(:password, min: 6, max: 100, message: "registration.error.password_too_short")
|
||||
end
|
||||
|
||||
def registration_changeset(struct, params) do
|
||||
struct
|
||||
|> changeset(params)
|
||||
|> cast(params, ~w(password)a, [])
|
||||
|> validate_length(:password, min: 6, max: 100)
|
||||
|> validate_required([:email, :password])
|
||||
|> validate_length(:password, min: 6, max: 100, message: "registration.error.password_too_short")
|
||||
|> hash_password()
|
||||
|> save_confirmation_token()
|
||||
|> unique_constraint(:confirmation_token, [message: "regisration.error.confirmation_token_already_in_use"])
|
||||
end
|
||||
|
||||
def send_password_reset_changeset(%User{} = user, attrs) do
|
||||
user
|
||||
|> cast(attrs, [:reset_password_token, :reset_password_sent_at])
|
||||
end
|
||||
|
||||
def password_reset_changeset(%User{} = user, attrs) do
|
||||
user
|
||||
|> cast(attrs, [:password, :reset_password_token, :reset_password_sent_at])
|
||||
|> validate_length(:password, min: 6, max: 100, message: "registration.error.password_too_short")
|
||||
|> hash_password()
|
||||
end
|
||||
|
||||
defp save_confirmation_token(changeset) do
|
||||
case changeset do
|
||||
%Ecto.Changeset{valid?: true,
|
||||
changes: %{email: _email}} ->
|
||||
changeset = put_change(changeset, :confirmation_token, random_string(30))
|
||||
put_change(changeset, :confirmation_sent_at, DateTime.utc_now())
|
||||
_ ->
|
||||
changeset
|
||||
end
|
||||
end
|
||||
|
||||
defp random_string(length) do
|
||||
:crypto.strong_rand_bytes(length) |> Base.url_encode64
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -47,4 +83,12 @@ defmodule Eventos.Actors.User do
|
||||
changeset
|
||||
end
|
||||
end
|
||||
|
||||
def is_confirmed(%User{confirmed_at: nil} = _user) do
|
||||
{:error, :unconfirmed}
|
||||
end
|
||||
|
||||
def is_confirmed(%User{} = user) do
|
||||
{:ok, user}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -26,6 +26,5 @@ defmodule Eventos.Addresses.Address do
|
||||
def changeset(%Address{} = address, attrs) do
|
||||
address
|
||||
|> cast(attrs, [:description, :floor, :geom, :addressCountry, :addressLocality, :addressRegion, :postalCode, :streetAddress])
|
||||
|> validate_required([:streetAddress])
|
||||
end
|
||||
end
|
||||
|
||||
46
lib/eventos/email/user.ex
Normal file
46
lib/eventos/email/user.ex
Normal file
@@ -0,0 +1,46 @@
|
||||
defmodule Eventos.Email.User do
|
||||
|
||||
alias Eventos.Actors.User
|
||||
|
||||
import Bamboo.Email
|
||||
import Bamboo.Phoenix
|
||||
use Bamboo.Phoenix, view: Eventos.EmailView
|
||||
import EventosWeb.Gettext
|
||||
|
||||
def confirmation_email(%User{} = user, locale \\ "en") do
|
||||
Gettext.put_locale(locale)
|
||||
instance_url = get_config(:instance)
|
||||
base_email()
|
||||
|> to(user.email)
|
||||
|> subject(gettext "Peakweaver: Confirmation instructions for %{instance}", instance: instance_url)
|
||||
|> put_header("Reply-To", get_config(:reply_to))
|
||||
|> assign(:token, user.confirmation_token)
|
||||
|> assign(:instance, instance_url)
|
||||
|> render(:registration_confirmation)
|
||||
end
|
||||
|
||||
def reset_password_email(%User{} = user, locale \\ "en") do
|
||||
Gettext.put_locale(locale)
|
||||
instance_url = get_config(:instance)
|
||||
base_email()
|
||||
|> to(user.email)
|
||||
|> subject(gettext "Peakweaver: Reset your password on %{instance} instructions", instance: instance_url)
|
||||
|> put_header("Reply-To", get_config(:reply_to))
|
||||
|> assign(:token, user.reset_password_token)
|
||||
|> assign(:instance, instance_url)
|
||||
|> render(:password_reset)
|
||||
end
|
||||
|
||||
defp base_email do
|
||||
# Here you can set a default from, default headers, etc.
|
||||
new_email()
|
||||
|> from(Application.get_env(:eventos, EventosWeb.Endpoint)[:email_from])
|
||||
|> put_html_layout({Eventos.EmailView, "email.html"})
|
||||
|> put_text_layout({Eventos.EmailView, "email.text"})
|
||||
end
|
||||
|
||||
@spec get_config(atom()) :: any()
|
||||
defp get_config(key) do
|
||||
_config = Application.get_env(:eventos, EventosWeb.Endpoint) |> Keyword.get(key)
|
||||
end
|
||||
end
|
||||
@@ -1,30 +1,5 @@
|
||||
defmodule Eventos.Events.Event.TitleSlug do
|
||||
@moduledoc """
|
||||
Generates a slug for an event title
|
||||
"""
|
||||
alias Eventos.Events.Event
|
||||
import Ecto.Query
|
||||
alias Eventos.Repo
|
||||
use EctoAutoslugField.Slug, from: :title, to: :slug
|
||||
|
||||
def build_slug(sources, changeset) do
|
||||
slug = super(sources, changeset)
|
||||
build_unique_slug(slug, changeset)
|
||||
end
|
||||
|
||||
defp build_unique_slug(slug, changeset) do
|
||||
query = from e in Event,
|
||||
where: e.slug == ^slug
|
||||
|
||||
case Repo.one(query) do
|
||||
nil -> slug
|
||||
_event ->
|
||||
slug
|
||||
|> Eventos.Slug.increment_slug()
|
||||
|> build_unique_slug(changeset)
|
||||
end
|
||||
end
|
||||
end
|
||||
import EctoEnum
|
||||
defenum AddressTypeEnum, :address_type, [:physical, :url, :phone, :other]
|
||||
|
||||
defmodule Eventos.Events.Event do
|
||||
@moduledoc """
|
||||
@@ -33,7 +8,6 @@ defmodule Eventos.Events.Event do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
alias Eventos.Events.{Event, Participant, Tag, Category, Session, Track}
|
||||
alias Eventos.Events.Event.TitleSlug
|
||||
alias Eventos.Actors.Actor
|
||||
alias Eventos.Addresses.Address
|
||||
|
||||
@@ -44,7 +18,6 @@ defmodule Eventos.Events.Event do
|
||||
field :description, :string
|
||||
field :ends_on, Timex.Ecto.DateTimeWithTimezone
|
||||
field :title, :string
|
||||
field :slug, TitleSlug.Type
|
||||
field :state, :integer, default: 0
|
||||
field :status, :integer, default: 0
|
||||
field :public, :boolean, default: true
|
||||
@@ -52,6 +25,9 @@ defmodule Eventos.Events.Event do
|
||||
field :large_image, :string
|
||||
field :publish_at, Timex.Ecto.DateTimeWithTimezone
|
||||
field :uuid, Ecto.UUID, default: Ecto.UUID.generate()
|
||||
field :address_type, AddressTypeEnum, default: :physical
|
||||
field :online_address, :string
|
||||
field :phone, :string
|
||||
belongs_to :organizer_actor, Actor, [foreign_key: :organizer_actor_id]
|
||||
belongs_to :attributed_to, Actor, [foreign_key: :attributed_to_id]
|
||||
many_to_many :tags, Tag, join_through: "events_tags"
|
||||
@@ -59,7 +35,7 @@ defmodule Eventos.Events.Event do
|
||||
many_to_many :participants, Actor, join_through: Participant
|
||||
has_many :tracks, Track
|
||||
has_many :sessions, Session
|
||||
belongs_to :address, Address
|
||||
belongs_to :physical_address, Address
|
||||
|
||||
timestamps(type: :utc_datetime)
|
||||
end
|
||||
@@ -75,13 +51,28 @@ defmodule Eventos.Events.Event do
|
||||
""
|
||||
end
|
||||
event
|
||||
|> cast(attrs, [:title, :description, :url, :begins_on, :ends_on, :organizer_actor_id, :category_id, :state, :status, :public, :thumbnail, :large_image, :publish_at])
|
||||
|> Ecto.Changeset.cast(attrs, [
|
||||
:title,
|
||||
:description,
|
||||
:url,
|
||||
:begins_on,
|
||||
:ends_on,
|
||||
:organizer_actor_id,
|
||||
:category_id,
|
||||
:state,
|
||||
:status,
|
||||
:public,
|
||||
:thumbnail,
|
||||
:large_image,
|
||||
:publish_at,
|
||||
:address_type,
|
||||
:online_address,
|
||||
:phone,
|
||||
])
|
||||
|> cast_assoc(:tags)
|
||||
|> cast_assoc(:address)
|
||||
|> TitleSlug.maybe_generate_slug()
|
||||
|> TitleSlug.unique_constraint()
|
||||
|> cast_assoc(:physical_address)
|
||||
|> put_change(:uuid, uuid)
|
||||
|> put_change(:url, "#{EventosWeb.Endpoint.url()}/@#{actor_url}/#{uuid}")
|
||||
|> validate_required([:title, :description, :begins_on, :ends_on, :organizer_actor_id, :category_id, :url, :uuid])
|
||||
|> validate_required([:title, :begins_on, :ends_on, :organizer_actor_id, :category_id, :url, :uuid, :address_type])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -32,7 +32,7 @@ defmodule Eventos.Events do
|
||||
limit: ^limit,
|
||||
order_by: [desc: :id],
|
||||
offset: ^start,
|
||||
preload: [:organizer_actor, :category, :sessions, :tracks, :tags, :participants, :address]
|
||||
preload: [:organizer_actor, :category, :sessions, :tracks, :tags, :participants, :physical_address]
|
||||
events = Repo.all(query)
|
||||
count_events = Repo.one(from e in Event, select: count(e.id), where: e.organizer_actor_id == ^actor_id)
|
||||
{:ok, events, count_events}
|
||||
@@ -89,7 +89,7 @@ defmodule Eventos.Events do
|
||||
"""
|
||||
def get_event_full!(id) do
|
||||
event = Repo.get!(Event, id)
|
||||
Repo.preload(event, [:organizer_actor, :category, :sessions, :tracks, :tags, :participants, :address])
|
||||
Repo.preload(event, [:organizer_actor, :category, :sessions, :tracks, :tags, :participants, :physical_address])
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -97,7 +97,7 @@ defmodule Eventos.Events do
|
||||
"""
|
||||
def get_event_full_by_url!(url) do
|
||||
event = Repo.get_by(Event, url: url)
|
||||
Repo.preload(event, [:organizer_actor, :category, :sessions, :tracks, :tags, :participants, :address])
|
||||
Repo.preload(event, [:organizer_actor, :category, :sessions, :tracks, :tags, :participants, :physical_address])
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -105,23 +105,7 @@ defmodule Eventos.Events do
|
||||
"""
|
||||
def get_event_full_by_uuid(uuid) do
|
||||
event = Repo.get_by(Event, uuid: uuid)
|
||||
Repo.preload(event, [:organizer_actor, :category, :sessions, :tracks, :tags, :participants, :address])
|
||||
end
|
||||
|
||||
@spec get_event_full_by_name_and_slug!(String.t, String.t) :: Event.t
|
||||
def get_event_full_by_name_and_slug!(name, slug) do
|
||||
query = case String.split(name, "@") do
|
||||
[name, domain] -> from e in Event,
|
||||
join: a in Actor,
|
||||
on: a.id == e.organizer_actor_id and a.preferred_username == ^name and a.domain == ^domain,
|
||||
where: e.slug == ^slug
|
||||
[name] -> from e in Event,
|
||||
join: a in Actor,
|
||||
on: a.id == e.organizer_actor_id and a.preferred_username == ^name and is_nil(a.domain),
|
||||
where: e.slug == ^slug
|
||||
end
|
||||
event = Repo.one(query)
|
||||
Repo.preload(event, [:organizer_actor, :category, :sessions, :tracks, :tags, :participants, :address])
|
||||
Repo.preload(event, [:organizer_actor, :category, :sessions, :tracks, :tags, :participants, :physical_address])
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
||||
3
lib/eventos/mailer.ex
Normal file
3
lib/eventos/mailer.ex
Normal file
@@ -0,0 +1,3 @@
|
||||
defmodule Eventos.Mailer do
|
||||
use Bamboo.Mailer, otp_app: :eventos
|
||||
end
|
||||
@@ -19,12 +19,9 @@ defmodule EventosWeb.EventController do
|
||||
end
|
||||
|
||||
def create(conn, %{"event" => event_params}) do
|
||||
address = process_address(event_params["address"])
|
||||
event_params = if is_nil address do
|
||||
event_params
|
||||
else
|
||||
%{event_params | "address" => address}
|
||||
end
|
||||
event_params = process_event_address(event_params)
|
||||
Logger.debug("creating event with")
|
||||
Logger.debug(inspect event_params)
|
||||
with {:ok, %Event{} = event} <- Events.create_event(event_params) do
|
||||
conn
|
||||
|> put_status(:created)
|
||||
@@ -33,15 +30,19 @@ defmodule EventosWeb.EventController do
|
||||
end
|
||||
end
|
||||
|
||||
defp process_address(address) do
|
||||
geom = EventosWeb.AddressController.process_geom(address["geom"])
|
||||
case geom do
|
||||
nil ->
|
||||
address
|
||||
_ ->
|
||||
%{address | "geom" => geom}
|
||||
_ ->
|
||||
address
|
||||
defp process_event_address(event) do
|
||||
if Map.has_key?(event, "address_type") && event["address_type"] === :physical do
|
||||
address = event["physical_address"]
|
||||
geom = EventosWeb.AddressController.process_geom(address["geom"])
|
||||
address = case geom do
|
||||
nil ->
|
||||
address
|
||||
_ ->
|
||||
%{address | "geom" => geom}
|
||||
end
|
||||
%{event | "physical_address" => address}
|
||||
else
|
||||
event
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ defmodule EventosWeb.UserController do
|
||||
alias Eventos.Actors
|
||||
alias Eventos.Actors.User
|
||||
alias Eventos.Repo
|
||||
alias Eventos.Actors.Service.{Activation, ResetPassword}
|
||||
|
||||
action_fallback EventosWeb.FallbackController
|
||||
|
||||
@@ -16,18 +17,80 @@ defmodule EventosWeb.UserController do
|
||||
end
|
||||
|
||||
def register(conn, %{"username" => username, "email" => email, "password" => password}) do
|
||||
with {:ok, %User{} = user} <- Actors.register(%{email: email, password: password, username: username}),
|
||||
{:ok, token, _claims} <- EventosWeb.Guardian.encode_and_sign(user) do
|
||||
conn
|
||||
with {:ok, %User{} = user} <- Actors.register(%{email: email, password: password, username: username}) do
|
||||
Activation.send_confirmation_email(user, "locale")
|
||||
conn
|
||||
|> put_status(:created)
|
||||
|> render("show_with_token.json", %{token: token, user: user})
|
||||
|> render("confirmation.json", %{user: user})
|
||||
end
|
||||
end
|
||||
|
||||
def validate(conn, %{"token" => token}) do
|
||||
with {:ok, %User{} = user} <- Activation.check_confirmation_token(token) do
|
||||
{:ok, token, _claims} = EventosWeb.Guardian.encode_and_sign(user)
|
||||
conn
|
||||
|> put_resp_header("location", user_path(conn, :show_current_actor))
|
||||
|> render("show_with_token.json", %{user: user, token: token})
|
||||
else
|
||||
{:error, msg} ->
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(%{"error" => msg})
|
||||
end
|
||||
end
|
||||
|
||||
def resend_confirmation(conn, %{"email" => email}) do
|
||||
with {:ok, %User{} = user} <- Actors.find_by_email(email),
|
||||
false <- is_nil(user.confirmation_token),
|
||||
true <- Timex.before?(Timex.shift(user.confirmation_sent_at, hours: 1), DateTime.utc_now()) do
|
||||
Activation.resend_confirmation_email(user)
|
||||
render(conn, "confirmation.json", %{user: user})
|
||||
else
|
||||
{:error, :not_found} ->
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(%{"error" => "Unable to find an user with this email"})
|
||||
_ ->
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(%{"error" => "Unable to resend the validation token"})
|
||||
end
|
||||
end
|
||||
|
||||
def send_reset_password(conn, %{"email" => email}) do
|
||||
with {:ok, %User{} = user} <- Actors.find_by_email(email),
|
||||
{:ok, _} <- ResetPassword.send_password_reset_email(user) do
|
||||
render(conn, "password_reset.json", %{user: user})
|
||||
else
|
||||
{:error, :not_found} ->
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(%{"errors" => "Unable to find an user with this email"})
|
||||
{:error, :email_too_soon} ->
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(%{"errors" => "You requested a new reset password too early"})
|
||||
end
|
||||
end
|
||||
|
||||
def reset_password(conn, %{"password" => password, "token" => token}) do
|
||||
with {:ok, %User{} = user} <- ResetPassword.check_reset_password_token(password, token) do
|
||||
{:ok, token, _claims} = EventosWeb.Guardian.encode_and_sign(user)
|
||||
render(conn, "show_with_token.json", %{user: user, token: token})
|
||||
else
|
||||
{:error, :invalid_token} ->
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(%{"errors" => %{"token" => ["Wrong token for password reset"]}})
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
conn
|
||||
|> put_status(:unprocessable_entity)
|
||||
|> render(EventosWeb.ChangesetView, "error.json", changeset: changeset)
|
||||
end
|
||||
end
|
||||
|
||||
def show_current_actor(conn, _params) do
|
||||
user = Guardian.Plug.current_resource(conn)
|
||||
user
|
||||
|> Repo.preload(:actor)
|
||||
user = Guardian.Plug.current_resource(conn) |> Repo.preload(:actor)
|
||||
render(conn, "show_simple.json", user: user)
|
||||
end
|
||||
|
||||
|
||||
@@ -7,18 +7,24 @@ defmodule EventosWeb.UserSessionController do
|
||||
alias Eventos.Actors
|
||||
|
||||
def sign_in(conn, %{"email" => email, "password" => password}) do
|
||||
case Actors.find_by_email(email) do
|
||||
%User{} = user ->
|
||||
# Attempt to authenticate the user
|
||||
case Actors.authenticate(%{user: user, password: password}) do
|
||||
{:ok, token, _claims} ->
|
||||
with {:ok, %User{} = user} <- Actors.find_by_email(email),
|
||||
{:ok, %User{} = _user} <- User.is_confirmed(user),
|
||||
{:ok, token, _claims} <- Actors.authenticate(%{user: user, password: password}) do
|
||||
# Render the token
|
||||
render conn, "token.json", %{token: token, user: user}
|
||||
_ ->
|
||||
send_resp(conn, 400, Poison.encode!(%{"error_msg" => "Bad login", "display_error" => "session.error.bad_login", "error_code" => 400}))
|
||||
end
|
||||
_ ->
|
||||
send_resp(conn, 400, Poison.encode!(%{"error_msg" => "No such user", "display_error" => "session.error.bad_login", "error_code" => 400}))
|
||||
else
|
||||
{:error, :not_found} ->
|
||||
conn
|
||||
|> put_status(401)
|
||||
|> json(%{"error_msg" => "No such user", "display_error" => "session.error.bad_login"})
|
||||
{:error, :unconfirmed} ->
|
||||
conn
|
||||
|> put_status(401)
|
||||
|> json(%{"error_msg" => "User is not activated", "display_error" => "session.error.not_activated"})
|
||||
{:error, :unauthorized} ->
|
||||
conn
|
||||
|> put_status(401)
|
||||
|> json(%{"error_msg" => "Bad login", "display_error" => "session.error.bad_login"})
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -36,6 +36,12 @@ defmodule EventosWeb.Router do
|
||||
scope "/v1" do
|
||||
|
||||
post "/users", UserController, :register
|
||||
get "/users/validate/:token", UserController, :validate
|
||||
post "/users/resend", UserController, :resend_confirmation
|
||||
|
||||
post "/users/password-reset/send", UserController, :send_reset_password
|
||||
post "/users/password-reset/post", UserController, :reset_password
|
||||
|
||||
post "/login", UserSessionController, :sign_in
|
||||
get "/groups", GroupController, :index
|
||||
get "/events", EventController, :index
|
||||
@@ -119,6 +125,11 @@ defmodule EventosWeb.Router do
|
||||
post "/inbox", ActivityPubController, :inbox
|
||||
end
|
||||
|
||||
if Mix.env == :dev do
|
||||
# If using Phoenix
|
||||
forward "/sent_emails", Bamboo.SentEmailViewerPlug
|
||||
end
|
||||
|
||||
scope "/", EventosWeb do
|
||||
pipe_through :browser
|
||||
|
||||
|
||||
10
lib/eventos_web/templates/email/email.html.eex
Normal file
10
lib/eventos_web/templates/email/email.html.eex
Normal file
@@ -0,0 +1,10 @@
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="<%= static_url(EventosWeb.Endpoint, "/css/email.css") %>">
|
||||
</head>
|
||||
<body>
|
||||
<%= render @view_module, @view_template, assigns %>
|
||||
|
||||
<p><%= gettext "An email sent by Eventos on %{instance}.", instance: @instance %></p>
|
||||
</body>
|
||||
</html>
|
||||
3
lib/eventos_web/templates/email/email.text.eex
Normal file
3
lib/eventos_web/templates/email/email.text.eex
Normal file
@@ -0,0 +1,3 @@
|
||||
<%= render @view_module, @view_template, assigns %>
|
||||
|
||||
<%= gettext "An email sent by Eventos on %{instance}.", instance: @instance %>
|
||||
5
lib/eventos_web/templates/email/password_reset.html.eex
Normal file
5
lib/eventos_web/templates/email/password_reset.html.eex
Normal file
@@ -0,0 +1,5 @@
|
||||
<h1><%= gettext "Password reset" %></h1>
|
||||
<p><%= gettext "You requested a new password for your account on %{host}.", host: @instance %></p>
|
||||
<p><%= gettext "If you didn't request this, please ignore this email. Your password won't change until you access the link below and create a new one." %></p>
|
||||
|
||||
<p><%= link "Change password", to: EventosWeb.Endpoint.url() <> "/password-reset/#{@token}", target: "_blank" %></p>
|
||||
11
lib/eventos_web/templates/email/password_reset.text.eex
Normal file
11
lib/eventos_web/templates/email/password_reset.text.eex
Normal file
@@ -0,0 +1,11 @@
|
||||
<%= gettext "Password reset" %>
|
||||
|
||||
==
|
||||
|
||||
<%= gettext "You requested a new password for your account on %{host}.", host: @instance %>
|
||||
|
||||
<%= gettext "If you didn't request this, please ignore this email. Your password won't change until you access the link below and create a new one." %>
|
||||
|
||||
<%= EventosWeb.Endpoint.url() <> "/password-reset/#{@token}" %>
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<h1><%= gettext "Confirm the email address" %></h1>
|
||||
<p><%= gettext "You created an account on %{host} with this email address. You are one click away from activating it. If this wasn't you, please ignore this email.", host: @instance %></p>
|
||||
|
||||
<p><%= link "Confirm your email address", to: EventosWeb.Endpoint.url() <> "/validate/#{@token}", target: "_blank" %></p>
|
||||
@@ -0,0 +1,9 @@
|
||||
<%= gettext "Confirm the email address" %>
|
||||
|
||||
==
|
||||
|
||||
<%= gettext "You created an account on %{host} with this email address. You are one click away from activating it. If this wasn't you, please ignore this email.", host: @instance %>
|
||||
|
||||
<%= EventosWeb.Endpoint.url() <> "/validate/#{@token}" %>
|
||||
|
||||
|
||||
3
lib/eventos_web/views/email_view.ex
Normal file
3
lib/eventos_web/views/email_view.ex
Normal file
@@ -0,0 +1,3 @@
|
||||
defmodule Eventos.EmailView do
|
||||
use EventosWeb, :view
|
||||
end
|
||||
@@ -20,7 +20,6 @@ defmodule EventosWeb.EventView do
|
||||
def render("event_for_actor.json", %{event: event}) do
|
||||
%{id: event.id,
|
||||
title: event.title,
|
||||
slug: event.slug,
|
||||
uuid: event.uuid,
|
||||
}
|
||||
end
|
||||
@@ -28,7 +27,6 @@ defmodule EventosWeb.EventView do
|
||||
def render("event_simple.json", %{event: event}) do
|
||||
%{id: event.id,
|
||||
title: event.title,
|
||||
slug: event.slug,
|
||||
description: event.description,
|
||||
begins_on: event.begins_on,
|
||||
ends_on: event.ends_on,
|
||||
@@ -39,6 +37,7 @@ defmodule EventosWeb.EventView do
|
||||
avatar: event.organizer_actor.avatar_url,
|
||||
},
|
||||
type: "Event",
|
||||
address_type: event.address_type,
|
||||
}
|
||||
end
|
||||
|
||||
@@ -51,8 +50,9 @@ defmodule EventosWeb.EventView do
|
||||
uuid: event.uuid,
|
||||
organizer: render_one(event.organizer_actor, ActorView, "acccount_basic.json"),
|
||||
participants: render_many(event.participants, ActorView, "show_basic.json"),
|
||||
address: render_one(event.address, AddressView, "address.json"),
|
||||
physical_address: render_one(event.physical_address, AddressView, "address.json"),
|
||||
type: "Event",
|
||||
address_type: event.address_type,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -45,4 +45,16 @@ defmodule EventosWeb.UserView do
|
||||
role: user.role,
|
||||
}
|
||||
end
|
||||
|
||||
def render("confirmation.json", %{user: user}) do
|
||||
%{
|
||||
email: user.email,
|
||||
}
|
||||
end
|
||||
|
||||
def render("password_reset.json", %{user: user}) do
|
||||
%{
|
||||
email: user.email,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -7,13 +7,14 @@ defmodule Mix.Tasks.CreateBot do
|
||||
alias Eventos.Actors
|
||||
alias Eventos.Actors.Bot
|
||||
alias Eventos.Repo
|
||||
alias Eventos.Actors.User
|
||||
import Logger
|
||||
|
||||
@shortdoc "Register user"
|
||||
def run([email, name, summary, type, url]) do
|
||||
Mix.Task.run("app.start")
|
||||
|
||||
with user <- Actors.find_by_email(email),
|
||||
with {:ok, %User{} = user} <- Actors.find_by_email(email),
|
||||
actor <- Actors.register_bot_account(%{name: name, summary: summary}),
|
||||
{:ok, %Bot{} = bot} <- Actors.create_bot(%{"type" => type, "source" => url, "actor_id" => actor.id, "user_id" => user.id}) do
|
||||
bot
|
||||
|
||||
Reference in New Issue
Block a user