import { createLogger } from '@sim/logger' import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { vantaUploadContract } from '@/lib/api/contracts/tools/vanta' import { parseRequest } from '@/lib/api/server' import { checkInternalAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { processFilesToUserFiles, type RawFileInput } from '@/lib/uploads/utils/file-utils' import { downloadServableFileFromStorage } from '@/lib/uploads/utils/file-utils.server' import { docNotReadyResponse } from '@/lib/uploads/utils/servable-file-response' import { assertToolFileAccess } from '@/app/api/files/authorization' import { asVantaRecord, buildVantaUrl, extractVantaError, fetchVantaWithAuth, getVantaBaseUrl, normalizeVantaUploadedFile, VANTA_DOCUMENT_UPLOAD_SCOPE, } from '@/tools/vanta/utils' export const dynamic = 'force-dynamic' const logger = createLogger('VantaUploadAPI') const MAX_UPLOAD_SIZE_BYTES = 100 * 1024 * 1024 function uploadSizeError(bytes: number): NextResponse { const sizeMB = (bytes / (1024 * 1024)).toFixed(2) return NextResponse.json( { success: false, error: `File size (${sizeMB}MB) exceeds upload limit of 100MB` }, { status: 400 } ) } export const POST = withRouteHandler(async (request: NextRequest) => { const requestId = generateRequestId() try { const authResult = await checkInternalAuth(request, { requireWorkflowId: false }) if (!authResult.success || !authResult.userId) { logger.warn(`[${requestId}] Unauthorized Vanta upload attempt`, { error: authResult.error || 'Missing userId', }) return NextResponse.json( { success: false, error: authResult.error || 'Unauthorized' }, { status: 401 } ) } const parsed = await parseRequest(vantaUploadContract, request, {}) if (!parsed.success) return parsed.response const params = parsed.data.body let fileBuffer: Buffer let fileName: string let mimeType: string if (params.file) { const userFiles = processFilesToUserFiles([params.file as RawFileInput], requestId, logger) if (userFiles.length === 0) { return NextResponse.json({ success: false, error: 'Invalid file input' }, { status: 400 }) } const userFile = userFiles[0] const denied = await assertToolFileAccess(userFile.key, authResult.userId, requestId, logger) if (denied) return denied if (userFile.size > MAX_UPLOAD_SIZE_BYTES) { return uploadSizeError(userFile.size) } try { const resolved = await downloadServableFileFromStorage(userFile, requestId, logger) fileBuffer = resolved.buffer fileName = params.fileName || userFile.name mimeType = resolved.contentType || userFile.type || params.mimeType || 'application/octet-stream' } catch (error) { const notReady = docNotReadyResponse(error) if (notReady) return notReady logger.error(`[${requestId}] Failed to download Vanta upload file`, { error: getErrorMessage(error), }) return NextResponse.json( { success: false, error: getErrorMessage(error, 'Failed to download file') }, { status: 500 } ) } } else if (params.fileContent) { fileBuffer = Buffer.from(params.fileContent, 'base64') fileName = params.fileName || 'file' mimeType = params.mimeType || 'application/octet-stream' } else { return NextResponse.json({ success: false, error: 'File is required' }, { status: 400 }) } if (fileBuffer.length > MAX_UPLOAD_SIZE_BYTES) { return uploadSizeError(fileBuffer.length) } logger.info(`[${requestId}] Uploading file to Vanta document`, { documentId: params.documentId, fileName, size: fileBuffer.length, }) const uploadUrl = buildVantaUrl( getVantaBaseUrl(params.region), `/documents/${encodeURIComponent(params.documentId)}/uploads` ) const response = await fetchVantaWithAuth( { clientId: params.clientId, clientSecret: params.clientSecret, region: params.region, scope: VANTA_DOCUMENT_UPLOAD_SCOPE, }, (accessToken) => { const formData = new FormData() formData.append( 'file', new Blob([new Uint8Array(fileBuffer)], { type: mimeType }), fileName ) if (params.description) { formData.append('description', params.description) } if (params.effectiveAtDate) { formData.append('effectiveAtDate', params.effectiveAtDate) } return fetch(uploadUrl, { method: 'POST', headers: { Authorization: `Bearer ${accessToken}` }, body: formData, cache: 'no-store', }) } ) const data: unknown = await response.json().catch(() => null) if (!response.ok) { const message = extractVantaError(data, 'Failed to upload file to Vanta document') logger.error(`[${requestId}] Vanta upload failed`, { status: response.status, message }) return NextResponse.json({ success: false, error: message }, { status: response.status }) } logger.info(`[${requestId}] Vanta upload successful`, { documentId: params.documentId }) return NextResponse.json({ success: true, output: { upload: normalizeVantaUploadedFile(asVantaRecord(data)) }, }) } catch (error) { const message = toError(error).message logger.error(`[${requestId}] Vanta upload failed`, { error: message }) return NextResponse.json({ success: false, error: message }, { status: 500 }) } })