diff --git a/.cla/contributors.txt b/.cla/contributors.txt index de436e99..8ade44a9 100644 --- a/.cla/contributors.txt +++ b/.cla/contributors.txt @@ -1,4 +1,5 @@ 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 diff --git a/lib/keila_web/controllers/account_controller.ex b/lib/keila_web/controllers/account_controller.ex index 774e019c..9cd31b9d 100644 --- a/lib/keila_web/controllers/account_controller.ex +++ b/lib/keila_web/controllers/account_controller.ex @@ -35,6 +35,31 @@ defmodule KeilaWeb.AccountController do end end + @spec delete(Plug.Conn.t(), map()) :: Plug.Conn.t() + def delete(conn, params) do + case get_in(params, ["require_confirmation"]) do + "true" -> + render_delete_confirmation(conn) + + _ -> + Keila.Admin.purge_user(conn.assigns.current_user.id) + + conn + |> end_auth_session() + |> redirect(to: Routes.auth_path(conn, :login)) + end + end + + defp render_delete_confirmation(conn) do + account = + Accounts.get_user_account(conn.assigns.current_user.id) + + conn + |> put_meta(:title, gettext("Confirm Account Deletion")) + |> assign(:account, account) + |> render("delete.html") + end + defp render_edit(conn, changeset) do account = Accounts.get_user_account(conn.assigns.current_user.id) credits = if account, do: Accounts.get_credits(account.id) diff --git a/lib/keila_web/router.ex b/lib/keila_web/router.ex index 39e6fa08..4e265ba5 100644 --- a/lib/keila_web/router.ex +++ b/lib/keila_web/router.ex @@ -64,6 +64,7 @@ defmodule KeilaWeb.Router do get "/account", AccountController, :edit put "/account", AccountController, :post_edit get "/account/await-subscription", AccountController, :await_subscription + delete "/account", AccountController, :delete get "/", ProjectController, :index get "/projects/new", ProjectController, :new diff --git a/lib/keila_web/templates/account/delete.html.heex b/lib/keila_web/templates/account/delete.html.heex new file mode 100644 index 00000000..2d4b3e81 --- /dev/null +++ b/lib/keila_web/templates/account/delete.html.heex @@ -0,0 +1,33 @@ +<div class="container flex py-8 sm:py-11 mb-4"> + <h1 class="text-2xl sm:text-3xl"> + <%= gettext("Delete Account?") %> + </h1> +</div> + +<div class="container"> + <div class="p-4 ring ring-red-900"> + <p class="text-lg text-gray-200"> + <%= gettext("Do you really want to delete your account?") %> + </p> + + <.form + for={@conn} + as="account" + action={Routes.account_path(@conn, :delete)} + method="delete" + class="flex flex-col gap-4" + > + <p class="text-lg text-gray-200 font-bold"> + <%= gettext("Deleting an account cannot be undone.") %> + </p> + <div class="flex gap-4 mt-4"> + <a class="button button--text button--large" href={Routes.account_path(@conn, :edit)}> + <%= gettext("Cancel") %> + </a> + <button type="submit" class="button button--warn button--large"> + <%= gettext("Delete") %> + </button> + </div> + </.form> + </div> +</div> diff --git a/lib/keila_web/templates/account/edit.html.heex b/lib/keila_web/templates/account/edit.html.heex index 579268ed..eadc7dfb 100644 --- a/lib/keila_web/templates/account/edit.html.heex +++ b/lib/keila_web/templates/account/edit.html.heex @@ -237,6 +237,30 @@ </button> </div> </.form> + <.form + let={f} + for={@conn} + action={Routes.account_path(@conn, :delete)} + class="rounded shadow p-8 mt-8 max-w-5xl mx-auto flex flex-col gap-4 bg-gray-900 text-gray-50" + method="delete" + > + <%= hidden_input(f, :require_confirmation, value: "true") %> + <h2 class="text-3xl font-bold"> + <%= gettext("Remove account") %> + </h2> + <p class="text-lg text-gray-200"> + <%= gettext("Here you can remove your Keila account") %> + </p> + <div class="flex justify-end mt-8"> + <button + type="submit" + class="button button--warn button--large" + @click="setUnsavedReminder(false)" + > + <%= gettext("Remove account") %> + </button> + </div> + </.form> </div> <script src="https://cdn.paddle.com/paddle/paddle.js"> diff --git a/test/keila_web/controllers/account_controller_test.exs b/test/keila_web/controllers/account_controller_test.exs index 63c575fd..50a6ff69 100644 --- a/test/keila_web/controllers/account_controller_test.exs +++ b/test/keila_web/controllers/account_controller_test.exs @@ -1,6 +1,7 @@ defmodule KeilaWeb.AccountControllerTest do use KeilaWeb.ConnCase alias Keila.Auth + alias Keila.Accounts describe "GET /account" do @tag :account_controller @@ -28,4 +29,15 @@ defmodule KeilaWeb.AccountControllerTest do assert {:ok, %{id: ^user_id}} = Auth.find_user_by_credentials(credentials) end end + + describe "DELETE /account" do + @tag :account_controller + test "deletes account", %{conn: conn} do + conn = with_login(conn) + + conn = delete(conn, Routes.account_path(conn, :delete)) + assert redirected_to(conn, 302) == Routes.auth_path(conn, :login) + assert nil == Accounts.get_user_account(conn.assigns.current_user.id) + end + end end