Files
sim/apps/sim/app/api/files/download/route.ts
Vikhyath Mondreti 193b95cfec fix(auth): swap out hybrid auth in relevant callsites (#3160)
* fix(logs): execution files should always use our internal route

* correct degree of access control

* fix tests

* fix tag defs flag

* fix type check

* fix mcp tools

* make webhooks consistent

* fix ollama and vllm visibility

* remove dup test
2026-02-06 22:07:55 -08:00

87 lines
2.7 KiB
TypeScript

import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
import type { StorageContext } from '@/lib/uploads/config'
import { hasCloudStorage } from '@/lib/uploads/core/storage-service'
import { verifyFileAccess } from '@/app/api/files/authorization'
import { createErrorResponse, FileNotFoundError } from '@/app/api/files/utils'
const logger = createLogger('FileDownload')
export const dynamic = 'force-dynamic'
export async function POST(request: NextRequest) {
try {
const authResult = await checkSessionOrInternalAuth(request, { requireWorkflowId: false })
if (!authResult.success || !authResult.userId) {
logger.warn('Unauthorized download URL request', {
error: authResult.error || 'Missing userId',
})
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const userId = authResult.userId
const body = await request.json()
const { key, name, isExecutionFile, context, url } = body
if (!key) {
return createErrorResponse(new Error('File key is required'), 400)
}
if (key.startsWith('url/')) {
if (!url) {
return createErrorResponse(new Error('URL is required for URL-type files'), 400)
}
return NextResponse.json({
downloadUrl: url,
expiresIn: null,
fileName: name || key.split('/').pop() || 'download',
})
}
let storageContext: StorageContext = context || 'general'
if (isExecutionFile && !context) {
storageContext = 'execution'
logger.info(`Using execution context for file: ${key}`)
}
const hasAccess = await verifyFileAccess(
key,
userId,
undefined, // customConfig
storageContext, // context
!hasCloudStorage() // isLocal
)
if (!hasAccess) {
logger.warn('Unauthorized download URL request', { userId, key, context: storageContext })
throw new FileNotFoundError(`File not found: ${key}`)
}
const { getBaseUrl } = await import('@/lib/core/utils/urls')
const downloadUrl = `${getBaseUrl()}/api/files/serve/${encodeURIComponent(key)}?context=${storageContext}`
logger.info(`Generated download URL for ${storageContext} file: ${key}`)
return NextResponse.json({
downloadUrl,
expiresIn: null,
fileName: name || key.split('/').pop() || 'download',
})
} catch (error) {
logger.error('Error in file download endpoint:', error)
if (error instanceof FileNotFoundError) {
return createErrorResponse(error)
}
return createErrorResponse(
error instanceof Error ? error : new Error('Internal server error'),
500
)
}
}