project-standalo-sonic-cloud/lib/upload.ts

160 lines
4.3 KiB
TypeScript

import { prisma } from './prisma'
import { requireAuth } from './auth'
// Using type from Prisma for now
type UploadSession = any
const DEFAULT_CHUNK_SIZE = 1024 * 1024 * 5 // 5MB chunks
const UPLOAD_SESSION_EXPIRY = 24 * 60 * 60 * 1000 // 24 hours in ms
export async function createUploadSession(
userId: string,
fileName: string,
fileSize: number,
mimeType: string,
chunkSize?: number,
metadata?: Record<string, unknown>
): Promise<any> {
const actualChunkSize = chunkSize || DEFAULT_CHUNK_SIZE
const totalChunks = Math.ceil(fileSize / actualChunkSize)
const expiresAt = new Date(Date.now() + UPLOAD_SESSION_EXPIRY)
const session = await prisma.uploadSession.create({
data: {
userId,
fileName,
fileSize,
mimeType,
chunkSize: actualChunkSize,
totalChunks,
metadata: metadata ? JSON.stringify(metadata) : undefined,
expiresAt,
},
})
return {
...session,
uploadedChunks: session.uploadedChunks ? JSON.parse(session.uploadedChunks) : [],
metadata: session.metadata ? JSON.parse(session.metadata) : undefined,
}
}
export async function getUploadSession(uploadId: string, userId: string): Promise<UploadSession | null> {
const session = await prisma.uploadSession.findFirst({
where: {
id: uploadId,
userId,
expiresAt: {
gt: new Date(),
},
},
})
if (!session) {
return null
}
return {
...session,
uploadedChunks: session.uploadedChunks ? JSON.parse(session.uploadedChunks) : [],
metadata: session.metadata ? JSON.parse(session.metadata) : undefined,
}
}
export async function markChunkUploaded(uploadId: string, chunkIndex: number): Promise<any> {
const session = await prisma.uploadSession.findUnique({
where: { id: uploadId },
})
if (!session) {
throw new Error('Upload session not found')
}
const uploadedChunks = session.uploadedChunks ? JSON.parse(session.uploadedChunks) : []
if (!uploadedChunks.includes(chunkIndex)) {
uploadedChunks.push(chunkIndex)
}
const updatedSession = await prisma.uploadSession.update({
where: { id: uploadId },
data: {
uploadedChunks: JSON.stringify(uploadedChunks),
status: uploadedChunks.length >= session.totalChunks ? 'completed' : 'uploading',
},
})
return {
...updatedSession,
uploadedChunks: JSON.parse(updatedSession.uploadedChunks || '[]'),
metadata: updatedSession.metadata ? JSON.parse(updatedSession.metadata) : undefined,
}
}
export async function completeUploadSession(uploadId: string, fileId: string): Promise<any> {
const session = await prisma.uploadSession.update({
where: { id: uploadId },
data: {
status: 'completed',
fileId,
},
})
return {
...session,
uploadedChunks: session.uploadedChunks ? JSON.parse(session.uploadedChunks) : [],
metadata: session.metadata ? JSON.parse(session.metadata) : undefined,
}
}
export async function failUploadSession(uploadId: string): Promise<void> {
await prisma.uploadSession.update({
where: { id: uploadId },
data: {
status: 'failed',
},
})
}
export async function generatePresignedUrl(
fileName: string,
mimeType: string,
expiresIn: number = 3600
): Promise<{ url: string; key: string }> {
// This is a placeholder for actual S3/CloudStorage presigned URL generation
// In a real implementation, you would use AWS SDK, Google Cloud Storage SDK, etc.
const key = `uploads/${Date.now()}-${fileName}`
// For now, return a mock URL - in production, generate a real presigned URL
const url = `${process.env.NEXT_PUBLIC_APP_URL}/api/upload/presigned-upload?key=${encodeURIComponent(key)}`
return { url, key }
}
export async function cleanupExpiredSessions(): Promise<void> {
await prisma.uploadSession.deleteMany({
where: {
expiresAt: {
lt: new Date(),
},
status: {
in: ['pending', 'uploading'],
},
},
})
}
// Helper function to validate file type
export function validateFileType(mimeType: string, allowedTypes: string[]): boolean {
return allowedTypes.some(type => {
if (type.endsWith('/*')) {
return mimeType.startsWith(type.slice(0, -1))
}
return mimeType === type
})
}
// Helper function to validate file size
export function validateFileSize(fileSize: number, maxSize: number): boolean {
return fileSize <= maxSize
}