From aa76910bc40fdd5012262d285a94c85260fa6a11 Mon Sep 17 00:00:00 2001 From: buttercat1791 Date: Wed, 30 Jul 2025 01:25:39 -0500 Subject: [PATCH] Use utils based on raw WebSockets for SSR --- src/lib/utils/nostrUtils.ts | 83 ---------- src/lib/utils/websocket_utils.ts | 143 ++++++++++++++++++ .../[type]/[identifier]/+layout.server.ts | 29 ++-- .../[type]/[identifier]/+page.server.ts | 28 ++-- .../[type]/[identifier]/+page.svelte | 17 ++- 5 files changed, 174 insertions(+), 126 deletions(-) create mode 100644 src/lib/utils/websocket_utils.ts diff --git a/src/lib/utils/nostrUtils.ts b/src/lib/utils/nostrUtils.ts index 5faa1bb..3d67c57 100644 --- a/src/lib/utils/nostrUtils.ts +++ b/src/lib/utils/nostrUtils.ts @@ -12,8 +12,6 @@ import { schnorr } from "@noble/curves/secp256k1"; import { bytesToHex } from "@noble/hashes/utils"; import { wellKnownUrl } from "./search_utility.ts"; import { VALIDATION } from "./search_constants.ts"; -import { error } from "@sveltejs/kit"; -import { naddrDecode, neventDecode } from "../utils.ts"; const badgeCheckSvg = ''; @@ -673,84 +671,3 @@ export function prefixNostrAddresses(content: string): string { return `nostr:${match}`; }); } - -// Added functions for fetching events by various identifiers - -/** - * Fetches an event by hex ID, throwing a SvelteKit 404 error if not found. - */ -export async function fetchEventById(ndk: NDK, id: string): Promise { - try { - const event = await fetchEventWithFallback(ndk, id); - if (!event) { - throw error(404, `Event not found for ID: ${id}`); - } - return event; - } catch (err) { - if (err && typeof err === "object" && "status" in err) { - throw err; - } - throw error(404, `Failed to fetch event by ID: ${err}`); - } -} - -/** - * Fetches an event by d tag, throwing a 404 if not found. - */ -export async function fetchEventByDTag(ndk: NDK, dTag: string): Promise { - try { - const event = await fetchEventWithFallback(ndk, { "#d": [dTag], limit: 1 }); - if (!event) { - throw error(404, `Event not found for d-tag: ${dTag}`); - } - return event; - } catch (err) { - if (err && typeof err === "object" && "status" in err) { - throw err; - } - throw error(404, `Failed to fetch event by d-tag: ${err}`); - } -} - -/** - * Fetches an event by naddr identifier. - */ -export async function fetchEventByNaddr(ndk: NDK, naddr: string): Promise { - try { - const decoded = naddrDecode(naddr); - const filter = { - kinds: [decoded.kind], - authors: [decoded.pubkey], - "#d": [decoded.identifier], - }; - const event = await fetchEventWithFallback(ndk, filter); - if (!event) { - throw error(404, `Event not found for naddr: ${naddr}`); - } - return event; - } catch (err) { - if (err && typeof err === "object" && "status" in err) { - throw err; - } - throw error(404, `Failed to fetch event by naddr: ${err}`); - } -} - -/** - * Fetches an event by nevent identifier. - */ -export async function fetchEventByNevent(ndk: NDK, nevent: string): Promise { - try { - const decoded = neventDecode(nevent); - const event = await fetchEventWithFallback(ndk, decoded.id); - if (!event) { - throw error(404, `Event not found for nevent: ${nevent}`); - } - return event; - } catch (err) { - if (err && typeof err === "object" && "status" in err) { - throw err; - } - throw error(404, `Failed to fetch event by nevent: ${err}`); - } -} diff --git a/src/lib/utils/websocket_utils.ts b/src/lib/utils/websocket_utils.ts new file mode 100644 index 0000000..9d0d382 --- /dev/null +++ b/src/lib/utils/websocket_utils.ts @@ -0,0 +1,143 @@ +import { WebSocketPool } from "../data_structures/websocket_pool.ts"; +import { error } from "@sveltejs/kit"; +import { naddrDecode, neventDecode } from "../utils.ts"; + +export interface NostrEvent { + id: string; + pubkey: string; + created_at: number; + kind: number; + tags: string[][]; + content: string; + sig: string; +} + +export interface NostrFilter { + ids?: string[]; + authors?: string[]; + kinds?: number[]; + [tag: `#${string}`]: string[] | undefined; + since?: number; + until?: number; + limit?: number; +} + +export async function fetchNostrEvent(filter: NostrFilter): Promise { + // TODO: Improve relay selection when relay management is implemented. + const ws = await WebSocketPool.instance.acquire("wss://thecitadel.nostr1.com"); + const subId = crypto.randomUUID(); + + const res = new Promise((resolve, reject) => { + ws.addEventListener("message", (ev) => { + const data = JSON.parse(ev.data); + + if (data[1] !== subId) { + return; + } + + switch (data[0]) { + case "EVENT": + break; + case "CLOSED": + reject(new Error(`[WebSocket Utils]: Subscription ${subId} closed`)); + break; + case "EOSE": + resolve(null); + break; + } + + const event = data[2] as NostrEvent; + if (!event) { + return; + } + + resolve(event); + }); + + ws.addEventListener("error", (ev) => { + reject(ev); + }); + }).withTimeout(2000); + + ws.send(JSON.stringify(["REQ", subId, filter])); + return res; +} + +/** + * Fetches an event by hex ID, throwing a SvelteKit 404 error if not found. + */ +export async function fetchEventById(id: string): Promise { + try { + const event = await fetchNostrEvent({ ids: [id], limit: 1 }); + if (!event) { + throw error(404, `Event not found for ID: ${id}`); + } + return event; + } catch (err) { + if (err && typeof err === "object" && "status" in err) { + throw err; + } + throw error(404, `Failed to fetch event by ID: ${err}`); + } +} + +/** + * Fetches an event by d tag, throwing a 404 if not found. + */ +export async function fetchEventByDTag(dTag: string): Promise { + try { + const event = await fetchNostrEvent({ "#d": [dTag], limit: 1 }); + if (!event) { + throw error(404, `Event not found for d-tag: ${dTag}`); + } + return event; + } catch (err) { + if (err && typeof err === "object" && "status" in err) { + throw err; + } + throw error(404, `Failed to fetch event by d-tag: ${err}`); + } +} + +/** + * Fetches an event by naddr identifier. + */ +export async function fetchEventByNaddr(naddr: string): Promise { + try { + const decoded = naddrDecode(naddr); + const filter = { + kinds: [decoded.kind], + authors: [decoded.pubkey], + "#d": [decoded.identifier], + }; + const event = await fetchNostrEvent(filter); + if (!event) { + throw error(404, `Event not found for naddr: ${naddr}`); + } + return event; + } catch (err) { + if (err && typeof err === "object" && "status" in err) { + throw err; + } + throw error(404, `Failed to fetch event by naddr: ${err}`); + } +} + +/** + * Fetches an event by nevent identifier. + */ +export async function fetchEventByNevent(nevent: string): Promise { + try { + const decoded = neventDecode(nevent); + const event = await fetchNostrEvent({ ids: [decoded.id], limit: 1 }); + if (!event) { + throw error(404, `Event not found for nevent: ${nevent}`); + } + return event; + } catch (err) { + if (err && typeof err === "object" && "status" in err) { + throw err; + } + throw error(404, `Failed to fetch event by nevent: ${err}`); + } +} diff --git a/src/routes/publication/[type]/[identifier]/+layout.server.ts b/src/routes/publication/[type]/[identifier]/+layout.server.ts index 2c6bebe..b89da64 100644 --- a/src/routes/publication/[type]/[identifier]/+layout.server.ts +++ b/src/routes/publication/[type]/[identifier]/+layout.server.ts @@ -1,44 +1,35 @@ import { error } from "@sveltejs/kit"; import type { LayoutServerLoad } from "./$types"; -import type { NDKEvent } from "@nostr-dev-kit/ndk"; -import { getMatchingTags, fetchEventById, fetchEventByDTag, fetchEventByNaddr, fetchEventByNevent } from "../../../../lib/utils/nostrUtils.ts"; +import { fetchEventByDTag, fetchEventById, fetchEventByNaddr, fetchEventByNevent, NostrEvent } from "../../../../lib/utils/websocket_utils.ts"; -export const load: LayoutServerLoad = async ({ params, parent, url }) => { +export const load: LayoutServerLoad = async ({ params, url }) => { const { type, identifier } = params; - // TODO: Remove the need for NDK in nostrUtils dependencies, since NDK is not available on the server. - // deno-lint-ignore no-explicit-any - const { ndk } = (await parent()) as any; - - if (!ndk) { - throw error(500, "NDK not available"); - } - - let indexEvent: NDKEvent; + let indexEvent: NostrEvent; // Handle different identifier types switch (type) { case 'id': - indexEvent = await fetchEventById(ndk, identifier); + indexEvent = await fetchEventById(identifier); break; case 'd': - indexEvent = await fetchEventByDTag(ndk, identifier); + indexEvent = await fetchEventByDTag(identifier); break; case 'naddr': - indexEvent = await fetchEventByNaddr(ndk, identifier); + indexEvent = await fetchEventByNaddr(identifier); break; case 'nevent': - indexEvent = await fetchEventByNevent(ndk, identifier); + indexEvent = await fetchEventByNevent(identifier); break; default: throw error(400, `Unsupported identifier type: ${type}`); } // Extract metadata for meta tags - const title = getMatchingTags(indexEvent, "title")[0]?.[1] || "Alexandria Publication"; - const summary = getMatchingTags(indexEvent, "summary")[0]?.[1] || + const title = indexEvent.tags.find((tag) => tag[0] === "title")?.[1] || "Alexandria Publication"; + const summary = indexEvent.tags.find((tag) => tag[0] === "summary")?.[1] || "Alexandria is a digital library, utilizing Nostr events for curated publications and wiki pages."; - const image = getMatchingTags(indexEvent, "image")[0]?.[1] || "/screenshots/old_books.jpg"; + const image = indexEvent.tags.find((tag) => tag[0] === "image")?.[1] || "/screenshots/old_books.jpg"; const currentUrl = `${url.origin}${url.pathname}`; return { diff --git a/src/routes/publication/[type]/[identifier]/+page.server.ts b/src/routes/publication/[type]/[identifier]/+page.server.ts index 95b58fe..18a5e41 100644 --- a/src/routes/publication/[type]/[identifier]/+page.server.ts +++ b/src/routes/publication/[type]/[identifier]/+page.server.ts @@ -1,42 +1,38 @@ import { error } from "@sveltejs/kit"; import type { PageServerLoad } from "./$types"; -import type { NDKEvent } from "@nostr-dev-kit/ndk"; -import { getMatchingTags, fetchEventById, fetchEventByDTag, fetchEventByNaddr, fetchEventByNevent } from "../../../../lib/utils/nostrUtils.ts"; +import { fetchEventByDTag, fetchEventById, fetchEventByNaddr, fetchEventByNevent, NostrEvent } from "../../../../lib/utils/websocket_utils.ts"; -export const load: PageServerLoad = async ({ params, parent }) => { +export const load: PageServerLoad = async ({ params }) => { const { type, identifier } = params; - // deno-lint-ignore no-explicit-any - const { ndk } = (await parent()) as any; - if (!ndk) { - throw error(500, "NDK not available"); - } - - let indexEvent: NDKEvent; + let indexEvent: NostrEvent | null; // Handle different identifier types switch (type) { case 'id': - indexEvent = await fetchEventById(ndk, identifier); + indexEvent = await fetchEventById(identifier); break; case 'd': - indexEvent = await fetchEventByDTag(ndk, identifier); + indexEvent = await fetchEventByDTag(identifier); break; case 'naddr': - indexEvent = await fetchEventByNaddr(ndk, identifier); + indexEvent = await fetchEventByNaddr(identifier); break; case 'nevent': - indexEvent = await fetchEventByNevent(ndk, identifier); + indexEvent = await fetchEventByNevent(identifier); break; default: throw error(400, `Unsupported identifier type: ${type}`); } - const publicationType = getMatchingTags(indexEvent, "type")[0]?.[1]; + if (!indexEvent) { + throw error(404, `Event not found for ${type}: ${identifier}`); + } + + const publicationType = indexEvent.tags.find((tag) => tag[0] === "type")?.[1] ?? ""; return { publicationType, indexEvent, - ndk, // Pass ndk to the page for the publication tree }; }; \ No newline at end of file diff --git a/src/routes/publication/[type]/[identifier]/+page.svelte b/src/routes/publication/[type]/[identifier]/+page.svelte index 07cf547..9b786a8 100644 --- a/src/routes/publication/[type]/[identifier]/+page.svelte +++ b/src/routes/publication/[type]/[identifier]/+page.svelte @@ -1,6 +1,5 @@