From 31b3cc78b4c61e3ecccb28877a725a8e7cd6592a Mon Sep 17 00:00:00 2001 From: Frederic Cabassut Date: Mon, 9 Mar 2026 02:19:47 +0100 Subject: [PATCH] feat: add logout button and delete account to settings Co-Authored-By: Claude Opus 4.6 --- src/routes/(app)/settings/+page.svelte | 80 +++++++++++++++++++++++++- src/routes/api/auth/account/+server.ts | 31 ++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 src/routes/api/auth/account/+server.ts diff --git a/src/routes/(app)/settings/+page.svelte b/src/routes/(app)/settings/+page.svelte index 6f16063..3340b0b 100644 --- a/src/routes/(app)/settings/+page.svelte +++ b/src/routes/(app)/settings/+page.svelte @@ -30,6 +30,12 @@ let activeSessions = $state([]); let sessionsLoaded = $state(false); + // Delete account state + let deletePassword = $state(''); + let deleteMsg = $state(''); + let deleteError = $state(false); + let showDeleteConfirm = $state(false); + async function loadSessions() { try { const res = await fetch('/api/auth/sessions'); @@ -80,6 +86,32 @@ return new Date(d).toLocaleString(); } + async function deleteAccount() { + deleteMsg = ''; + try { + const res = await fetch('/api/auth/account', { + method: 'DELETE', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ password: deletePassword }) + }); + if (res.ok) { + window.location.href = '/login'; + } else { + const d = await res.json(); + deleteMsg = d.message || 'Failed to delete account'; + deleteError = true; + } + } catch { + deleteMsg = 'Connection error'; + deleteError = true; + } + } + + async function handleLogout() { + await fetch('/api/auth/logout', { method: 'POST' }); + window.location.href = '/login'; + } + async function saveProfile() { profileMsg = ''; try { @@ -220,7 +252,7 @@ -
+

Active Sessions

{#if activeSessions.length > 1} @@ -265,3 +297,49 @@
{/if}
+ + +
+
+ + +
+ + {#if showDeleteConfirm} +
+

+ This will permanently delete your account and all your notes. This cannot be undone. +

+ + {#if deleteMsg} +
{deleteMsg}
+ {/if} + +
+ + +
+
+ {/if} +
diff --git a/src/routes/api/auth/account/+server.ts b/src/routes/api/auth/account/+server.ts new file mode 100644 index 0000000..7fb48c8 --- /dev/null +++ b/src/routes/api/auth/account/+server.ts @@ -0,0 +1,31 @@ +import { json, error } from '@sveltejs/kit'; +import type { RequestHandler } from './$types.js'; +import { getUserId } from '$lib/server/api-utils.js'; +import { verifyPassword, deleteUser, deleteSession, getUser } from '$lib/server/auth.js'; + +export const DELETE: RequestHandler = async ({ request, cookies, ...event }) => { + const userId = getUserId(event); + const user = getUser(userId); + if (!user) throw error(404, 'User not found'); + + const body = await request.json(); + + // OAuth users don't have passwords — allow deletion without password + if (user.authProvider === 'password') { + if (!body.password) throw error(400, 'Password is required'); + const verified = await verifyPassword(user.email, body.password); + if (!verified) throw error(401, 'Invalid password'); + } + + // Delete user and all associated data + await deleteUser(userId); + + // Clear session cookie + const token = cookies.get('session'); + if (token) { + await deleteSession(token); + cookies.delete('session', { path: '/' }); + } + + return json({ success: true }); +};