feat(files): preview attachments via short-lived signed URLs
Downloads require an Authorization header, which <iframe>/<img> cannot
send. The previous blob: workaround broke under download managers (IDM
hooks blob URLs, fails to resolve them, and leaves a 0-byte file) and in
Chrome's mobile emulation, which has no inline PDF plugin.
Backend:
- preview_token.py: HMAC-signed token bound to one (quote, kind, file)
with a 5-minute TTL, keyed off an existing secret
- GET /quotes/{id}/preview-url/{kind}/{attachment} mints the URL
- the two file GET routes accept a Bearer session OR the signed ?t= token
(they leave the global auth dep, so the other six file routes now carry
it explicitly — nothing is open by omission)
Frontend:
- api.files.previewUrl() replaces the blob helper
- shared FilePreviewModal + useFilePreview; FileList and the dashboard
quote card both use it
- PDFs render with the browser's native viewer on desktop and pdf.js on
canvas where no inline viewer exists (mobile); pdf.js is fed the bytes
in memory so no PDF URL is exposed for a download manager to intercept
- removed the per-attachment download buttons on the dashboard card and
the modal header
Ops:
- nginx: explicit MIME types (.mjs as text/javascript) — a module worker
served as octet-stream is refused and crashed the page
- frontend service bypasses the nginx entrypoint, whose apk manifest call
hangs when the package mirror is unreachable (502s)
This commit is contained in:
@@ -29,6 +29,26 @@ server {
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
# MIME types: the pdf.js worker ships as .mjs, and a browser REFUSES to
|
||||
# start a module worker served as application/octet-stream (nginx's
|
||||
# default for unknown extensions) — which crashes the PDF preview page.
|
||||
# Declared explicitly rather than relying on the system mime.types.
|
||||
types {
|
||||
text/javascript mjs js;
|
||||
text/css css;
|
||||
text/html html;
|
||||
image/svg+xml svg;
|
||||
image/png png;
|
||||
image/jpeg jpg jpeg;
|
||||
image/webp webp;
|
||||
image/x-icon ico;
|
||||
font/woff2 woff2;
|
||||
font/woff woff;
|
||||
application/json json;
|
||||
application/pdf pdf;
|
||||
application/wasm wasm;
|
||||
}
|
||||
|
||||
# SPA fallback — all client-side routes go to index.html
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
|
||||
Reference in New Issue
Block a user