project-standalo-sonic-cloud/app/api/upload/chunk/[uploadId]/[chunkIndex]/route.ts

105 lines
3.2 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server'
import { requireAuth } from '@/lib/auth'
import { getUploadSession, markChunkUploaded, generatePresignedUrl } from '@/lib/upload'
import { writeFile, mkdir } from 'fs/promises'
import { join } from 'path'
export async function POST(
request: NextRequest,
{ params }: { params: Promise<{ uploadId: string; chunkIndex: string }> }
) {
try {
const user = await requireAuth()
const { uploadId, chunkIndex: chunkIndexStr } = await params
const chunkIndex = parseInt(chunkIndexStr)
if (isNaN(chunkIndex)) {
return NextResponse.json(
{ error: 'Invalid chunk index' },
{ status: 400 }
)
}
// Get upload session
const uploadSession = await getUploadSession(uploadId, user.id)
if (!uploadSession) {
return NextResponse.json(
{ error: 'Upload session not found or expired' },
{ status: 404 }
)
}
if (uploadSession.status === 'completed') {
return NextResponse.json(
{ error: 'Upload already completed' },
{ status: 400 }
)
}
if (chunkIndex < 0 || chunkIndex >= uploadSession.totalChunks) {
return NextResponse.json(
{ error: 'Invalid chunk index' },
{ status: 400 }
)
}
// Get the chunk data from the request
const chunkData = await request.arrayBuffer()
const expectedSize = Math.min(uploadSession.chunkSize, uploadSession.fileSize - chunkIndex * uploadSession.chunkSize)
if (chunkData.byteLength !== expectedSize) {
return NextResponse.json(
{ error: `Invalid chunk size. Expected ${expectedSize}, got ${chunkData.byteLength}` },
{ status: 400 }
)
}
// In a real implementation, you would:
// 1. Upload to S3/CloudStorage using the presigned URL
// 2. Or store temporarily on disk/cloud storage
// For now, we'll simulate by storing in a temporary directory
const tempDir = join(process.cwd(), 'temp', 'uploads', uploadId)
await mkdir(tempDir, { recursive: true })
const chunkPath = join(tempDir, `chunk-${chunkIndex}`)
await writeFile(chunkPath, Buffer.from(chunkData))
// Mark chunk as uploaded
const updatedSession = await markChunkUploaded(uploadId, chunkIndex)
// Generate presigned URL for next chunk if not complete
let nextPresignedUrl = null
if (updatedSession.status !== 'completed' && chunkIndex + 1 < uploadSession.totalChunks) {
const { url } = await generatePresignedUrl(
`chunk-${chunkIndex + 1}-${uploadSession.fileName}`,
uploadSession.mimeType
)
nextPresignedUrl = url
}
return NextResponse.json(
{
success: true,
chunkIndex,
uploadedChunks: updatedSession.uploadedChunks,
totalChunks: updatedSession.totalChunks,
status: updatedSession.status,
nextPresignedUrl,
},
{ status: 200 }
)
} catch (error) {
console.error('Chunk upload error:', error)
if (error instanceof Error && error.message === 'Unauthorized') {
return NextResponse.json(
{ error: 'Unauthorized' },
{ status: 401 }
)
}
return NextResponse.json(
{ error: 'Failed to upload chunk' },
{ status: 500 }
)
}
}