update main routes
This commit is contained in:
		| @@ -1,10 +1,13 @@ | ||||
| defmodule Lokal.Accounts.User do | ||||
|   @moduledoc """ | ||||
|   Schema for a registered user | ||||
|   A Lokal user | ||||
|   """ | ||||
|  | ||||
|   use Ecto.Schema | ||||
|   import Ecto.Changeset | ||||
|   import LokalWeb.Gettext | ||||
|   alias Ecto.{Changeset, UUID} | ||||
|   alias Lokal.{Accounts.User, Invites.Invite} | ||||
|  | ||||
|   @derive {Inspect, except: [:password]} | ||||
|   @primary_key {:id, :binary_id, autogenerate: true} | ||||
| @@ -14,10 +17,27 @@ defmodule Lokal.Accounts.User do | ||||
|     field :password, :string, virtual: true | ||||
|     field :hashed_password, :string | ||||
|     field :confirmed_at, :naive_datetime | ||||
|     field :role, Ecto.Enum, values: [:admin, :user], default: :user | ||||
|  | ||||
|     has_many :invites, Invite, on_delete: :delete_all | ||||
|  | ||||
|     timestamps() | ||||
|   end | ||||
|  | ||||
|   @type t :: %User{ | ||||
|           id: id(), | ||||
|           email: String.t(), | ||||
|           password: String.t(), | ||||
|           hashed_password: String.t(), | ||||
|           confirmed_at: NaiveDateTime.t(), | ||||
|           role: atom(), | ||||
|           invites: [Invite.t()], | ||||
|           inserted_at: NaiveDateTime.t(), | ||||
|           updated_at: NaiveDateTime.t() | ||||
|         } | ||||
|   @type new_user :: %User{} | ||||
|   @type id :: UUID.t() | ||||
|  | ||||
|   @doc """ | ||||
|   A user changeset for registration. | ||||
|  | ||||
| @@ -35,22 +55,39 @@ defmodule Lokal.Accounts.User do | ||||
|       validations on a LiveView form), this option can be set to `false`. | ||||
|       Defaults to `true`. | ||||
|   """ | ||||
|   @spec registration_changeset(t() | new_user(), attrs :: map()) :: Changeset.t(t() | new_user()) | ||||
|   @spec registration_changeset(t() | new_user(), attrs :: map(), opts :: keyword()) :: | ||||
|           Changeset.t(t() | new_user()) | ||||
|   def registration_changeset(user, attrs, opts \\ []) do | ||||
|     user | ||||
|     |> cast(attrs, [:email, :password]) | ||||
|     |> cast(attrs, [:email, :password, :role]) | ||||
|     |> validate_email() | ||||
|     |> validate_password(opts) | ||||
|   end | ||||
|  | ||||
|   @doc """ | ||||
|   A user changeset for role. | ||||
|  | ||||
|   """ | ||||
|   @spec role_changeset(t(), role :: atom()) :: Changeset.t(t()) | ||||
|   def role_changeset(user, role) do | ||||
|     user |> cast(%{"role" => role}, [:role]) | ||||
|   end | ||||
|  | ||||
|   @spec validate_email(Changeset.t(t() | new_user())) :: Changeset.t(t() | new_user()) | ||||
|   defp validate_email(changeset) do | ||||
|     changeset | ||||
|     |> validate_required([:email]) | ||||
|     |> validate_format(:email, ~r/^[^\s]+@[^\s]+$/, message: "must have the @ sign and no spaces") | ||||
|     |> validate_format(:email, ~r/^[^\s]+@[^\s]+$/, | ||||
|       message: dgettext("errors", "must have the @ sign and no spaces") | ||||
|     ) | ||||
|     |> validate_length(:email, max: 160) | ||||
|     |> unsafe_validate_unique(:email, Lokal.Repo) | ||||
|     |> unique_constraint(:email) | ||||
|   end | ||||
|  | ||||
|   @spec validate_password(Changeset.t(t() | new_user()), opts :: keyword()) :: | ||||
|           Changeset.t(t() | new_user()) | ||||
|   defp validate_password(changeset, opts) do | ||||
|     changeset | ||||
|     |> validate_required([:password]) | ||||
| @@ -61,6 +98,8 @@ defmodule Lokal.Accounts.User do | ||||
|     |> maybe_hash_password(opts) | ||||
|   end | ||||
|  | ||||
|   @spec maybe_hash_password(Changeset.t(t() | new_user()), opts :: keyword()) :: | ||||
|           Changeset.t(t() | new_user()) | ||||
|   defp maybe_hash_password(changeset, opts) do | ||||
|     hash_password? = Keyword.get(opts, :hash_password, true) | ||||
|     password = get_change(changeset, :password) | ||||
| @@ -79,13 +118,14 @@ defmodule Lokal.Accounts.User do | ||||
|  | ||||
|   It requires the email to change otherwise an error is added. | ||||
|   """ | ||||
|   @spec email_changeset(t(), attrs :: map()) :: Changeset.t(t()) | ||||
|   def email_changeset(user, attrs) do | ||||
|     user | ||||
|     |> cast(attrs, [:email]) | ||||
|     |> validate_email() | ||||
|     |> case do | ||||
|       %{changes: %{email: _}} = changeset -> changeset | ||||
|       %{} = changeset -> add_error(changeset, :email, "did not change") | ||||
|       %{} = changeset -> add_error(changeset, :email, dgettext("errors", "did not change")) | ||||
|     end | ||||
|   end | ||||
|  | ||||
| @@ -101,19 +141,22 @@ defmodule Lokal.Accounts.User do | ||||
|       validations on a LiveView form), this option can be set to `false`. | ||||
|       Defaults to `true`. | ||||
|   """ | ||||
|   @spec password_changeset(t(), attrs :: map()) :: Changeset.t(t()) | ||||
|   @spec password_changeset(t(), attrs :: map(), opts :: keyword()) :: Changeset.t(t()) | ||||
|   def password_changeset(user, attrs, opts \\ []) do | ||||
|     user | ||||
|     |> cast(attrs, [:password]) | ||||
|     |> validate_confirmation(:password, message: "does not match password") | ||||
|     |> validate_confirmation(:password, message: dgettext("errors", "does not match password")) | ||||
|     |> validate_password(opts) | ||||
|   end | ||||
|  | ||||
|   @doc """ | ||||
|   Confirms the account by setting `confirmed_at`. | ||||
|   """ | ||||
|   def confirm_changeset(user) do | ||||
|   @spec confirm_changeset(t() | Changeset.t(t())) :: Changeset.t(t()) | ||||
|   def confirm_changeset(user_or_changeset) do | ||||
|     now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second) | ||||
|     change(user, confirmed_at: now) | ||||
|     user_or_changeset |> change(confirmed_at: now) | ||||
|   end | ||||
|  | ||||
|   @doc """ | ||||
| @@ -122,7 +165,8 @@ defmodule Lokal.Accounts.User do | ||||
|   If there is no user or the user doesn't have a password, we call | ||||
|   `Bcrypt.no_user_verify/0` to avoid timing attacks. | ||||
|   """ | ||||
|   def valid_password?(%Lokal.Accounts.User{hashed_password: hashed_password}, password) | ||||
|   @spec valid_password?(t(), String.t()) :: boolean() | ||||
|   def valid_password?(%User{hashed_password: hashed_password}, password) | ||||
|       when is_binary(hashed_password) and byte_size(password) > 0 do | ||||
|     Bcrypt.verify_pass(password, hashed_password) | ||||
|   end | ||||
| @@ -135,11 +179,10 @@ defmodule Lokal.Accounts.User do | ||||
|   @doc """ | ||||
|   Validates the current password otherwise adds an error to the changeset. | ||||
|   """ | ||||
|   @spec validate_current_password(Changeset.t(t()), String.t()) :: Changeset.t(t()) | ||||
|   def validate_current_password(changeset, password) do | ||||
|     if valid_password?(changeset.data, password) do | ||||
|       changeset | ||||
|     else | ||||
|       add_error(changeset, :current_password, "is not valid") | ||||
|     end | ||||
|     if valid_password?(changeset.data, password), | ||||
|       do: changeset, | ||||
|       else: changeset |> add_error(:current_password, dgettext("errors", "is not valid")) | ||||
|   end | ||||
| end | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| defmodule LokalWeb.Router do | ||||
|   use LokalWeb, :router | ||||
|   import Phoenix.LiveDashboard.Router | ||||
|   import LokalWeb.UserAuth | ||||
|  | ||||
|   pipeline :browser do | ||||
| @@ -12,6 +13,10 @@ defmodule LokalWeb.Router do | ||||
|     plug :fetch_current_user | ||||
|   end | ||||
|  | ||||
|   pipeline :require_admin do | ||||
|     plug :require_role, role: :admin | ||||
|   end | ||||
|  | ||||
|   pipeline :api do | ||||
|     plug :accepts, ["json"] | ||||
|   end | ||||
| @@ -22,39 +27,6 @@ defmodule LokalWeb.Router do | ||||
|     live "/", PageLive | ||||
|   end | ||||
|  | ||||
|   # Other scopes may use custom stacks. | ||||
|   # scope "/api", LokalWeb do | ||||
|   #   pipe_through :api | ||||
|   # end | ||||
|  | ||||
|   # Enables LiveDashboard only for development | ||||
|   # | ||||
|   # If you want to use the LiveDashboard in production, you should put | ||||
|   # it behind authentication and allow only admins to access it. | ||||
|   # If your application does not have an admins-only section yet, | ||||
|   # you can use Plug.BasicAuth to set up some basic authentication | ||||
|   # as long as you are also using SSL (which you should anyway). | ||||
|   if Mix.env() in [:dev, :test] do | ||||
|     import Phoenix.LiveDashboard.Router | ||||
|  | ||||
|     scope "/" do | ||||
|       pipe_through :browser | ||||
|       live_dashboard "/dashboard", metrics: LokalWeb.Telemetry | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   # Enables the Swoosh mailbox preview in development. | ||||
|   # | ||||
|   # Note that preview only shows emails that were sent by the same | ||||
|   # node running the Phoenix server. | ||||
|   if Mix.env() == :dev do | ||||
|     scope "/dev" do | ||||
|       pipe_through :browser | ||||
|  | ||||
|       forward "/mailbox", Plug.Swoosh.MailboxPreview | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   ## Authentication routes | ||||
|  | ||||
|   scope "/", LokalWeb do | ||||
| @@ -75,9 +47,20 @@ defmodule LokalWeb.Router do | ||||
|  | ||||
|     get "/users/settings", UserSettingsController, :edit | ||||
|     put "/users/settings", UserSettingsController, :update | ||||
|     delete "/users/settings/:id", UserSettingsController, :delete | ||||
|     get "/users/settings/confirm_email/:token", UserSettingsController, :confirm_email | ||||
|   end | ||||
|  | ||||
|   scope "/", LokalWeb do | ||||
|     pipe_through [:browser, :require_authenticated_user, :require_admin] | ||||
|  | ||||
|     live_dashboard "/dashboard", metrics: LokalWeb.Telemetry, ecto_repos: [Lokal.Repo] | ||||
|  | ||||
|     live "/invites", InviteLive.Index, :index | ||||
|     live "/invites/new", InviteLive.Index, :new | ||||
|     live "/invites/:id/edit", InviteLive.Index, :edit | ||||
|   end | ||||
|  | ||||
|   scope "/", LokalWeb do | ||||
|     pipe_through [:browser] | ||||
|  | ||||
| @@ -86,4 +69,20 @@ defmodule LokalWeb.Router do | ||||
|     post "/users/confirm", UserConfirmationController, :create | ||||
|     get "/users/confirm/:token", UserConfirmationController, :confirm | ||||
|   end | ||||
|  | ||||
|   # Enables the Swoosh mailbox preview in development. | ||||
|   # | ||||
|   # Note that preview only shows emails that were sent by the same | ||||
|   # node running the Phoenix server. | ||||
|   if Mix.env() == :dev do | ||||
|     scope "/dev" do | ||||
|       pipe_through :browser | ||||
|  | ||||
|       forward "/mailbox", Plug.Swoosh.MailboxPreview | ||||
|     end | ||||
|  | ||||
|     scope "/dev" do | ||||
|       get "/preview/:id", LokalWeb.EmailController, :preview | ||||
|     end | ||||
|   end | ||||
| end | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| defmodule LokalWeb.Telemetry do | ||||
|   @moduledoc """ | ||||
|   Telemetry genserver | ||||
|   Collects telemetry | ||||
|   """ | ||||
|  | ||||
|   use Supervisor | ||||
|   | ||||
		Reference in New Issue
	
	Block a user