const { Readable } = require("node:stream"); const { pipeline } = require("node:stream/promises"); const { AssetError } = require("./errors"); function readableBody(body) { if (body && typeof body.pipe === "function") return body; if (Buffer.isBuffer(body) || body instanceof Uint8Array) { return Readable.from([body]); } if (body && typeof body.getReader === "function") { return Readable.fromWeb(body); } throw new AssetError( "STORAGE_INCONSISTENCY", "Stored asset bytes could not be streamed", { statusCode: 503 }, ); } function validateDescriptor(descriptor) { if ( typeof descriptor.contentType !== "string" || !Number.isSafeInteger(descriptor.contentLength) || descriptor.contentLength < 0 ) { throw new AssetError( "STORAGE_INCONSISTENCY", "Stored asset metadata is incomplete", { statusCode: 503 }, ); } } function setCommonHeaders(response, descriptor) { response.set("Content-Type", descriptor.contentType); response.set("Content-Length", String(descriptor.contentLength)); response.set("X-Content-Type-Options", "nosniff"); } async function storedBody(assetService, descriptor) { const stored = await assetService.readStoredObject(descriptor); return readableBody(stored.body); } async function sendImmutableMedia(assetService, response, descriptor) { validateDescriptor(descriptor); const body = await storedBody(assetService, descriptor); setCommonHeaders(response, descriptor); response.set("Cache-Control", "public, max-age=31536000, immutable"); response.set("ETag", `"${descriptor.hash}"`); await pipeline(body, response); } async function sendOriginal(assetService, response, descriptor) { validateDescriptor(descriptor); const body = await storedBody(assetService, descriptor); response.attachment(descriptor.asset.original_filename); setCommonHeaders(response, descriptor); response.set("Cache-Control", "private, no-store"); await pipeline(body, response); } module.exports = { sendImmutableMedia, sendOriginal, };