aboutsummaryrefslogtreecommitdiffstats
path: root/lib/image-worker.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/image-worker.ts')
-rw-r--r--lib/image-worker.ts29
1 files changed, 29 insertions, 0 deletions
diff --git a/lib/image-worker.ts b/lib/image-worker.ts
new file mode 100644
index 00000000..d45927d1
--- /dev/null
+++ b/lib/image-worker.ts
@@ -0,0 +1,29 @@
+// Image conversion runs in THIS separate process, spawned per upload by
+// lib/image.ts. It is the only place sharp/libvips is ever loaded. Reads the
+// raw upload on stdin, writes the resized webp to stdout, exits 0. Any
+// decoder bug (libheif/libwebp/libpng heap corruption, SIGSEGV, SIGILL) kills
+// this process alone — the app keeps serving. 2026-09-26: a stream of corrupt
+// HEIF uploads crash-looped the whole server when this ran in-process.
+const sharp = require("sharp");
+
+const MAX_PIXELS = 80_000_000;
+
+const width = Number(process.argv[2]);
+if (!Number.isInteger(width) || width <= 0 || width > 4096) {
+ console.error("bad width");
+ process.exit(2);
+}
+
+try {
+ const input = Buffer.from(await new Response(Bun.stdin.stream()).arrayBuffer());
+ const out = await sharp(input, { failOnError: true, limitInputPixels: MAX_PIXELS })
+ .rotate()
+ .resize(width)
+ .webp()
+ .toBuffer();
+ await Bun.write(Bun.stdout, out);
+ process.exit(0);
+} catch (e) {
+ console.error(e?.message || String(e));
+ process.exit(2);
+}