105 lines
3.2 KiB
TypeScript
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 }
|
|
)
|
|
}
|
|
} |