From 07a4cb687206cbafaf8244d03148397ca3e4600c Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Tue, 12 May 2026 10:42:05 +0000 Subject: [PATCH] branch download UI on shouldDownloadViaClient instead of onclick interception (#9118) * fix(frontend): branch download UI on shouldDownloadViaClient instead of intercepting in onclick When OpenAPI.TOKEN is set, several download links rendered an `` to the API and relied on an `onclick` handler to call `e.preventDefault()` and route the request through `downloadViaClient`. This is fragile in embedded contexts (e.g. the whitelabel React SDK) where Svelte's hydrated event listener may not intercept the click in time, so the browser follows the unauthenticated `href` straight to the API. Mirror the drawer pattern already used in `LogViewer` and `FlowStatusViewerInner`: render a ` + {:else} + + Download {filename ? '' : 'as JSON'} + + {/if} {#if download_as_csv} convertJsonToCsv(result)} diff --git a/frontend/src/lib/components/LogViewer.svelte b/frontend/src/lib/components/LogViewer.svelte index 9d7b71b6e3..39089dec8b 100644 --- a/frontend/src/lib/components/LogViewer.svelte +++ b/frontend/src/lib/components/LogViewer.svelte @@ -208,11 +208,6 @@ let logsApiPath = $derived(`/w/${$workspaceStore}/jobs_u/get_logs/${jobId}`) let downloadHref = $derived(withExternalDomain(`${base}/api${logsApiPath}`)) let downloadName = $derived(`windmill_logs_${jobId}.txt`) - async function onDownloadClick(e: MouseEvent) { - if (!shouldDownloadViaClient()) return - e.preventDefault() - await downloadViaClient(logsApiPath, downloadName) - } let truncatedContent = $derived(truncateContent(content, loadedFromObjectStore, LOG_LIMIT)) let prefixInfo = $derived(findPrefixInfo(truncatedContent)) let downloadStartUrl = $derived(findStartUrl(truncatedContent, prefixInfo)) @@ -357,14 +352,21 @@
{#if jobId && download}
- - + {#if shouldDownloadViaClient()} + + {:else} + + + {/if}
{/if} diff --git a/frontend/src/lib/components/ParqetCsvTableRenderer.svelte b/frontend/src/lib/components/ParqetCsvTableRenderer.svelte index 91cc107791..fd08eb64be 100644 --- a/frontend/src/lib/components/ParqetCsvTableRenderer.svelte +++ b/frontend/src/lib/components/ParqetCsvTableRenderer.svelte @@ -203,16 +203,24 @@ {#if !disable_download && !s3resource.endsWith('.csv')} {@const csvApiPath = `/w/${workspaceId}/job_helpers/download_s3_parquet_file_as_csv?file_key=${encodeURIComponent(s3resource)}${storage ? `&storage=${storage}` : ''}`} {@const csvName = (s3resource.split('/').pop() ?? 'download') + '.csv'} - { - if (!shouldDownloadViaClient()) return - e.preventDefault() - await downloadViaClient(csvApiPath, csvName) - }}>
CSV
+ {#if shouldDownloadViaClient()} + + {:else} +
CSV
+ {/if} {/if} {#if nbRows != undefined} diff --git a/frontend/src/lib/components/common/fileDownload/FileDownload.svelte b/frontend/src/lib/components/common/fileDownload/FileDownload.svelte index 0cb48eb7e1..8d6529764b 100644 --- a/frontend/src/lib/components/common/fileDownload/FileDownload.svelte +++ b/frontend/src/lib/components/common/fileDownload/FileDownload.svelte @@ -26,26 +26,26 @@ ) let href = $derived(`${base}/api${apiPath}`) - async function onclick(e: MouseEvent) { - if (!shouldDownloadViaClient()) return - e.preventDefault() - await downloadViaClient(apiPath, filename) - } + const sharedClass = `relative center-center flex w-full text-center font-normal text-primary text-sm +border border-dashed border-gray-400 hover:border-blue-500 +focus-within:border-blue-500 hover:bg-blue-50 dark:hover:bg-frost-900 focus-within:bg-blue-50 +duration-200 rounded-lg p-1 gap-2` {#if s3object && s3object?.s3} - - - - {s3object?.storage ? `s3://${s3object.storage}/${s3object.s3}` : `s3:///${s3object.s3}`} - - + {#if shouldDownloadViaClient()} + + {:else} + + + + {s3object?.storage ? `s3://${s3object.storage}/${s3object.s3}` : `s3:///${s3object.s3}`} + + + {/if} {/if} diff --git a/frontend/src/lib/components/propertyPicker/ObjectViewer.svelte b/frontend/src/lib/components/propertyPicker/ObjectViewer.svelte index d954618518..9746991d6b 100644 --- a/frontend/src/lib/components/propertyPicker/ObjectViewer.svelte +++ b/frontend/src/lib/components/propertyPicker/ObjectViewer.svelte @@ -410,18 +410,26 @@ {#if getTypeAsString(jsonFiltered) === 's3object'} {@const s3DownloadApiPath = `/w/${$workspaceStore}/job_helpers/download_s3_file?file_key=${encodeURIComponent(jsonFiltered?.s3 ?? '')}${jsonFiltered?.storage ? `&storage=${jsonFiltered.storage}` : ''}`} {@const s3DownloadName = jsonFiltered?.s3.split('/').pop() ?? 'unnamed_download.file'} - { - if (!shouldDownloadViaClient()) return - e.preventDefault() - await downloadViaClient(s3DownloadApiPath, s3DownloadName) - }} - > - download - + {#if shouldDownloadViaClient()} + + {:else} + + download + + {/if}