Files
bizmatch-app/web/public/viewer/viewer.js
2026-07-26 17:45:58 -05:00

52 lines
1.7 KiB
JavaScript

// Bootstrap for the standalone PDF viewer page. Plays the role of app.js in
// viewer-phase1: it owns the #pdfMessage overlay and hands one URL to PdfViewer
// (from pdf_viewer.js, unchanged). Everything else — zoom, resize, retry — lives
// in PdfViewer already.
//
// URL contract: /viewer/index.html?file=<urlencoded /api/... path>
// Same-origin only: any file value that is not a root-relative /api/ path is
// refused before it ever reaches pdf.js.
const viewer = document.querySelector(".viewer");
const pdfContainer = document.querySelector("#pdfViewer");
const pdfMessage = document.querySelector("#pdfMessage");
function showMessage(text) {
viewer.classList.remove("loaded");
pdfMessage.textContent = text;
pdfMessage.hidden = false;
}
// Only root-relative /api/ paths. Rejects absolute URLs ("https://..."),
// protocol-relative ones ("//host/api/x") and anything outside /api/.
function safeFileUrl(raw) {
if (!raw) return null;
if (!raw.startsWith("/api/") || raw.startsWith("//")) return null;
// A backslash can be normalized to "/" by some URL parsers — reject outright.
if (raw.includes("\\")) return null;
return raw;
}
const params = new URLSearchParams(globalThis.location.search);
const file = safeFileUrl(params.get("file"));
if (!file) {
showMessage(
params.get("file")
? "Refused to load this document: only same-origin /api/ paths are allowed."
: "Select a document",
);
} else {
showMessage("Loading PDF…");
const pdfViewer = new PdfViewer(pdfContainer);
viewer.classList.add("loaded");
pdfMessage.hidden = true;
pdfViewer.load(file).catch((error) => {
showMessage(
`Cannot open PDF: ${
error instanceof Error ? error.message : String(error)
}`,
);
});
}