Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,10 @@ describe('BulkActionsService', () => {
'Content-Disposition',
`attachment; filename="${expectedFilename}"`,
);
expect(mockResponse.setHeader).toHaveBeenCalledWith(
'Access-Control-Expose-Headers',
'Content-Disposition',
);
expect(mockResponse.setHeader).toHaveBeenCalledWith(
'Transfer-Encoding',
'chunked',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export class BulkActionsService {
'Content-Disposition',
`attachment; filename="bulk-delete-report-${timestamp}.txt"`,
);
res.setHeader('Access-Control-Expose-Headers', 'Content-Disposition');
res.setHeader('Transfer-Encoding', 'chunked');

// Attach the response stream to the bulk action
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,6 @@
.nodeSelected {
border-left-color: var(--browserComponentActive) !important;
background-color: var(--browserComponentActive) !important;

.nodeContent * {
color: var(--euiColorFullShade) !important;
}
}

.nodeIcon {
Expand Down
35 changes: 32 additions & 3 deletions redisinsight/ui/src/utils/dom/triggerDownloadFromUrl.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,41 @@
import { CustomHeaders } from 'uiSrc/constants/api'

/**
* Triggers a file download from a URL by creating a temporary link element
* Triggers a file download from a URL using fetch with proper headers
* This is necessary for Electron app where window ID authentication is required
* @param url The full URL to download from
*/
export const triggerDownloadFromUrl = (url: string): void => {
export const triggerDownloadFromUrl = async (url: string): Promise<void> => {
const headers: Record<string, string> = {}

// Add window ID header for Electron app authentication
if (window.windowId) {
headers[CustomHeaders.WindowId] = window.windowId
}

const response = await fetch(url, { headers })

if (!response.ok) {
throw new Error(`Download failed: ${response.statusText}`)
}

// Extract filename from Content-Disposition header
const contentDisposition = response.headers.get('content-disposition') || ''
const filenameMatch = contentDisposition.match(/filename="?([^";\n]+)"?/)
const filename = filenameMatch?.[1] || 'download'

// Convert response to blob and trigger download
const blob = await response.blob()
const blobUrl = URL.createObjectURL(blob)

const link = document.createElement('a')
link.href = url
link.href = blobUrl
link.download = filename
link.style.display = 'none'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)

// Clean up the blob URL
URL.revokeObjectURL(blobUrl)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Async function call without await loses error handling

The triggerDownloadFromUrl function was changed from synchronous to async (returning Promise<void>), but its caller in BulkActionsConfig.tsx at line 144 does not await the result or handle the promise. When the download fails (e.g., network error or non-ok response), the throw new Error() at line 19 results in an unhandled promise rejection, silently failing without notifying the user.

Fix in Cursor Fix in Web

}
Loading