Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Make files clickable
  • Loading branch information
Sg312 committed Aug 5, 2025
commit e10b384b978f0da78a682018d877ff08ab4a7580
32 changes: 29 additions & 3 deletions apps/sim/app/api/files/serve/[...path]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,11 @@ export async function GET(
if (isUsingCloudStorage() || isCloudPath) {
// Extract the actual key (remove 's3/' or 'blob/' prefix if present)
const cloudKey = isCloudPath ? path.slice(1).join('/') : fullPath
return await handleCloudProxy(cloudKey)

// Get bucket type from query parameter
const bucketType = request.nextUrl.searchParams.get('bucket')

return await handleCloudProxy(cloudKey, bucketType)
}

// Use local handler for local files
Expand Down Expand Up @@ -152,12 +156,34 @@ async function downloadKBFile(cloudKey: string): Promise<Buffer> {
/**
* Proxy cloud file through our server
*/
async function handleCloudProxy(cloudKey: string): Promise<NextResponse> {
async function handleCloudProxy(cloudKey: string, bucketType?: string | null): Promise<NextResponse> {
try {
// Check if this is a KB file (starts with 'kb/')
const isKBFile = cloudKey.startsWith('kb/')

const fileBuffer = isKBFile ? await downloadKBFile(cloudKey) : await downloadFile(cloudKey)
let fileBuffer: Buffer

if (isKBFile) {
fileBuffer = await downloadKBFile(cloudKey)
} else if (bucketType === 'copilot') {
// Download from copilot-specific bucket
const storageProvider = getStorageProvider()

if (storageProvider === 's3') {
const { downloadFromS3WithConfig } = await import('@/lib/uploads/s3/s3-client')
const { S3_COPILOT_CONFIG } = await import('@/lib/uploads/setup')
fileBuffer = await downloadFromS3WithConfig(cloudKey, S3_COPILOT_CONFIG)
} else if (storageProvider === 'blob') {
// For Azure Blob, use the default downloadFile for now
// TODO: Add downloadFromBlobWithConfig when needed
fileBuffer = await downloadFile(cloudKey)
} else {
fileBuffer = await downloadFile(cloudKey)
}
} else {
// Default bucket
fileBuffer = await downloadFile(cloudKey)
}

// Extract the original filename from the key (last part after last /)
const originalFilename = cloudKey.split('/').pop() || 'download'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,27 @@ const FileAttachmentDisplay = memo(({ fileAttachments }: FileAttachmentDisplayPr
return <FileText className='h-4 w-4' />
}

const handleFileClick = (file: any) => {
Comment thread
Sg312 marked this conversation as resolved.
// Construct the serve URL for the file with bucket type parameter
const serveUrl = `/api/files/serve/s3/${encodeURIComponent(file.s3_key)}?bucket=copilot`

// Open the file in a new tab
window.open(serveUrl, '_blank')
}

return (
<div className='mt-2 space-y-1'>
{fileAttachments.map((file) => (
<div
key={file.id}
className='flex items-center gap-2 rounded-md bg-muted/50 px-2 py-1 text-xs'
className='flex items-center gap-2 rounded-md bg-muted/50 px-2 py-1 text-xs cursor-pointer hover:bg-muted/70 transition-colors'
onClick={() => handleFileClick(file)}
title={`Click to view ${file.filename}`}
>
{getFileIcon(file.media_type)}
<span className='flex-1 truncate'>{file.filename}</span>
<span className='flex-1 truncate text-primary underline decoration-dotted underline-offset-2'>
{file.filename}
</span>
<span className='text-muted-foreground'>
{formatFileSize(file.size)}
</span>
Expand Down