diff options
| author | Adam Soltys <[email protected]> | 2026-11-04 14:29:35 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-11-04 14:38:48 -0700 |
| commit | a5ff626228b35ef587f6b9493998184e3e8354fe (patch) | |
| tree | f15d2726fbd391bbd3c4a3a1ca2826ff6c6ac2bd /src/routes/(app) | |
| parent | 976473a781f1bcda7ba02fee9db122e163243e2b (diff) | |
| download | coinos-ui-a5ff626228b35ef587f6b9493998184e3e8354fe.tar.gz coinos-ui-a5ff626228b35ef587f6b9493998184e3e8354fe.zip | |
A Delete account entry on the security tab leads to /settings/delete,
which warns what's lost, asks for the username (and PIN when set) and
calls the server's POST /delete, then logs out. The page resets to the
(app) layout so its form isn't nested in the settings save form.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/routes/(app)')
| -rw-r--r-- | src/routes/(app)/settings/delete/+page.server.ts | 26 | ||||
| -rw-r--r-- | src/routes/(app)/settings/delete/+page@(app).svelte | 82 | ||||
| -rw-r--r-- | src/routes/(app)/settings/security/+page.svelte | 11 |
3 files changed, 119 insertions, 0 deletions
diff --git a/src/routes/(app)/settings/delete/+page.server.ts b/src/routes/(app)/settings/delete/+page.server.ts new file mode 100644 index 00000000..05f4f026 --- /dev/null +++ b/src/routes/(app)/settings/delete/+page.server.ts @@ -0,0 +1,26 @@ +import { auth, post } from "$lib/utils"; +import { fail, redirect } from "@sveltejs/kit"; + +export async function load({ parent, url }) { + const { user } = await parent(); + if (!user) redirect(307, `/login?redirect=${encodeURIComponent(url.pathname)}`); +} + +export const actions = { + default: async ({ cookies, request }) => { + const form = await request.formData(); + const confirm = String(form.get("confirm") || ""); + const pin = String(form.get("pin") || ""); + + try { + // The server wipes everything (lib/deluser), including disabling the + // account's bolt12 offers, and refuses while a real balance remains. + await post("/delete", { confirm, ...(pin ? { pin } : {}) }, auth(cookies)); + } catch (e) { + return fail(400, { error: e?.message || String(e), confirm }); + } + + // The account is gone; /logout clears the session cookies. + redirect(303, "/logout"); + }, +}; diff --git a/src/routes/(app)/settings/delete/+page@(app).svelte b/src/routes/(app)/settings/delete/+page@(app).svelte new file mode 100644 index 00000000..e19a6604 --- /dev/null +++ b/src/routes/(app)/settings/delete/+page@(app).svelte @@ -0,0 +1,82 @@ +<script> + // Rendered outside the settings layout (+page@(app)) so this form isn't + // nested inside the settings save form. + import { enhance } from "$app/forms"; + import { t } from "$lib/translations"; + + let { data, form } = $props(); + let { user } = $derived(data); + + let confirm = $state(form?.confirm || ""); + let working = $state(false); + let matches = $derived( + confirm.trim().toLowerCase() === user.username.toLowerCase(), + ); +</script> + +<div class="container px-4 max-w-xl mx-auto space-y-5 mt-10"> + <h1 class="text-3xl font-bold text-center"> + {$t("user.settings.deleteAccount")} + </h1> + + <div class="rounded-2xl border-2 border-error p-4 space-y-3"> + <p class="flex gap-2"> + <iconify-icon + noobserver + icon="ph:warning-bold" + width="24" + class="text-error shrink-0" + ></iconify-icon> + <span>{$t("user.settings.deleteAccountWarning")}</span> + </p> + <p class="text-secondary">{$t("user.settings.deleteAccountBalance")}</p> + </div> + + {#if form?.error}<p class="text-error">{form.error}</p>{/if} + + <form + method="POST" + class="space-y-3" + use:enhance={() => { + working = true; + return async ({ update }) => { + await update({ reset: false }); + working = false; + }; + }} + > + <label class="block space-y-1"> + <span class="font-bold">{$t("user.settings.deleteAccountConfirm")}</span> + <input + class="input input-bordered w-full" + name="confirm" + autocomplete="off" + autocapitalize="off" + spellcheck="false" + placeholder={user.username} + bind:value={confirm} + /> + </label> + + {#if user.haspin} + <input + class="input input-bordered w-full" + type="password" + inputmode="numeric" + name="pin" + placeholder={$t("user.settings.securityPIN")} + /> + {/if} + + <button + class="btn btn-error w-full rounded-2xl" + disabled={!matches || working} + > + <iconify-icon noobserver icon="ph:trash-bold" width="24"></iconify-icon> + {$t("user.settings.deleteAccountButton")} + </button> + <a href="/settings/security" class="btn w-full rounded-2xl"> + {$t("payments.cancel")} + </a> + </form> +</div> diff --git a/src/routes/(app)/settings/security/+page.svelte b/src/routes/(app)/settings/security/+page.svelte index b774d919..b8c71aa6 100644 --- a/src/routes/(app)/settings/security/+page.svelte +++ b/src/routes/(app)/settings/security/+page.svelte @@ -288,3 +288,14 @@ </button> </a> </div> + +<div> + <span class="font-bold mb-1">{$t("user.settings.deleteAccount")}</span> + <p class="text-secondary mb-4"> + {$t("user.settings.deleteAccountDescription")} + </p> + <a href="/settings/delete" class="btn btn-outline btn-error"> + <iconify-icon noobserver icon="ph:trash-bold" width="32"></iconify-icon> + {$t("user.settings.deleteAccount")} + </a> +</div> |
