mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 21:21:10 +02:00
fix(mana/web/news): use client-side API URL + snapshot $state arrays
Onboarding's "Fertig" button was failing with two distinct errors: 1. Feed fetch hit `http://mana-api:3060/api/v1/news/feed` (the SSR-only internal Docker hostname) and was blocked by CSP. The news client was reading `$env/dynamic/public.PUBLIC_MANA_API_URL`, which on the client resolves to whatever the SSR process had — i.e. the internal hostname. Switched to the existing `getManaApiUrl()` helper, which on the client reads `window.__PUBLIC_MANA_API_URL__` (set from `PUBLIC_MANA_API_URL_CLIENT` = `https://mana-api.mana.how`). 2. `completeOnboarding` passed Svelte 5 `$state` proxy arrays directly into the preferences store, which then handed them to Dexie's update hook → `_pendingChanges.add` → `DataCloneError`. The picked arrays are now snapshotted with `$state.snapshot()` at the call site, and the store-side setters defensively spread their inputs so any future caller is safe by default.
This commit is contained in:
parent
59b5114348
commit
4fab323234
3 changed files with 28 additions and 19 deletions
|
|
@ -5,17 +5,20 @@
|
|||
* - GET /feed — pulls the curated pool, with topic/lang filters
|
||||
* - POST /extract/* — Mozilla Readability for ad-hoc URL saves
|
||||
*
|
||||
* The base URL is read from `PUBLIC_MANA_API_URL` if set (production
|
||||
* docker setup), otherwise falls back to localhost dev. Auth is the
|
||||
* unified Mana JWT, picked up by the same fetch wrapper the rest of
|
||||
* the app uses (cookie + Authorization header set by SvelteKit `fetch`
|
||||
* via the auth-provider middleware).
|
||||
* The base URL comes from `getManaApiUrl()`, which on the client reads the
|
||||
* browser-injected `__PUBLIC_MANA_API_URL__` (set from
|
||||
* `PUBLIC_MANA_API_URL_CLIENT` in hooks.server.ts → e.g.
|
||||
* `https://mana-api.mana.how`) and on the server reads `process.env`
|
||||
* directly. Reading `$env/dynamic/public.PUBLIC_MANA_API_URL` here would
|
||||
* leak the SSR-side internal Docker hostname (`http://mana-api:3060`) to
|
||||
* the browser and trip CSP / DNS.
|
||||
*
|
||||
* Auth is the unified Mana JWT, picked up by the same fetch wrapper the
|
||||
* rest of the app uses (cookie + Authorization header set by SvelteKit
|
||||
* `fetch` via the auth-provider middleware).
|
||||
*/
|
||||
|
||||
import { env as publicEnv } from '$env/dynamic/public';
|
||||
|
||||
const API_BASE =
|
||||
publicEnv.PUBLIC_MANA_API_URL || (typeof window !== 'undefined' ? '' : 'http://localhost:3060');
|
||||
import { getManaApiUrl } from '$lib/api/config';
|
||||
|
||||
export interface FeedArticleDto {
|
||||
id: string;
|
||||
|
|
@ -57,7 +60,7 @@ export async function fetchFeed(
|
|||
if (query.limit != null) params.set('limit', String(query.limit));
|
||||
if (query.offset != null) params.set('offset', String(query.offset));
|
||||
|
||||
const url = `${API_BASE}/api/v1/news/feed${params.toString() ? `?${params}` : ''}`;
|
||||
const url = `${getManaApiUrl()}/api/v1/news/feed${params.toString() ? `?${params}` : ''}`;
|
||||
const response = await fetchImpl(url, { credentials: 'include' });
|
||||
if (!response.ok) {
|
||||
throw new Error(`fetchFeed failed: ${response.status}`);
|
||||
|
|
@ -87,7 +90,7 @@ export async function extractFromUrl(
|
|||
url: string,
|
||||
fetchImpl: typeof fetch = fetch
|
||||
): Promise<ExtractedArticleDto> {
|
||||
const response = await fetchImpl(`${API_BASE}/api/v1/news/extract/save`, {
|
||||
const response = await fetchImpl(`${getManaApiUrl()}/api/v1/news/extract/save`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
|
|
|
|||
|
|
@ -33,10 +33,13 @@ export const preferencesStore = {
|
|||
blockedSources?: string[];
|
||||
}): Promise<void> {
|
||||
await ensureRow();
|
||||
// Spread the input arrays — callers in onboarding pass Svelte 5
|
||||
// `$state` proxy arrays, which IndexedDB cannot structured-clone
|
||||
// (DataCloneError on the Dexie hook's _pendingChanges write).
|
||||
const diff: Partial<LocalPreferences> = {
|
||||
selectedTopics: input.topics,
|
||||
preferredLanguages: input.languages,
|
||||
blockedSources: input.blockedSources ?? [],
|
||||
selectedTopics: [...input.topics],
|
||||
preferredLanguages: [...input.languages],
|
||||
blockedSources: [...(input.blockedSources ?? [])],
|
||||
onboardingCompleted: true,
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
|
|
@ -47,7 +50,7 @@ export const preferencesStore = {
|
|||
async setTopics(topics: Topic[]): Promise<void> {
|
||||
await ensureRow();
|
||||
const diff: Partial<LocalPreferences> = {
|
||||
selectedTopics: topics,
|
||||
selectedTopics: [...topics],
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
await encryptRecord('newsPreferences', diff);
|
||||
|
|
@ -57,7 +60,7 @@ export const preferencesStore = {
|
|||
async setLanguages(languages: Language[]): Promise<void> {
|
||||
await ensureRow();
|
||||
const diff: Partial<LocalPreferences> = {
|
||||
preferredLanguages: languages,
|
||||
preferredLanguages: [...languages],
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
await encryptRecord('newsPreferences', diff);
|
||||
|
|
|
|||
|
|
@ -57,10 +57,13 @@
|
|||
}
|
||||
|
||||
async function finishOnboarding() {
|
||||
// $state.snapshot strips the Svelte 5 reactive proxies — without it
|
||||
// the arrays travel into Dexie hooks as proxies and trip
|
||||
// DataCloneError on the structured-clone into _pendingChanges.
|
||||
await preferencesStore.completeOnboarding({
|
||||
topics: pickedTopics,
|
||||
languages: pickedLanguages,
|
||||
blockedSources: pickedBlocked,
|
||||
topics: $state.snapshot(pickedTopics) as Topic[],
|
||||
languages: $state.snapshot(pickedLanguages) as Language[],
|
||||
blockedSources: $state.snapshot(pickedBlocked) as string[],
|
||||
});
|
||||
// The +layout effect will pick up the new prefs and refresh.
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue