Stage three: the viewer. Drawing on pages comes next. pdf.js stays out of the bundle everyone downloads — it is larger than the rest of the application put together, so it is behind a dynamic import and a session that never opens a PDF never fetches it. Main bundle grew by 8 kB for the viewer shell; the 162 kB library arrives only when needed. The worker is bundled rather than pulled from a CDN, because a viewer that stops working during someone else's outage is not one you can rely on offline. Two things that only showed up once it ran in a browser: - pdf.js 6 calls Uint8Array.prototype.toHex, which only very recent browsers have. Anything older failed with "n.toHex is not a function", which says nothing about the cause. Now on the legacy build, which compiles those away — a little larger, and the difference between working everywhere and working on this year's browsers. - nginx has no MIME type for .mjs and served the worker as octet-stream, which a browser refuses to run as a module. Pages are laid out at their true proportions before anything is drawn, so the scrollbar is honest immediately and a document mixing portrait and landscape keeps both — the first version scaled every page to the widest one, which the suite caught by checking the two aspect ratios differ. Bytes come from the authenticated attachment route and are cached in IndexedDB. Content-addressed storage means a cached copy can never be stale, so a document opened once needs no connection again. Verified with the network switched off. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
75 lines
2.2 KiB
Nginx Configuration File
75 lines
2.2 KiB
Nginx Configuration File
server {
|
|
listen 80;
|
|
server_name _;
|
|
root /usr/share/nginx/html;
|
|
index index.html;
|
|
|
|
client_max_body_size 50m;
|
|
|
|
# PWA service worker — never cache
|
|
location = /sw.js {
|
|
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
|
add_header Pragma "no-cache";
|
|
add_header Expires "0";
|
|
add_header Service-Worker-Allowed "/";
|
|
types { application/javascript js; }
|
|
try_files $uri =404;
|
|
}
|
|
|
|
# PWA manifest
|
|
location = /manifest.json {
|
|
add_header Cache-Control "no-cache";
|
|
types { application/manifest+json json; }
|
|
try_files $uri =404;
|
|
}
|
|
|
|
# Vite hashed assets — immutable cache
|
|
location /assets/ {
|
|
add_header Cache-Control "public, max-age=31536000, immutable";
|
|
# nginx has no mapping for .mjs and falls back to octet-stream, which a
|
|
# browser refuses to run as a module. pdf.js ships its worker as .mjs.
|
|
types {
|
|
application/javascript js mjs;
|
|
text/css css;
|
|
application/json json map;
|
|
image/svg+xml svg;
|
|
font/woff2 woff2;
|
|
}
|
|
try_files $uri =404;
|
|
}
|
|
|
|
# Icons (icon-*.png and apple-touch-icon.png)
|
|
location ~* ^/(icon-.*|apple-touch-icon)\.png$ {
|
|
add_header Cache-Control "public, max-age=86400";
|
|
try_files $uri =404;
|
|
}
|
|
|
|
# SPA fallback
|
|
location / {
|
|
add_header Cache-Control "no-cache";
|
|
try_files $uri $uri/ /index.html;
|
|
}
|
|
|
|
# API proxy
|
|
location /api/ {
|
|
proxy_pass http://inknote-server:4000;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
proxy_connect_timeout 10;
|
|
proxy_send_timeout 60;
|
|
proxy_read_timeout 60;
|
|
}
|
|
|
|
# WebSocket proxy
|
|
location /ws {
|
|
proxy_pass http://inknote-server:4000;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
proxy_set_header Connection "upgrade";
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_read_timeout 86400;
|
|
}
|
|
} |