diff --git a/client/js/upload.js b/client/js/upload.js index 3b2e164c..33d4e154 100644 --- a/client/js/upload.js +++ b/client/js/upload.js @@ -9,6 +9,7 @@ class Uploader { init() { this.xhr = null; this.fileQueue = []; + this.tokenKeepAlive = null; document.addEventListener("dragenter", (e) => this.dragEnter(e)); document.addEventListener("dragover", (e) => this.dragOver(e)); @@ -131,6 +132,12 @@ class Uploader { uploadNextFileInQueue(token) { const file = this.fileQueue.shift(); + // Tell the server that we are still upload to this token + // so it does not become invalidated and fail the upload. + // This issue only happens if The Lounge is proxied through other software + // as it may buffer the upload before the upload request will be processed by The Lounge. + this.tokenKeepAlive = setInterval(() => socket.emit("upload:ping", token), 40 * 1000); + if ( store.state.settings.uploadCanvas && file.type.startsWith("image/") && @@ -219,6 +226,11 @@ class Uploader { handleResponse(response) { this.setProgress(0); + if (this.tokenKeepAlive) { + clearInterval(this.tokenKeepAlive); + this.tokenKeepAlive = null; + } + if (response.error) { store.commit("currentUserVisibleError", response.error); return; diff --git a/src/plugins/uploader.js b/src/plugins/uploader.js index dde02e4a..c365f776 100644 --- a/src/plugins/uploader.js +++ b/src/plugins/uploader.js @@ -11,7 +11,9 @@ const crypto = require("crypto"); const isUtf8 = require("is-utf8"); const log = require("../log"); -const whitelist = [ +// List of allowed mime types that can be rendered in browser +// without forcing it to be downloaded +const inlineContentDispositionTypes = [ "application/ogg", "audio/midi", "audio/mpeg", @@ -35,17 +37,33 @@ class Uploader { socket.on("upload:auth", () => { const token = uuidv4(); - uploadTokens.set(token, true); - socket.emit("upload:auth", token); // Invalidate the token in one minute - setTimeout(() => uploadTokens.delete(token), 60 * 1000); + const timeout = Uploader.createTokenTimeout(token); + + uploadTokens.set(token, timeout); + }); + + socket.on("upload:ping", (token) => { + if (typeof token !== "string") { + return; + } + + let timeout = uploadTokens.get(token); + + if (!timeout) { + return; + } + + clearTimeout(timeout); + timeout = Uploader.createTokenTimeout(token); + uploadTokens.set(token, timeout); }); } - static isValidType(mimeType) { - return whitelist.includes(mimeType); + static createTokenTimeout(token) { + return setTimeout(() => uploadTokens.delete(token), 60 * 1000); } static router(express) { @@ -72,8 +90,10 @@ class Uploader { return res.status(404).send("Not found"); } - // Force a download in the browser if it's not a whitelisted type (binary or otherwise unknown) - const contentDisposition = Uploader.isValidType(detectedMimeType) ? "inline" : "attachment"; + // Force a download in the browser if it's not an allowed type (binary or otherwise unknown) + const contentDisposition = inlineContentDispositionTypes.includes(detectedMimeType) + ? "inline" + : "attachment"; if (detectedMimeType === "audio/vnd.wave") { // Send a more common mime type for wave audio files