defmodule BerrypodWeb.Setup.Recover do use BerrypodWeb, :live_view require Logger alias Berrypod.{Accounts, Setup} @impl true def mount(_params, _session, socket) do cond do get_user(socket) -> {:ok, push_navigate(socket, to: ~p"/admin")} not Accounts.has_admin?() -> {:ok, push_navigate(socket, to: ~p"/setup")} true -> Logger.warning("Account recovery requested. Setup secret: #{Setup.setup_secret()}") {:ok, socket |> assign(:page_title, "Account recovery") |> assign(:require_secret, Application.get_env(:berrypod, :env, :dev) == :prod) |> assign(:form, to_form(%{}, as: :recover))} end end defp get_user(socket) do case socket.assigns do %{current_scope: %{user: user}} when not is_nil(user) -> user _ -> nil end end @impl true def handle_event("recover", %{"recover" => params}, socket) do secret = params["secret"] || "" password = params["password"] || "" cond do socket.assigns.require_secret and not Plug.Crypto.secure_compare(secret, Setup.setup_secret()) -> {:noreply, put_flash(socket, :error, "Wrong setup secret")} String.length(password) < 12 -> {:noreply, put_flash(socket, :error, "Password must be at least 12 characters")} true -> user = Accounts.get_first_admin() case Accounts.update_user_password(user, %{password: password}) do {:ok, {_user, _tokens}} -> token = Accounts.generate_login_token(user) {:noreply, redirect(socket, to: ~p"/recover/login/#{token}")} {:error, changeset} -> message = changeset |> Ecto.Changeset.traverse_errors(fn {msg, _} -> msg end) |> Enum.flat_map(fn {_field, msgs} -> msgs end) |> Enum.join(", ") {:noreply, put_flash(socket, :error, message)} end end end @impl true def render(assigns) do ~H"""
<.header> Account recovery <:subtitle> Reset your admin password using the setup secret from your server logs.
<.icon name="hero-information-circle" class="size-6 shrink-0" />

A recovery secret has been printed to your server logs.

<.form for={@form} phx-submit="recover"> <.input :if={@require_secret} name="recover[secret]" value="" type="password" label="Setup secret" autocomplete="off" required /> <.input name="recover[password]" value="" type="password" label="New password" autocomplete="new-password" required />

Minimum 12 characters

<.button variant="primary" class="admin-btn-block"> Reset password and log in
""" end end