fix(rpubs): canonical subdomain URLs for published pages

Published hosted_url, pdf_url, epub_url, and all links in the reader
page now use {space}.rspace.online/rpubs/... (subdomain form) instead
of path-scoped rspace.online/{space}/rpubs/... — matching the site
convention that space slugs always appear as subdomains.

The server already rewrites subdomain → path-scope internally for
routing, so Hono route mounts stay the same.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-04-16 15:51:28 -04:00
parent d55b9f74c0
commit 07b4714f48
1 changed files with 14 additions and 16 deletions

View File

@ -200,7 +200,10 @@ function formatBytes(n: number): string {
function renderReaderPage(opts: { record: import("./publications-store").PublicationRecord; spaceSlug: string }): string {
const { record, spaceSlug } = opts;
const base = `/${escapeAttr(spaceSlug)}/rpubs/publications/${escapeAttr(record.slug)}`;
// Always emit canonical subdomain absolute URLs so links are correct
// regardless of whether the user arrived via {space}.rspace.online/...
// or rspace.online/{space}/... (internal path-scope rewriting form).
const base = `https://${encodeURIComponent(record.space)}.rspace.online/rpubs/publications/${encodeURIComponent(record.slug)}`;
const pdfUrl = `${base}/pdf`;
const epubUrl = `${base}/epub`;
const epubFixedUrl = record.fixedEpubBytes ? `${base}/epub-fixed` : null;
@ -550,23 +553,18 @@ routes.post("/api/publish", async (c) => {
fixedEpub,
});
const proto = c.req.header("x-forwarded-proto") || "https";
const host = c.req.header("host") || "rspace.online";
const baseUrl = `${proto}://${host}`;
// The reader lives under the module mount (`/:space/rpubs/publications/:slug`)
// or, on the standalone rpubs.online domain, at `/publications/:slug`.
const isStandalone = host.includes("rpubs.online");
const hostedPath = isStandalone
? `/publications/${record.slug}`
: `/${record.space}/rpubs/publications/${record.slug}`;
// Canonical subdomain form: {space}.rspace.online/rpubs/publications/{slug}.
// The server rewrites subdomain → path-scope internally, so this URL works
// even though Hono routes are mounted at `/:space/rpubs/...`.
const hostedUrl = `https://${record.space}.rspace.online/rpubs/publications/${record.slug}`;
return c.json({
...record,
hosted_url: `${baseUrl}${hostedPath}`,
hosted_path: hostedPath,
pdf_url: `${baseUrl}${hostedPath}/pdf`,
epub_url: `${baseUrl}${hostedPath}/epub`,
epub_fixed_url: fixedEpub ? `${baseUrl}${hostedPath}/epub-fixed` : null,
hosted_url: hostedUrl,
hosted_path: `/rpubs/publications/${record.slug}`,
pdf_url: `${hostedUrl}/pdf`,
epub_url: `${hostedUrl}/epub`,
epub_fixed_url: fixedEpub ? `${hostedUrl}/epub-fixed` : null,
}, 201);
} catch (error) {
console.error("[Pubs] Publish error:", error);
@ -598,7 +596,7 @@ routes.get("/publications/:slug", async (c) => {
const record = await getPublication(dataSpace, slug);
if (!record) {
return c.html(`<!doctype html><meta charset="utf-8"><title>Not found</title><body style="font:14px system-ui;padding:40px;color:#ddd;background:#111"><h1>Publication not found</h1><p>No publication exists at this URL.</p><p><a style="color:#14b8a6" href="/${escapeAttr(spaceSlug)}/rpubs">Back to rPubs</a></p></body>`, 404);
return c.html(`<!doctype html><meta charset="utf-8"><title>Not found</title><body style="font:14px system-ui;padding:40px;color:#ddd;background:#111"><h1>Publication not found</h1><p>No publication exists at this URL.</p><p><a style="color:#14b8a6" href="https://${encodeURIComponent(spaceSlug)}.rspace.online/rpubs">Back to rPubs</a></p></body>`, 404);
}
return c.html(renderShell({