From b119c0010afa54cfc88d930bddfd76c58a2a0fc1 Mon Sep 17 00:00:00 2001 From: silberengel Date: Sun, 3 Aug 2025 09:37:03 +0200 Subject: [PATCH] improved error handling for missing publications --- src/lib/utils/websocket_utils.ts | 16 ++- src/routes/publication/+error.svelte | 123 ++++++++++++++++-- .../publication/[type]/[identifier]/+page.ts | 23 +++- 3 files changed, 142 insertions(+), 20 deletions(-) diff --git a/src/lib/utils/websocket_utils.ts b/src/lib/utils/websocket_utils.ts index c95001e..f835408 100644 --- a/src/lib/utils/websocket_utils.ts +++ b/src/lib/utils/websocket_utils.ts @@ -69,7 +69,7 @@ function handleError( reject(ev); } -export async function fetchNostrEvent(filter: NostrFilter): Promise { +export async function fetchNostrEvent(filter: NostrFilter): Promise { // AI-NOTE: Updated to use active relay stores instead of hardcoded relay URL // This ensures the function uses the user's configured relays and can find events // across multiple relays rather than being limited to a single hardcoded relay. @@ -82,7 +82,11 @@ export async function fetchNostrEvent(filter: NostrFilter): Promise const availableRelays = [...inboxRelays, ...outboxRelays]; if (availableRelays.length === 0) { - throw new Error("[WebSocket Utils]: No relays available for fetching events"); + // AI-NOTE: Return null instead of throwing error when no relays are available + // This allows the publication routes to handle the case gracefully during preloading + // when relay stores haven't been populated yet + console.warn("[WebSocket Utils]: No relays available for fetching events, returning null"); + return null; } // Select a relay - prefer inbox relays if available, otherwise use any available relay @@ -135,7 +139,7 @@ export async function fetchEventById(id: string): Promise { try { const event = await fetchNostrEvent({ ids: [id], limit: 1 }); if (!event) { - error(404, `Event not found for ID: ${id}`); + error(404, `Event not found for ID: ${id}. href="/events?id=${id}"`); } return event; } catch (err) { @@ -153,7 +157,7 @@ export async function fetchEventByDTag(dTag: string): Promise { try { const event = await fetchNostrEvent({ "#d": [dTag], limit: 1 }); if (!event) { - error(404, `Event not found for d-tag: ${dTag}`); + error(404, `Event not found for d-tag: ${dTag}. href="/events?d=${dTag}"`); } return event; } catch (err) { @@ -177,7 +181,7 @@ export async function fetchEventByNaddr(naddr: string): Promise { }; const event = await fetchNostrEvent(filter); if (!event) { - error(404, `Event not found for naddr: ${naddr}`); + error(404, `Event not found for naddr: ${naddr}. href="/events?id=${naddr}"`); } return event; } catch (err) { @@ -196,7 +200,7 @@ export async function fetchEventByNevent(nevent: string): Promise { const decoded = neventDecode(nevent); const event = await fetchNostrEvent({ ids: [decoded.id], limit: 1 }); if (!event) { - error(404, `Event not found for nevent: ${nevent}`); + error(404, `Event not found for nevent: ${nevent}. href="/events?id=${nevent}"`); } return event; } catch (err) { diff --git a/src/routes/publication/+error.svelte b/src/routes/publication/+error.svelte index 9d0d347..c9d1ce2 100644 --- a/src/routes/publication/+error.svelte +++ b/src/routes/publication/+error.svelte @@ -3,28 +3,125 @@ import { Alert, P, Button } from "flowbite-svelte"; import { ExclamationCircleOutline } from "flowbite-svelte-icons"; import { page } from "$app/state"; + + // Parse error message to extract search parameters and format it nicely + function parseErrorMessage(message: string): { + errorType: string; + identifier: string; + searchUrl?: string; + shortIdentifier?: string; + } { + const searchLinkMatch = message.match(/href="([^"]+)"/); + let searchUrl: string | undefined; + let baseMessage = message; + + if (searchLinkMatch) { + searchUrl = searchLinkMatch[1]; + baseMessage = message.replace(/href="[^"]+"/, '').trim(); + } + + // Extract error type and identifier from the message + const match = baseMessage.match(/Event not found for (\w+): (.+)/); + if (match) { + const errorType = match[1]; + const fullIdentifier = match[2]; + const shortIdentifier = fullIdentifier.length > 50 + ? fullIdentifier.substring(0, 47) + '...' + : fullIdentifier; + + return { + errorType, + identifier: fullIdentifier, + searchUrl, + shortIdentifier + }; + } + + return { + errorType: 'unknown', + identifier: baseMessage, + searchUrl, + shortIdentifier: baseMessage.length > 50 + ? baseMessage.substring(0, 47) + '...' + : baseMessage + }; + } + + $: errorInfo = page.error?.message ? parseErrorMessage(page.error.message) : { + errorType: 'unknown', + identifier: '', + shortIdentifier: '' + }; -
- -
- - Failed to load publication. +
+ +
+ + + Failed to load publication +
-

- Alexandria failed to find one or more of the events comprising this - publication. -

-

- {page.error?.message} + +

+ Alexandria failed to find one or more of the events comprising this publication.

+ +
+
+ + Error Type: + + + {errorInfo.errorType} + +
+ +
+ + Identifier: + +
+
+ {errorInfo.shortIdentifier} +
+ {#if errorInfo.identifier.length > 50} +
+ + Show full identifier + +
+ {errorInfo.identifier} +
+
+ {/if} +
+
+
+ + {#if errorInfo.searchUrl} +
+ +
+ {/if} +
diff --git a/src/routes/publication/[type]/[identifier]/+page.ts b/src/routes/publication/[type]/[identifier]/+page.ts index 1c00099..51c7d55 100644 --- a/src/routes/publication/[type]/[identifier]/+page.ts +++ b/src/routes/publication/[type]/[identifier]/+page.ts @@ -27,7 +27,28 @@ export const load: PageLoad = async ({ params }: { params: { type: string; ident } if (!indexEvent) { - error(404, `Event not found for ${type}: ${identifier}`); + // AI-NOTE: Handle case where no relays are available during preloading + // This prevents 404 errors when relay stores haven't been populated yet + console.warn(`[Publication Load] Event not found for ${type}: ${identifier} - may be due to no relays available`); + + // Create appropriate search link based on type + let searchParam = ''; + switch (type) { + case 'id': + searchParam = `id=${identifier}`; + break; + case 'd': + searchParam = `d=${identifier}`; + break; + case 'naddr': + case 'nevent': + searchParam = `id=${identifier}`; + break; + default: + searchParam = `q=${identifier}`; + } + + error(404, `Event not found for ${type}: ${identifier}. href="/events?${searchParam}"`); } const publicationType = indexEvent.tags.find((tag) => tag[0] === "type")?.[1] ?? "";