Files
upbeatBytes/frontend/src/lib/auth.svelte.js
T
thejayman77 bb008cfaa5 Accounts Phase 4: prefs sync + account/settings panel
- 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) <noreply@anthropic.com>
2026-06-03 14:02:38 +00:00

72 lines
1.8 KiB
JavaScript

// Shared, reactive auth + saved-articles state (Svelte 5 runes in a module).
import { SvelteSet } from 'svelte/reactivity';
import { getJSON, postJSON, delJSON } from './api.js';
export const auth = $state({ user: null, ready: false });
export const savedIds = new SvelteSet(); // reactive set of saved article ids
export async function refresh() {
try {
auth.user = await getJSON('/api/auth/me');
} catch {
auth.user = null;
} finally {
auth.ready = true;
}
if (auth.user) await loadSaved();
else savedIds.clear();
}
export async function loadSaved() {
try {
const ids = await getJSON('/api/saved/ids');
savedIds.clear();
for (const id of ids) savedIds.add(id);
} catch {
/* not signed in / transient — leave as-is */
}
}
// Optimistic toggle; reverts on failure.
export async function toggleSave(id) {
if (!auth.user) return false;
const willSave = !savedIds.has(id);
if (willSave) savedIds.add(id);
else savedIds.delete(id);
try {
if (willSave) await postJSON(`/api/saved/${id}`, {});
else await delJSON(`/api/saved/${id}`);
} catch {
if (willSave) savedIds.delete(id);
else savedIds.add(id);
}
return savedIds.has(id);
}
// Request a magic link. Reply is intentionally identical for any address.
export function startEmail(email) {
return postJSON('/api/auth/email/start', { email });
}
export async function verifyToken(token) {
const res = await postJSON('/api/auth/email/verify', { token });
auth.user = res.user;
await loadSaved();
return res;
}
export async function logout() {
try {
await postJSON('/api/auth/logout', {});
} finally {
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();
}