From f938bec632d3013c7345504524131ed60444b2b8 Mon Sep 17 00:00:00 2001 From: 1amKhush Date: Thu, 30 Apr 2026 13:10:08 +0530 Subject: [PATCH] refactor: address maintainer feedback on routing logic and env fallbacks --- server.js | 188 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 126 insertions(+), 62 deletions(-) diff --git a/server.js b/server.js index 6d337d65..4b9df7a2 100644 --- a/server.js +++ b/server.js @@ -2,9 +2,10 @@ import path from "node:path" import {promises as fs} from "node:fs" import {fileURLToPath} from "node:url" +import "dotenv/config" import {serve} from "@hono/node-server" import {serveStatic} from "@hono/node-server/serve-static" -import {fetchRelay} from "@welshman/app" +import {loadRelay} from "@welshman/app" import {displayRelayUrl, normalizeRelayUrl} from "@welshman/util" import {load} from "cheerio" import {Hono} from "hono" @@ -26,15 +27,8 @@ try { process.exit(1) } -const TEMPLATE_DOCUMENT = load(TEMPLATE_HTML) -const DEFAULT_PLATFORM_NAME = - process.env.VITE_PLATFORM_NAME || - TEMPLATE_DOCUMENT('meta[property="og:title"]').attr("content") || - "Flotilla" -const DEFAULT_PLATFORM_DESCRIPTION = - process.env.VITE_PLATFORM_DESCRIPTION || - TEMPLATE_DOCUMENT('meta[name="description"]').attr("content") || - "Flotilla is nostr - for communities." +const PLATFORM_NAME = process.env.VITE_PLATFORM_NAME +const PLATFORM_DESCRIPTION = process.env.VITE_PLATFORM_DESCRIPTION // Match client-side decode logic const decodeRelay = url => { @@ -61,72 +55,142 @@ const requestUrlFromContext = context => { return requestUrl } -const resolveMetadata = async requestUrl => { - const pathname = requestUrl.pathname - let relayParam = undefined - - // Match /join?r=... - if (pathname === "/join" || pathname === "/join/") { - relayParam = requestUrl.searchParams.get("r") - } - // Match /spaces/:relay/... - else if (pathname.startsWith("/spaces/")) { - const parts = pathname.split("/").filter(Boolean) - if (parts.length >= 2) { - relayParam = decodeRelay(parts[1]) - } - } - - if (!relayParam) { +const fetchRelayMeta = async relayUrl => { + if (!relayUrl) return undefined + try { + return await loadRelay(normalizeRelayUrl(relayUrl)) + } catch (err) { + console.error(`Failed to fetch relay metadata for ${relayUrl}:`, err) return undefined } +} - try { - // Note: fetchRelay from @welshman/app handles the ws->http conversion and caching - const relayMetadata = await fetchRelay(normalizeRelayUrl(relayParam)) +const buildDefaultImage = requestUrl => { + return new URL("/maskable-icon-512x512.png", requestUrl.origin).toString() +} - if (!relayMetadata) { - return undefined - } +const getMetadataForInvite = async (url, match) => { + const relayParam = url.searchParams.get("r") + if (!relayParam) return undefined - const relayDisplay = displayRelayUrl(relayParam) - const spaceName = relayMetadata.name - const relayDescription = relayMetadata.description + const relayMetadata = await fetchRelayMeta(relayParam) + if (!relayMetadata) return undefined - const title = spaceName - ? `Invite to ${spaceName} on ${DEFAULT_PLATFORM_NAME}` - : `Invite to a Space on ${DEFAULT_PLATFORM_NAME}` + const relayDisplay = displayRelayUrl(relayParam) + const spaceName = relayMetadata.name + const relayDescription = relayMetadata.description - const parts = [] - if (spaceName) { - parts.push(`You are invited to join ${spaceName} on ${DEFAULT_PLATFORM_NAME}.`) - } else { - parts.push(`You are invited to join a space on ${DEFAULT_PLATFORM_NAME}.`) - } + const title = spaceName + ? `Invite to ${spaceName} on ${PLATFORM_NAME}` + : `Invite to a Space on ${PLATFORM_NAME}` - if (relayDisplay) parts.push(`Relay: ${relayDisplay}.`) - if (relayDescription) parts.push(relayDescription) - else parts.push(DEFAULT_PLATFORM_DESCRIPTION) + const parts = [] + if (spaceName) { + parts.push(`You are invited to join ${spaceName} on ${PLATFORM_NAME}.`) + } else { + parts.push(`You are invited to join a space on ${PLATFORM_NAME}.`) + } - const description = parts.join(" ") - const image = + if (relayDisplay) parts.push(`Relay: ${relayDisplay}.`) + if (relayDescription) parts.push(relayDescription) + else parts.push(PLATFORM_DESCRIPTION) + + const description = parts.join(" ") + const image = + relayMetadata.icon || + relayMetadata.picture || + relayMetadata.image || + buildDefaultImage(url) + + return { + title, + description, + image, + url: url.toString(), + site: url.origin, + } +} + +const getMetadataForSpace = async (url, match) => { + const relayParam = decodeRelay(match[1]) + if (!relayParam) return undefined + + const relayMetadata = await fetchRelayMeta(relayParam) + if (!relayMetadata) return undefined + + const spaceName = relayMetadata.name || displayRelayUrl(relayParam) + + return { + title: `${spaceName} on ${PLATFORM_NAME}`, + description: relayMetadata.description || PLATFORM_DESCRIPTION, + image: relayMetadata.icon || relayMetadata.picture || relayMetadata.image || - new URL("/maskable-icon-512x512.png", requestUrl.origin).toString() - - return { - title, - description, - image, - url: requestUrl.toString(), - site: requestUrl.origin, - } - } catch (err) { - return undefined + buildDefaultImage(url), + url: url.toString(), + site: url.origin, } } +const getMetadataForSpaceSection = async (url, match) => { + const spaceMeta = await getMetadataForSpace(url, match) + if (!spaceMeta) return undefined + + const section = match[2] + const sectionName = section.charAt(0).toUpperCase() + section.slice(1) + spaceMeta.title = `${sectionName} on ${spaceMeta.title}` + return spaceMeta +} + +const getMetadataForSpaceItem = async (url, match) => { + const spaceMeta = await getMetadataForSpace(url, match) + if (!spaceMeta) return undefined + + const section = match[2] + let itemType = "Item" + if (section === "calendar") itemType = "Event" + if (section === "threads") itemType = "Thread" + if (section === "polls") itemType = "Poll" + if (section === "goals") itemType = "Goal" + if (section === "classifieds") itemType = "Listing" + + spaceMeta.title = `${itemType} on ${spaceMeta.title}` + return spaceMeta +} + +const getMetadataForRoom = async (url, match) => { + const spaceMeta = await getMetadataForSpace(url, match) + if (!spaceMeta) return undefined + + // Room metadata requires fetching from Nostr, which can be added later. + spaceMeta.title = `Room on ${spaceMeta.title}` + return spaceMeta +} + +const routes = [ + [/^\/join\/?$/, getMetadataForInvite], + [/^\/spaces\/([^/]+)\/(calendar|chat|threads|polls|goals|classifieds|recent)\/?$/, getMetadataForSpaceSection], + [/^\/spaces\/([^/]+)\/(calendar|threads|polls|goals|classifieds)\/([^/]+)\/?$/, getMetadataForSpaceItem], + [/^\/spaces\/([^/]+)\/([^/]+)\/?$/, getMetadataForRoom], + [/^\/spaces\/([^/]+)\/?$/, getMetadataForSpace], +] + +const getMetadataForRoute = async url => { + for (const [regex, getMetadata] of routes) { + const match = url.pathname.match(regex) + if (match) { + try { + return await getMetadata(url, match) + } catch (err) { + console.error(`Error generating metadata for route ${url.pathname}:`, err) + return undefined + } + } + } + return undefined +} + const injectMeta = metadata => { const $ = load(TEMPLATE_HTML) @@ -196,7 +260,7 @@ app.get("*", async context => { return context.text("Not found", 404) } - const metadata = await resolveMetadata(requestUrl) + const metadata = await getMetadataForRoute(requestUrl) const html = metadata ? injectMeta(metadata) : TEMPLATE_HTML return context.html(html, 200, {