/*********************************************************************************************************************************************************************** * File Name: uploadProgress.service.js * Type of Program: Service * Description: In-memory SSE broadcaster for real upload progress on the * Express -> S3 (Garage) leg of an asset upload. Keyed by a * client-generated uploadId so the browser can open the stream * before the upload request itself is even sent. * * No Redis — single-process only, same tradeoff already made by * mediaToken.service.js's in-memory token cache. Fine for one * instance; a second app instance would just never see progress * for uploads routed to the other process. * * Author: Kenneth Obsequio (@lash0000) ***********************************************************************************************************************************************************************/ "use strict"; const clients = new Map(); // uploadId -> Response function subscribe(uploadId, res) { res.writeHead(200, { "Content-Type": "text/event-stream", "Cache-Control": "no-cache, no-transform", Connection: "keep-alive", "X-Accel-Buffering": "no", // disable proxy-side buffering (nginx-style intermediaries) }); res.write(": connected\n\n"); clients.set(uploadId, res); res.on("close", () => { if (clients.get(uploadId) === res) clients.delete(uploadId); }); } // No-ops if nobody's subscribed (client never opened the stream, or already // disconnected) — progress is a best-effort visual, never load-bearing for // the actual upload. function publish(uploadId, data) { const res = clients.get(uploadId); if (!res) return; res.write(`data: ${JSON.stringify(data)}\n\n`); } function complete(uploadId, data = {}) { const res = clients.get(uploadId); if (!res) return; res.write(`data: ${JSON.stringify({ ...data, done: true })}\n\n`); res.end(); clients.delete(uploadId); } module.exports = { subscribe, publish, complete };