diff --git a/apps/cards/apps/web/src/hooks.server.ts b/apps/cards/apps/web/src/hooks.server.ts index fe2b663b9..1bef6e03a 100644 --- a/apps/cards/apps/web/src/hooks.server.ts +++ b/apps/cards/apps/web/src/hooks.server.ts @@ -21,6 +21,8 @@ const PUBLIC_MANA_LLM_URL_CLIENT = process.env.PUBLIC_MANA_LLM_URL_CLIENT || process.env.PUBLIC_MANA_LLM_URL || ''; const PUBLIC_MANA_MEDIA_URL_CLIENT = process.env.PUBLIC_MANA_MEDIA_URL_CLIENT || process.env.PUBLIC_MANA_MEDIA_URL || ''; +const PUBLIC_CARDS_API_URL_CLIENT = + process.env.PUBLIC_CARDS_API_URL_CLIENT || process.env.PUBLIC_CARDS_API_URL || ''; export const handle: Handle = async ({ event, resolve }) => { return resolve(event, { @@ -31,6 +33,7 @@ export const handle: Handle = async ({ event, resolve }) => { `window.__PUBLIC_MANA_SYNC_URL__=${JSON.stringify(PUBLIC_MANA_SYNC_URL_CLIENT)};` + `window.__PUBLIC_MANA_LLM_URL__=${JSON.stringify(PUBLIC_MANA_LLM_URL_CLIENT)};` + `window.__PUBLIC_MANA_MEDIA_URL__=${JSON.stringify(PUBLIC_MANA_MEDIA_URL_CLIENT)};` + + `window.__PUBLIC_CARDS_API_URL__=${JSON.stringify(PUBLIC_CARDS_API_URL_CLIENT)};` + ``; return html.replace('
', `${envScript}`); }, diff --git a/apps/cards/apps/web/src/lib/api/cards-api.ts b/apps/cards/apps/web/src/lib/api/cards-api.ts new file mode 100644 index 000000000..3062a61cc --- /dev/null +++ b/apps/cards/apps/web/src/lib/api/cards-api.ts @@ -0,0 +1,176 @@ +/** + * Thin client for cards-server (https://cards-api.mana.how / dev :3072). + * + * The auth-store provides the JWT; we never read tokens from storage + * here directly so there's only one place that knows about token + * lifecycle (refresh, expiry, vault). + * + * All endpoints under /v1 require auth; the wrapper just always + * sends `Authorization: Bearer …`. Errors come back as Hono's + * `{ statusCode, message, details? }` shape — we surface that to + * callers via the typed `CardsApiError` so UIs can branch on it. + */ + +import { authStore } from '$lib/stores/auth.svelte'; + +function baseUrl(): string { + if (typeof window !== 'undefined') { + const fromWindow = (window as unknown as { __PUBLIC_CARDS_API_URL__?: string }) + .__PUBLIC_CARDS_API_URL__; + if (fromWindow) return fromWindow.replace(/\/$/, ''); + } + return 'http://localhost:3072'; +} + +export class CardsApiError extends Error { + constructor( + public status: number, + message: string, + public details?: unknown + ) { + super(message); + this.name = 'CardsApiError'; + } +} + +interface RequestOptions { + method?: 'GET' | 'POST' | 'PATCH' | 'DELETE'; + body?: unknown; + signal?: AbortSignal; + /** When false, send the request without an Authorization header. */ + auth?: boolean; +} + +async function request
+ Erstelle ein Author-Profil — andere User finden deine Decks unter
+ cards.mana.how/u/dein-slug.
+
{authorStore.error}
+ {/if} +
+ Veröffentlicht als cards.mana.how/d/{deckSlug || '...'}
+
+ {cards.length} + {cards.length === 1 ? 'Karte' : 'Karten'} werden veröffentlicht. Das Deck durchläuft eine KI-Inhaltsprüfung + — offensichtlich harmloses Material geht direkt durch. +
+