112 lines
3.7 KiB
TypeScript
112 lines
3.7 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server'
|
|
import { requireAuth } from '@/lib/auth'
|
|
import { getUploadSession, completeUploadSession } from '@/lib/upload'
|
|
import { prisma } from '@/lib/prisma'
|
|
import { readFile, writeFile, mkdir, rm } from 'fs/promises'
|
|
import { join } from 'path'
|
|
|
|
export async function POST(
|
|
request: NextRequest,
|
|
{ params }: { params: Promise<{ uploadId: string }> }
|
|
) {
|
|
try {
|
|
const user = await requireAuth()
|
|
const { uploadId } = await params
|
|
|
|
// 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 not complete. All chunks must be uploaded first.' },
|
|
{ status: 400 }
|
|
)
|
|
}
|
|
|
|
// Combine chunks into a single file
|
|
const tempDir = join(process.cwd(), 'temp', 'uploads', uploadId)
|
|
const finalPath = join(process.cwd(), 'uploads', `${uploadId}-${uploadSession.fileName}`)
|
|
|
|
// Ensure uploads directory exists
|
|
await mkdir(join(process.cwd(), 'uploads'), { recursive: true })
|
|
|
|
// Combine all chunks
|
|
const fileBuffer = Buffer.alloc(uploadSession.fileSize)
|
|
let offset = 0
|
|
|
|
for (let i = 0; i < uploadSession.totalChunks; i++) {
|
|
const chunkPath = join(tempDir, `chunk-${i}`)
|
|
const chunkData = await readFile(chunkPath)
|
|
chunkData.copy(fileBuffer, offset)
|
|
offset += chunkData.length
|
|
}
|
|
|
|
// Write the complete file
|
|
await writeFile(finalPath, fileBuffer)
|
|
|
|
// Create a database record for the uploaded file
|
|
const fileId = `file_${Date.now()}_${uploadId}`
|
|
|
|
// In a real implementation, you would:
|
|
// 1. Upload to S3/CloudStorage
|
|
// 2. Store the URL/Key in the database
|
|
// 3. Process the file (transcode audio, generate thumbnails, etc.)
|
|
// For now, we'll create a simple record
|
|
|
|
let createdRecord = null
|
|
|
|
if (uploadSession.mimeType.startsWith('audio/')) {
|
|
// Create a song record
|
|
createdRecord = await prisma.song.create({
|
|
data: {
|
|
artistId: user.id, // Assuming user is an artist
|
|
title: uploadSession.fileName.replace(/\.[^/.]+$/, ''), // Remove extension
|
|
slug: uploadSession.fileName.replace(/\.[^/.]+$/, '').toLowerCase().replace(/\s+/g, '-'),
|
|
audioUrl: `/uploads/${uploadId}-${uploadSession.fileName}`,
|
|
duration: 0, // Would be determined during processing
|
|
isPublic: false, // Default to private until processed
|
|
},
|
|
})
|
|
} else if (uploadSession.mimeType.startsWith('image/')) {
|
|
// Could create an image record or associate with artist/album
|
|
// For now, just store the file reference
|
|
}
|
|
|
|
// Mark upload session as complete with file ID
|
|
const completedSession = await completeUploadSession(uploadId, fileId)
|
|
|
|
// Clean up temporary chunks
|
|
await rm(tempDir, { recursive: true, force: true })
|
|
|
|
return NextResponse.json(
|
|
{
|
|
success: true,
|
|
fileId,
|
|
fileName: uploadSession.fileName,
|
|
fileSize: uploadSession.fileSize,
|
|
mimeType: uploadSession.mimeType,
|
|
record: createdRecord,
|
|
uploadSession: completedSession,
|
|
},
|
|
{ status: 200 }
|
|
)
|
|
} catch (error) {
|
|
console.error('Upload complete error:', error)
|
|
if (error instanceof Error && error.message === 'Unauthorized') {
|
|
return NextResponse.json(
|
|
{ error: 'Unauthorized' },
|
|
{ status: 401 }
|
|
)
|
|
}
|
|
return NextResponse.json(
|
|
{ error: 'Failed to complete upload' },
|
|
{ status: 500 }
|
|
)
|
|
}
|
|
} |