diff --git a/frontend/package.json b/frontend/package.json index 4ee7d91..389e394 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -8,6 +8,7 @@ "build": "vue-tsc -b && vite build", "preview": "vite preview", "test": "vitest run", + "optimize-dist-wasm": "node scripts/optimize-dist-wasm.mjs", "postinstall": "node patches/patch-exiftool-stderr.mjs && node patches/patch-zeroperl-worker-fetch.mjs" }, "dependencies": { diff --git a/frontend/scripts/optimize-dist-wasm.mjs b/frontend/scripts/optimize-dist-wasm.mjs new file mode 100644 index 0000000..3d30348 --- /dev/null +++ b/frontend/scripts/optimize-dist-wasm.mjs @@ -0,0 +1,32 @@ +import { brotliCompressSync, constants as zlibConstants } from 'node:zlib'; +import { readdirSync, readFileSync, writeFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const distAssetsDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../dist/assets'); + +const brotliOptions = { + params: { + [zlibConstants.BROTLI_PARAM_QUALITY]: 11, + [zlibConstants.BROTLI_PARAM_LGWIN]: 24 + } +}; + +const wasmFiles = readdirSync(distAssetsDir).filter(name => name.endsWith('.wasm')); + +if (wasmFiles.length === 0) { + console.error('optimize-dist-wasm: no .wasm files in dist/assets'); + process.exit(1); +} + +console.log('optimize-dist-wasm: brotli sidecars for', wasmFiles.length, 'file(s)'); + +for (const fileName of wasmFiles) { + const wasmPath = path.join(distAssetsDir, fileName); + console.log('optimize-dist-wasm: brotli', wasmPath); + + const compressed = brotliCompressSync(readFileSync(wasmPath), brotliOptions); + console.log('optimize-dist-wasm: compressed', compressed.length); + + writeFileSync(`${wasmPath}.br`, compressed); +} diff --git a/nginx/Dockerfile.prod b/nginx/Dockerfile.prod index 82a3fe7..9ce0aa5 100644 --- a/nginx/Dockerfile.prod +++ b/nginx/Dockerfile.prod @@ -29,11 +29,14 @@ ENV VITE_API_BASE_URL=$VITE_API_BASE_URL \ VITE_VALIDATION_UPLOAD_MAX_VIEWS_MAX=$VITE_VALIDATION_UPLOAD_MAX_VIEWS_MAX \ VITE_VALIDATION_UPLOAD_MAX_IMAGE_BYTES=$VITE_VALIDATION_UPLOAD_MAX_IMAGE_BYTES -RUN npm run build +RUN npm run build && node scripts/optimize-dist-wasm.mjs -FROM nginx:1.29.4-alpine +FROM alpine:3.23 -RUN apk add --no-cache gettext +RUN apk add --no-cache \ + nginx=1.28.3-r7 \ + nginx-mod-http-brotli=1.28.3-r7 \ + gettext COPY --from=frontend-builder /frontend/dist /usr/share/nginx/html diff --git a/nginx/docker-entrypoint.sh b/nginx/docker-entrypoint.sh index 2c732a1..cbdb355 100644 --- a/nginx/docker-entrypoint.sh +++ b/nginx/docker-entrypoint.sh @@ -37,7 +37,7 @@ render_server() { mkdir -p /etc/nginx/snippets render_locations -render_server /etc/nginx/templates/conf.d/clearnet.conf.template /etc/nginx/conf.d/clearnet.conf clearnet -render_server /etc/nginx/templates/conf.d/onion.conf.template /etc/nginx/conf.d/onion.conf onion +render_server /etc/nginx/templates/conf.d/clearnet.conf.template /etc/nginx/http.d/clearnet.conf clearnet +render_server /etc/nginx/templates/conf.d/onion.conf.template /etc/nginx/http.d/onion.conf onion exec nginx -g 'daemon off;' diff --git a/nginx/snippets/private-image-locations.conf.template b/nginx/snippets/private-image-locations.conf.template index 51ad13c..af45fcc 100644 --- a/nginx/snippets/private-image-locations.conf.template +++ b/nginx/snippets/private-image-locations.conf.template @@ -39,6 +39,14 @@ location /pgadmin { proxy_redirect off; } +location ^~ /assets/ { + root /usr/share/nginx/html; + + brotli_static on; + + add_header Cache-Control "public, max-age=31536000, immutable" always; +} + location / { ${ONION_LOCATION_HEADER} add_header Referrer-Policy "no-referrer" always;