From bb008cfaa545b5bc449f5210d2646b4cb798c434 Mon Sep 17 00:00:00 2001 From: jay Date: Wed, 3 Jun 2026 14:02:38 +0000 Subject: [PATCH] Accounts Phase 4: prefs sync + account/settings panel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Prefs sync: GET/PUT /api/prefs store Calm Filters/Boundaries on the account. On sign-in the client adopts the account's prefs if present, else seeds them from the device; every change PUTs to the account so tuning follows you across devices. (Login side-effects run under untrack so browsing doesn't re-trigger.) - Account panel: GET /api/account (email, connected sign-in methods, saved count, active sessions); Export my data (GET /api/account/export → JSON download); Sign out everywhere (revoke all sessions); Delete account (cascades to all account data) with an inline confirm. Reachable from You → Account. Deferred to a follow-up: link/unlink a provider (OAuth link-mode) and per-session revoke. 118 tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) --- frontend/src/lib/api.js | 4 + frontend/src/lib/auth.svelte.js | 10 +- .../src/lib/components/AccountPanel.svelte | 119 ++++++++++++++++++ frontend/src/routes/+page.svelte | 52 ++++++-- goodnews/api.py | 103 +++++++++++++++ tests/test_account_api.py | 71 +++++++++++ 6 files changed, 345 insertions(+), 14 deletions(-) create mode 100644 frontend/src/lib/components/AccountPanel.svelte create mode 100644 tests/test_account_api.py diff --git a/frontend/src/lib/api.js b/frontend/src/lib/api.js index 5d00cdd..be63130 100644 --- a/frontend/src/lib/api.js +++ b/frontend/src/lib/api.js @@ -8,6 +8,10 @@ export async function postJSON(url, body) { return sendJSON('POST', url, body); } +export async function putJSON(url, body) { + return sendJSON('PUT', url, body); +} + export async function delJSON(url) { return sendJSON('DELETE', url); } diff --git a/frontend/src/lib/auth.svelte.js b/frontend/src/lib/auth.svelte.js index e702a4a..fc01bde 100644 --- a/frontend/src/lib/auth.svelte.js +++ b/frontend/src/lib/auth.svelte.js @@ -59,7 +59,13 @@ export async function logout() { try { await postJSON('/api/auth/logout', {}); } finally { - auth.user = null; - savedIds.clear(); + clearLocal(); } } + +// Clear client auth state without a logout call (cookie already cleared server-side, +// e.g. after delete-account or sign-out-everywhere). +export function clearLocal() { + auth.user = null; + savedIds.clear(); +} diff --git a/frontend/src/lib/components/AccountPanel.svelte b/frontend/src/lib/components/AccountPanel.svelte new file mode 100644 index 0000000..6b2e2dd --- /dev/null +++ b/frontend/src/lib/components/AccountPanel.svelte @@ -0,0 +1,119 @@ + + +
+
+

Account

+ +
+ + {#if error}

{error}

{/if} + + {#if info} +
Email{info.user.email}
+
+ Sign-in methods + {info.providers.map((p) => PROVIDER_LABEL[p] ?? p).join(', ')} +
+
Saved articles{info.saved_count}
+
Active sessions{info.sessions}
+ +
+ Export my data + +
+ +
+ {#if !confirmingDelete} + + {:else} +

This permanently removes your account, saved articles, and history. This can't be undone.

+
+ + +
+ {/if} +
+ +

Browsing without an account still works — signing out keeps your saved items safe on the server for next time.

+ {:else if !error} +

Loading…

+ {/if} +
+ + diff --git a/frontend/src/routes/+page.svelte b/frontend/src/routes/+page.svelte index 54c1f8e..4f46f01 100644 --- a/frontend/src/routes/+page.svelte +++ b/frontend/src/routes/+page.svelte @@ -1,6 +1,6 @@