From a4b4c1877f803c8d764a284df9ba2f4f8c71c7fd Mon Sep 17 00:00:00 2001 From: ShraddhanandKhot Date: Fri, 14 Aug 2026 13:42:50 +0000 Subject: [PATCH] fix: download chat file attachments inline with auth MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clicking a file attachment in chat opens a preview modal (plain file refs) or a new tab via window.open to /files/{id}/content. The JWT is stored in localStorage and sent as an Authorization header on fetch — it is not available as a cookie on plain navigation, so the new-tab request returns 401 for regular logins (the token cookie is only set in the OAuth flow). Fix: skip the preview modal for plain file refs and fetch the file bytes with the Authorization header, then trigger a direct browser download with the original filename (?attachment=true forces Content-Disposition: attachment for all content types). Falls back to the previous window.open behavior on failure. Items with embedded content still open the modal; external http(s) URLs are unchanged; input-box attachments are unaffected (they pass modal=true). --- src/lib/components/common/FileItem.svelte | 27 +++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/lib/components/common/FileItem.svelte b/src/lib/components/common/FileItem.svelte index 881088ca26..21001ab1fb 100644 --- a/src/lib/components/common/FileItem.svelte +++ b/src/lib/components/common/FileItem.svelte @@ -57,7 +57,7 @@ : 'gap-1 rounded-2xl p-1.5'} text-left" type="button" on:click={async () => { - if (item?.file?.data?.content || item?.type === 'file' || item?.content || modal) { + if (item?.file?.data?.content || item?.content || modal) { showModal = !showModal; } else { if (url) { @@ -65,7 +65,30 @@ if (url.startsWith('http')) { window.open(`${url}/content`, '_blank').focus(); } else { - window.open(`${WEBUI_API_BASE_URL}/files/${url}/content`, '_blank').focus(); + // The JWT lives in localStorage (sent as an Authorization header + // on fetch) and is not available as a cookie on plain navigation, + // so opening the file URL in a new tab returns 401. Fetch the + // bytes with the token and trigger a direct browser download. + try { + const res = await fetch( + `${WEBUI_API_BASE_URL}/files/${url}/content?attachment=true`, + { headers: { Authorization: `Bearer ${localStorage.token}` } } + ); + if (!res.ok) throw new Error(`HTTP ${res.status}`); + + const blob = await res.blob(); + const objectUrl = window.URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = objectUrl; + a.download = name; + document.body.appendChild(a); + a.click(); + a.remove(); + setTimeout(() => window.URL.revokeObjectURL(objectUrl), 3000); + } catch (err) { + console.error(err); + window.open(`${WEBUI_API_BASE_URL}/files/${url}/content`, '_blank').focus(); + } } } else { window.open(`${url}`, '_blank').focus();