fix(eoi): render signed-PDF preview inline (preview endpoint + font-src)
After adding frame-src the preview iframe stopped showing the broken-file icon but went blank: it pointed at /api/v1/files/[id]/download, which presigns with the filename so S3 returns Content-Disposition: attachment — the browser downloaded the PDF instead of rendering it. Point the SignedPdfPreview iframe at the existing /preview endpoint, which presigns WITHOUT a filename (inline disposition) so the native PDF viewer renders. Also widen font-src to include https: so react-pdf/pdf.js can load its standard-font pack (LiberationSans*) — previously blocked by font-src 'self' data:, breaking the pdf.js-based viewers' glyphs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -51,7 +51,8 @@ const csp = [
|
||||
`script-src 'self' 'unsafe-inline'${isProd ? '' : " 'unsafe-eval'"}${devScriptHosts}`,
|
||||
"style-src 'self' 'unsafe-inline'",
|
||||
"img-src 'self' data: blob: https:",
|
||||
"font-src 'self' data:",
|
||||
// https: so react-pdf/pdf.js can load its standard-font pack + branding fonts.
|
||||
"font-src 'self' data: https:",
|
||||
`connect-src 'self' ws: wss: https:${devConnectHosts}`,
|
||||
// PDF previews iframe a presigned storage URL; embedded-signing iframes the
|
||||
// Documenso host. Both are per-port/per-env, so allow https: (matching
|
||||
|
||||
@@ -868,10 +868,15 @@ function SignedEoiCard({
|
||||
* the file in a new tab via the alongside View button for full-screen.
|
||||
*/
|
||||
function SignedPdfPreview({ fileId }: { fileId: string }) {
|
||||
const { data, isLoading, isError } = useQuery<{ data: { url: string; filename: string } }>({
|
||||
queryKey: ['files', fileId, 'download-url'],
|
||||
// Use the PREVIEW endpoint, not /download: /download presigns with the
|
||||
// filename so S3 returns `Content-Disposition: attachment`, which makes the
|
||||
// iframe trigger a file download (blank preview) instead of rendering. The
|
||||
// preview endpoint presigns WITHOUT a filename → inline disposition → the
|
||||
// browser's native PDF viewer renders it in the card.
|
||||
const { data, isLoading, isError } = useQuery<{ data: { url: string; mimeType: string } }>({
|
||||
queryKey: ['files', fileId, 'preview-url'],
|
||||
queryFn: () =>
|
||||
apiFetch<{ data: { url: string; filename: string } }>(`/api/v1/files/${fileId}/download`),
|
||||
apiFetch<{ data: { url: string; mimeType: string } }>(`/api/v1/files/${fileId}/preview`),
|
||||
// Presigned URL TTLs vary per backend - refresh well before they
|
||||
// expire so a long-open card doesn't suddenly 403. 4 minutes is
|
||||
// comfortably below the 5-minute MinIO default.
|
||||
|
||||
@@ -27,7 +27,9 @@ function buildCspWithNonce(nonce: string, isProd: boolean): string {
|
||||
scriptSrc,
|
||||
"style-src 'self' 'unsafe-inline'",
|
||||
"img-src 'self' data: blob: https:",
|
||||
"font-src 'self' data:",
|
||||
// https: so react-pdf/pdf.js can pull its standard-font pack (the PDF
|
||||
// viewers fetch LiberationSans etc. from a CDN) and port-branding fonts.
|
||||
"font-src 'self' data: https:",
|
||||
connectSrc,
|
||||
// PDF previews (signed EOIs etc.) iframe a presigned storage URL, and the
|
||||
// embedded-signing card iframes the Documenso host. Both are per-port /
|
||||
|
||||
Reference in New Issue
Block a user