Skip to content
28 changes: 18 additions & 10 deletions apps/sim/app/api/files/upload/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import { captureServerEvent } from '@/lib/posthog/server'
import type { StorageContext } from '@/lib/uploads/config'
import { generateWorkspaceFileKey } from '@/lib/uploads/contexts/workspace/workspace-file-manager'
import { isImageFileType } from '@/lib/uploads/utils/file-utils'
import { isImageFileType, resolveFileType } from '@/lib/uploads/utils/file-utils'
import {
SUPPORTED_AUDIO_EXTENSIONS,
SUPPORTED_CODE_EXTENSIONS,
SUPPORTED_DOCUMENT_EXTENSIONS,
SUPPORTED_IMAGE_EXTENSIONS,
SUPPORTED_VIDEO_EXTENSIONS,
validateFileType,
} from '@/lib/uploads/utils/validation'
Expand All @@ -28,12 +29,10 @@ import {
InvalidRequestError,
} from '@/app/api/files/utils'

const IMAGE_EXTENSIONS = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'svg'] as const

const ALLOWED_EXTENSIONS = new Set<string>([
...SUPPORTED_DOCUMENT_EXTENSIONS,
...SUPPORTED_CODE_EXTENSIONS,
...IMAGE_EXTENSIONS,
...SUPPORTED_IMAGE_EXTENSIONS,
Comment thread
waleedlatif1 marked this conversation as resolved.
...SUPPORTED_AUDIO_EXTENSIONS,
...SUPPORTED_VIDEO_EXTENSIONS,
])
Expand Down Expand Up @@ -305,10 +304,17 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
context === 'profile-pictures' ||
context === 'workspace-logos'
) {
if (context !== 'copilot' && !isImageFileType(file.type)) {
throw new InvalidRequestError(
`Only image files (JPEG, PNG, GIF, WebP, SVG) are allowed for ${context} uploads`
if (context !== 'copilot') {
const mimeType = file.type
const isGenericMime = !mimeType || mimeType === 'application/octet-stream'
const extension = originalName.split('.').pop()?.toLowerCase() ?? ''
const extensionIsImage = (SUPPORTED_IMAGE_EXTENSIONS as readonly string[]).includes(
extension
)
const isImage = isGenericMime ? extensionIsImage : isImageFileType(mimeType)
if (!isImage) {
throw new InvalidRequestError(`Only image files are allowed for ${context} uploads`)
}
Comment thread
waleedlatif1 marked this conversation as resolved.
}

if (context === 'workspace-logos') {
Expand Down Expand Up @@ -344,6 +350,8 @@ export const POST = withRouteHandler(async (request: NextRequest) => {

logger.info(`Uploading ${context} file: ${originalName}`)

const resolvedContentType = resolveFileType({ type: file.type, name: originalName })

const timestamp = Date.now()
const safeFileName = sanitizeFileName(originalName)
const storageKey = `${context}/${timestamp}-${safeFileName}`
Expand All @@ -362,7 +370,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
const fileInfo = await storageService.uploadFile({
file: buffer,
fileName: storageKey,
contentType: file.type,
contentType: resolvedContentType,
context,
preserveKey: true,
customKey: storageKey,
Expand All @@ -379,7 +387,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
key: fileInfo.key,
name: originalName,
size: buffer.length,
type: file.type,
type: resolvedContentType,
Comment thread
waleedlatif1 marked this conversation as resolved.
},
directUploadSupported: false,
}
Expand All @@ -400,7 +408,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
fileName: originalName,
fileKey: fileInfo.key,
fileSize: buffer.length,
fileType: file.type,
fileType: resolvedContentType,
},
request,
})
Expand Down
33 changes: 33 additions & 0 deletions apps/sim/lib/uploads/utils/file-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ export const MIME_TYPE_MAPPING: Record<string, 'image' | 'document' | 'audio' |
'image/gif': 'image',
'image/webp': 'image',
'image/svg+xml': 'image', // SVG upload is allowed; createFileContent handles it separately for Claude API
'image/bmp': 'image',
'image/tiff': 'image',
'image/heic': 'image',
'image/heif': 'image',
'image/avif': 'image',
'image/x-icon': 'image',
'image/vnd.microsoft.icon': 'image',
Comment thread
waleedlatif1 marked this conversation as resolved.
Comment thread
waleedlatif1 marked this conversation as resolved.

// Documents
'application/pdf': 'document',
Expand Down Expand Up @@ -158,6 +165,10 @@ export function createFileContent(fileBuffer: Buffer, mimeType: string): Message
return null
}

if (contentType === 'image' && !CLAUDE_SUPPORTED_IMAGE_MIME_TYPES.has(mimeType.toLowerCase())) {
return null
}

return {
type: contentType,
source: {
Expand All @@ -168,6 +179,14 @@ export function createFileContent(fileBuffer: Buffer, mimeType: string): Message
}
}

const CLAUDE_SUPPORTED_IMAGE_MIME_TYPES = new Set([
'image/jpeg',
'image/jpg',
'image/png',
'image/gif',
'image/webp',
])
Comment thread
waleedlatif1 marked this conversation as resolved.

/**
* Extract file extension from filename
*/
Expand All @@ -184,6 +203,13 @@ const EXTENSION_TO_MIME: Record<string, string> = {
gif: 'image/gif',
webp: 'image/webp',
svg: 'image/svg+xml',
bmp: 'image/bmp',
tif: 'image/tiff',
tiff: 'image/tiff',
heic: 'image/heic',
heif: 'image/heif',
avif: 'image/avif',
ico: 'image/x-icon',
Comment thread
waleedlatif1 marked this conversation as resolved.

// Documents
pdf: 'application/pdf',
Expand Down Expand Up @@ -339,6 +365,13 @@ const MIME_TO_EXTENSION: Record<string, string> = {
'image/gif': 'gif',
'image/webp': 'webp',
'image/svg+xml': 'svg',
'image/bmp': 'bmp',
'image/tiff': 'tiff',
'image/heic': 'heic',
'image/heif': 'heif',
'image/avif': 'avif',
'image/x-icon': 'ico',
'image/vnd.microsoft.icon': 'ico',

// Documents
'application/pdf': 'pdf',
Expand Down
Loading