diff --git a/src/utils/resolveStandardSitePostUrl.ts b/src/utils/resolveStandardSitePostUrl.ts index 9885ad4d..4890796a 100644 --- a/src/utils/resolveStandardSitePostUrl.ts +++ b/src/utils/resolveStandardSitePostUrl.ts @@ -1,7 +1,10 @@ import type { SupabaseClient } from "@supabase/supabase-js"; import type { Database, Json } from "supabase/database.types"; import { parseStandardSitePostInput } from "components/Blocks/StandardSitePostBlock/parseStandardSitePostInput"; -import { normalizeDocumentRecord } from "src/utils/normalizeRecords"; +import { + normalizeDocumentRecord, + normalizePublicationRecord, +} from "src/utils/normalizeRecords"; /** * Resolve a URL or AT URI to a standard-site-post AT URI by checking our DB. @@ -29,28 +32,53 @@ export async function resolveStandardSitePostUrl( return null; } const origin = `${url.protocol}//${url.host}`; - const pathOnly = url.pathname.replace(/\/$/, "") || "/"; + const requestedPath = url.pathname.replace(/\/$/, "") || "/"; + const requestedUrl = origin + (requestedPath === "/" ? "" : requestedPath); - // Find a publication whose URL matches this origin. Supports both + // Publications can live at a sub-path (e.g. https://example.com/blog), so we + // can't match the origin exactly. Prefix-match on the host to gather every + // publication that could own this URL, then filter to the one whose url is + // the longest prefix of the requested URL. Supports both // pub.leaflet.publication (base_path) and site.standard.publication (url). - // Descending uri order prefers site.standard.publication over pub.leaflet.publication. - const { data: publication } = await supabase + // Descending uri order prefers site.standard.publication over + // pub.leaflet.publication when base paths are equally specific. + const { data: publications } = await supabase .from("publications") - .select("uri") + .select("uri, record") .or( - `record->>base_path.eq.${url.host},record->>url.eq.${origin},record->>url.eq.${origin}/`, + [ + `record->>base_path.like.${url.host}*`, + `record->>url.like.${origin}*`, + ].join(","), ) - .order("uri", { ascending: false }) - .limit(1) - .maybeSingle(); - console.log(publication); - if (!publication) return null; + .order("uri", { ascending: false }); - // Find documents in that publication where data.path matches pathOnly + if (!publications?.length) return null; + + // Pick the publication whose url is the longest prefix of the requested URL, + // and compute the document path relative to that publication's base path. + let best: { uri: string; path: string; urlLength: number } | null = null; + for (const pub of publications) { + const normalized = normalizePublicationRecord(pub.record); + if (!normalized?.url) continue; + const pubUrl = normalized.url.replace(/\/$/, ""); + if (requestedUrl !== pubUrl && !requestedUrl.startsWith(`${pubUrl}/`)) + continue; + const basePath = new URL(pubUrl).pathname.replace(/\/$/, ""); + const relative = requestedPath.slice(basePath.length) || "/"; + const path = relative.startsWith("/") ? relative : `/${relative}`; + if (!best || pubUrl.length > best.urlLength) + best = { uri: pub.uri, path, urlLength: pubUrl.length }; + } + + if (!best) return null; + + // Find documents in that publication where data.path matches the requested + // path (relative to the publication's base path). const { data: docsInPub } = await supabase .from("documents_in_publications") .select("documents(uri, data)") - .eq("publication", publication.uri); + .eq("publication", best.uri); if (!docsInPub) return null; @@ -65,8 +93,7 @@ export async function resolveStandardSitePostUrl( : `/${normalized.path}` : null; const docPath = rawPath ? rawPath.replace(/\/$/, "") || "/" : null; - console.log(docPath, pathOnly); - if (docPath === pathOnly) return doc.uri; + if (docPath === best.path) return doc.uri; } return null; }