diff --git a/config/config.exs b/config/config.exs index 99cfae1..81fda3e 100644 --- a/config/config.exs +++ b/config/config.exs @@ -67,6 +67,14 @@ config :logger, :console, # Use Jason for JSON parsing in Phoenix config :phoenix, :json_library, Jason +config :basket, :pow, + web_mailer_module: BasketWeb, + user: Basket.Users.User, + repo: Basket.Repo, + extensions: [PowResetPassword, PowEmailConfirmation], + controller_callbacks: Pow.Extension.Phoenix.ControllerCallbacks, + mailer: MyAppWeb.Pow.Mailer + # Import environment specific config. This must remain at the bottom # of this file so it overrides the configuration defined above. import_config "#{config_env()}.exs" diff --git a/lib/basket/users/user.ex b/lib/basket/users/user.ex new file mode 100644 index 0000000..114c862 --- /dev/null +++ b/lib/basket/users/user.ex @@ -0,0 +1,21 @@ +defmodule Basket.Users.User do + @moduledoc false + + use Ecto.Schema + use Pow.Ecto.Schema + + use Pow.Extension.Ecto.Schema, + extensions: [PowResetPassword, PowEmailConfirmation] + + def changeset(user_or_changeset, attrs) do + user_or_changeset + |> pow_changeset(attrs) + |> pow_extension_changeset(attrs) + end + + schema "users" do + pow_user_fields() + + timestamps() + end +end diff --git a/lib/basket_web.ex b/lib/basket_web.ex index b5c3a52..c13ca52 100644 --- a/lib/basket_web.ex +++ b/lib/basket_web.ex @@ -19,6 +19,14 @@ defmodule BasketWeb do def static_paths, do: ~w(assets fonts images favicon.ico robots.txt) + def mail do + quote do + use Pow.Phoenix.Mailer.Component + + unquote(html_helpers()) + end + end + def router do quote do use Phoenix.Router, helpers: false diff --git a/lib/basket_web/controllers/pow_invitation/invitation_html.ex b/lib/basket_web/controllers/pow_invitation/invitation_html.ex new file mode 100644 index 0000000..598601f --- /dev/null +++ b/lib/basket_web/controllers/pow_invitation/invitation_html.ex @@ -0,0 +1,5 @@ +defmodule BasketWeb.PowInvitation.InvitationHTML do + use BasketWeb, :html + + embed_templates "invitation_html/*" +end diff --git a/lib/basket_web/controllers/pow_invitation/invitation_html/edit.html.heex b/lib/basket_web/controllers/pow_invitation/invitation_html/edit.html.heex new file mode 100644 index 0000000..d296a9c --- /dev/null +++ b/lib/basket_web/controllers/pow_invitation/invitation_html/edit.html.heex @@ -0,0 +1,35 @@ +
Please use the following link to confirm your e-mail address:
+ + """, + text: ~P""" + Hi, + + Please use the following link to confirm your e-mail address: + + <%= @url %> + """ + } + end +end diff --git a/lib/basket_web/mails/pow_invitation_mail.ex b/lib/basket_web/mails/pow_invitation_mail.ex new file mode 100644 index 0000000..bb39415 --- /dev/null +++ b/lib/basket_web/mails/pow_invitation_mail.ex @@ -0,0 +1,25 @@ +defmodule BasketWeb.PowInvitationMail do + @moduledoc false + + use BasketWeb, :mail + + def invitation(assigns) do + %Pow.Phoenix.Mailer.Template{ + subject: "You've been invited", + html: ~H""" ++ You've been invited by <%= @invited_by_user_id %>. Please use the following link to accept your invitation: +
+ + """, + text: ~P""" + Hi, + + You've been invited by <%= @invited_by_user_id %>. Please use the following link to accept your invitation: + + {@url} + """ + } + end +end diff --git a/lib/basket_web/mails/pow_reset_password_mail.ex b/lib/basket_web/mails/pow_reset_password_mail.ex new file mode 100644 index 0000000..e53330c --- /dev/null +++ b/lib/basket_web/mails/pow_reset_password_mail.ex @@ -0,0 +1,26 @@ +defmodule BasketWeb.PowResetPasswordMail do + @moduledoc false + + use BasketWeb, :mail + + def reset_password(assigns) do + %Pow.Phoenix.Mailer.Template{ + subject: "Reset password link", + html: ~H""" +Please use the following link to reset your password:
+ +You can disregard this email if you didn't request a password reset.
+ """, + text: ~P""" + Hi, + + Please use the following link to reset your password: + + <%= @url %> + + You can disregard this email if you didn't request a password reset. + """ + } + end +end diff --git a/lib/basket_web/router.ex b/lib/basket_web/router.ex index cd7190d..9052667 100644 --- a/lib/basket_web/router.ex +++ b/lib/basket_web/router.ex @@ -1,5 +1,9 @@ defmodule BasketWeb.Router do use BasketWeb, :router + use Pow.Phoenix.Router + + use Pow.Extension.Phoenix.Router, + extensions: [PowResetPassword, PowEmailConfirmation, PowInvitation, PowPersistentSession] import Surface.Catalogue.Router @@ -16,6 +20,13 @@ defmodule BasketWeb.Router do plug :accepts, ["json"] end + scope "/" do + pipe_through :browser + + pow_routes() + pow_extension_routes() + end + scope "/", BasketWeb do pipe_through :browser diff --git a/mix.exs b/mix.exs index 650cb20..b7acabf 100644 --- a/mix.exs +++ b/mix.exs @@ -66,14 +66,15 @@ defmodule Basket.MixProject do {:dns_cluster, "~> 0.1.1"}, {:plug_cowboy, "~> 2.5"}, {:surface, "~> 0.11.0"}, - # for surface.init + # for surface.init; possible to remove. {:sourceror, "~> 0.12.0"}, {:surface_catalogue, "~> 0.6.0"}, {:excoveralls, "~> 0.18", only: :test}, {:sobelow, "~> 0.13.0", only: [:dev, :test], runtime: false}, {:credo, "~> 1.7.1", only: [:dev, :test], runtime: false}, {:dialyxir, "~> 1.4.2", runtime: false}, - {:mix_audit, "~> 2.1.1", runtime: false} + {:mix_audit, "~> 2.1.1", runtime: false}, + {:pow, "~> 1.0.34"} ] end diff --git a/mix.lock b/mix.lock index 81e1928..bf9cce3 100644 --- a/mix.lock +++ b/mix.lock @@ -43,6 +43,7 @@ "plug_cowboy": {:hex, :plug_cowboy, "2.6.1", "9a3bbfceeb65eff5f39dab529e5cd79137ac36e913c02067dba3963a26efe9b2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "de36e1a21f451a18b790f37765db198075c25875c64834bcc82d90b309eb6613"}, "plug_crypto": {:hex, :plug_crypto, "2.0.0", "77515cc10af06645abbfb5e6ad7a3e9714f805ae118fa1a70205f80d2d70fe73", [:mix], [], "hexpm", "53695bae57cc4e54566d993eb01074e4d894b65a3766f1c43e2c61a1b0f45ea9"}, "postgrex": {:hex, :postgrex, "0.17.3", "c92cda8de2033a7585dae8c61b1d420a1a1322421df84da9a82a6764580c503d", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "946cf46935a4fdca7a81448be76ba3503cff082df42c6ec1ff16a4bdfbfb098d"}, + "pow": {:hex, :pow, "1.0.34", "51999e624475a4c75d9e5d04fcf7e38b3c5a1f8d09f37c1311d7bef43962aafa", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix, ">= 1.3.0 and < 1.8.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, ">= 2.0.0 and < 4.0.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, ">= 0.18.0", [hex: :phoenix_live_view, repo: "hexpm", optional: true]}, {:plug, ">= 1.5.0 and < 2.0.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "63a5e3b5197a39ac0320224526fb555b2b009852d878d29efc4362537393080b"}, "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, "sobelow": {:hex, :sobelow, "0.13.0", "218afe9075904793f5c64b8837cc356e493d88fddde126a463839351870b8d1e", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cd6e9026b85fc35d7529da14f95e85a078d9dd1907a9097b3ba6ac7ebbe34a0d"}, "sourceror": {:hex, :sourceror, "0.12.3", "a2ad3a1a4554b486d8a113ae7adad5646f938cad99bf8bfcef26dc0c88e8fade", [:mix], [], "hexpm", "4d4e78010ca046524e8194ffc4683422f34a96f6b82901abbb45acc79ace0316"}, diff --git a/priv/repo/migrations/20231031183908_create_users.exs b/priv/repo/migrations/20231031183908_create_users.exs new file mode 100644 index 0000000..e49e54e --- /dev/null +++ b/priv/repo/migrations/20231031183908_create_users.exs @@ -0,0 +1,14 @@ +defmodule Basket.Repo.Migrations.CreateUsers do + use Ecto.Migration + + def change do + create table(:users) do + add :email, :string, null: false + add :password_hash, :string + + timestamps() + end + + create unique_index(:users, [:email]) + end +end diff --git a/priv/repo/migrations/20231031184704_add_pow_email_confirmation_to_users.exs b/priv/repo/migrations/20231031184704_add_pow_email_confirmation_to_users.exs new file mode 100644 index 0000000..0364ff7 --- /dev/null +++ b/priv/repo/migrations/20231031184704_add_pow_email_confirmation_to_users.exs @@ -0,0 +1,13 @@ +defmodule Basket.Repo.Migrations.AddPowEmailConfirmationToUsers do + use Ecto.Migration + + def change do + alter table(:users) do + add :email_confirmation_token, :string + add :email_confirmed_at, :utc_datetime + add :unconfirmed_email, :string + end + + create unique_index(:users, [:email_confirmation_token]) + end +end diff --git a/priv/repo/migrations/20231031184705_add_pow_invitation_to_users.exs b/priv/repo/migrations/20231031184705_add_pow_invitation_to_users.exs new file mode 100644 index 0000000..9162e69 --- /dev/null +++ b/priv/repo/migrations/20231031184705_add_pow_invitation_to_users.exs @@ -0,0 +1,13 @@ +defmodule Basket.Repo.Migrations.AddPowInvitationToUsers do + use Ecto.Migration + + def change do + alter table(:users) do + add :invitation_token, :string + add :invitation_accepted_at, :utc_datetime + add :invited_by_id, references("users", on_delete: :nothing) + end + + create unique_index(:users, [:invitation_token]) + end +end