From 3b035e930f28d3b48dc79f4bdbcde14cb801c3fd Mon Sep 17 00:00:00 2001 From: Till JS Date: Thu, 9 Apr 2026 18:52:51 +0200 Subject: [PATCH] fix(mana/web/news): use getValidToken + guard prefs against locked vault MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After the previous round of fixes, two issues remained: 1. Feed fetch returned 401 against `mana-api.mana.how`. The new `authHeader()` helper called `authStore.getAccessToken()`, which just reads `@auth/appToken` from localStorage and is happy to return null/stale. The unified sync engine in `sync.ts` uses `authStore.getValidToken()`, which routes through the tokenManager and refreshes if needed. Switched the news client to the same. 2. `Cannot read properties of undefined (reading 'emoji')` from `TOPIC_LABELS[topic]`. When the vault is briefly locked at boot, `decryptRecord` deliberately leaves the encrypted blob string in place — so `local.selectedTopics` can be a string. The `?? []` fallback in `toPreferences` doesn't catch it, and `{#each prefs.selectedTopics}` iterates the blob char-by-char. Force the three array fields (and the two map fields) back to their expected shapes with `Array.isArray` / object checks. --- apps/mana/apps/web/src/lib/modules/news/api.ts | 6 +++++- .../apps/web/src/lib/modules/news/queries.ts | 18 +++++++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/apps/mana/apps/web/src/lib/modules/news/api.ts b/apps/mana/apps/web/src/lib/modules/news/api.ts index ff5f4132d..49b3f046c 100644 --- a/apps/mana/apps/web/src/lib/modules/news/api.ts +++ b/apps/mana/apps/web/src/lib/modules/news/api.ts @@ -25,7 +25,11 @@ import { authStore } from '$lib/stores/auth.svelte'; import { getManaApiUrl } from '$lib/api/config'; async function authHeader(): Promise> { - const token = await authStore.getAccessToken(); + // getValidToken (not getAccessToken) — runs the token through the + // tokenManager so it refreshes if expired. getAccessToken just reads + // localStorage and returns null/stale, which is what made the first + // pass at this fix still 401. sync.ts uses the same getValidToken. + const token = await authStore.getValidToken(); return token ? { Authorization: `Bearer ${token}` } : {}; } diff --git a/apps/mana/apps/web/src/lib/modules/news/queries.ts b/apps/mana/apps/web/src/lib/modules/news/queries.ts index 580e05838..9b689a8f0 100644 --- a/apps/mana/apps/web/src/lib/modules/news/queries.ts +++ b/apps/mana/apps/web/src/lib/modules/news/queries.ts @@ -69,13 +69,21 @@ export function toCategory(local: LocalCategory): Category { } export function toPreferences(local: LocalPreferences): Preferences { + // Force the array fields back to arrays even if decryption left an + // encrypted blob string in place (vault locked at boot). Without this + // guard `{#each prefs.selectedTopics}` iterates the encrypted string + // char-by-char and crashes `TOPIC_LABELS[topic].emoji` on render. return { id: local.id, - selectedTopics: local.selectedTopics ?? [], - blockedSources: local.blockedSources ?? [], - preferredLanguages: local.preferredLanguages ?? ['de', 'en'], - topicWeights: local.topicWeights ?? {}, - sourceWeights: local.sourceWeights ?? {}, + selectedTopics: Array.isArray(local.selectedTopics) ? local.selectedTopics : [], + blockedSources: Array.isArray(local.blockedSources) ? local.blockedSources : [], + preferredLanguages: Array.isArray(local.preferredLanguages) + ? local.preferredLanguages + : ['de', 'en'], + topicWeights: + local.topicWeights && typeof local.topicWeights === 'object' ? local.topicWeights : {}, + sourceWeights: + local.sourceWeights && typeof local.sourceWeights === 'object' ? local.sourceWeights : {}, onboardingCompleted: local.onboardingCompleted ?? false, }; }