diff options
Diffstat (limited to 'frontend/app/api/upload')
| -rw-r--r-- | frontend/app/api/upload/route.ts | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/frontend/app/api/upload/route.ts b/frontend/app/api/upload/route.ts new file mode 100644 index 0000000..eb1ecaa --- /dev/null +++ b/frontend/app/api/upload/route.ts | |||
| @@ -0,0 +1,127 @@ | |||
| 1 | import { NextRequest, NextResponse } from 'next/server' | ||
| 2 | import { writeFile, unlink } from 'fs/promises' | ||
| 3 | import { tmpdir } from 'os' | ||
| 4 | import { join } from 'path' | ||
| 5 | import { randomUUID } from 'crypto' | ||
| 6 | import { Auth_get_user, Auth_user_can_upload } from '@/lib/auth' | ||
| 7 | import { Drive_import } from '@/lib/drive' | ||
| 8 | import { UPLOAD_MAX_FILE_SIZE, UPLOAD_MAX_FILES } from '@/lib/constants' | ||
| 9 | import { revalidatePath } from 'next/cache' | ||
| 10 | |||
| 11 | export async function POST(request: NextRequest) { | ||
| 12 | try { | ||
| 13 | // Check user authentication and permissions | ||
| 14 | const user = await Auth_get_user() | ||
| 15 | if (!user.isLoggedIn) { | ||
| 16 | return NextResponse.json({ error: 'User not authenticated' }, { status: 401 }) | ||
| 17 | } | ||
| 18 | |||
| 19 | if (!Auth_user_can_upload(user)) { | ||
| 20 | return NextResponse.json({ error: 'User does not have upload permissions' }, { status: 403 }) | ||
| 21 | } | ||
| 22 | |||
| 23 | // Parse form data | ||
| 24 | const formData = await request.formData() | ||
| 25 | const files = formData.getAll('files') as File[] | ||
| 26 | const targetPath = formData.get('targetPath') as string || '' | ||
| 27 | |||
| 28 | // Validate files | ||
| 29 | if (!files || files.length === 0) { | ||
| 30 | return NextResponse.json({ error: 'No files provided' }, { status: 400 }) | ||
| 31 | } | ||
| 32 | |||
| 33 | if (files.length > UPLOAD_MAX_FILES) { | ||
| 34 | return NextResponse.json({ | ||
| 35 | error: `Too many files. Maximum ${UPLOAD_MAX_FILES} files allowed` | ||
| 36 | }, { status: 400 }) | ||
| 37 | } | ||
| 38 | |||
| 39 | // Validate each file | ||
| 40 | for (const file of files) { | ||
| 41 | if (file.size > UPLOAD_MAX_FILE_SIZE) { | ||
| 42 | return NextResponse.json({ | ||
| 43 | error: `File '${file.name}' exceeds maximum size of ${UPLOAD_MAX_FILE_SIZE / (1024 * 1024)}MB` | ||
| 44 | }, { status: 400 }) | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | const uploadResults = [] | ||
| 49 | const tempFiles: string[] = [] | ||
| 50 | |||
| 51 | try { | ||
| 52 | // Process each file | ||
| 53 | for (const file of files) { | ||
| 54 | // Create temporary file | ||
| 55 | const tempFileName = `${randomUUID()}-${file.name}` | ||
| 56 | const tempFilePath = join(tmpdir(), tempFileName) | ||
| 57 | tempFiles.push(tempFilePath) | ||
| 58 | |||
| 59 | // Save file to temporary location | ||
| 60 | const bytes = await file.arrayBuffer() | ||
| 61 | const buffer = Buffer.from(bytes) | ||
| 62 | await writeFile(tempFilePath, buffer) | ||
| 63 | |||
| 64 | // Determine target drive path | ||
| 65 | const driveFilePath = targetPath ? `${targetPath}/${file.name}` : `/${file.name}` | ||
| 66 | |||
| 67 | try { | ||
| 68 | // Import file using Drive_import | ||
| 69 | await Drive_import(tempFilePath, driveFilePath, user.email) | ||
| 70 | uploadResults.push({ | ||
| 71 | filename: file.name, | ||
| 72 | success: true, | ||
| 73 | message: 'File uploaded successfully' | ||
| 74 | }) | ||
| 75 | } catch (error) { | ||
| 76 | console.error(`Failed to import file ${file.name}:`, error) | ||
| 77 | uploadResults.push({ | ||
| 78 | filename: file.name, | ||
| 79 | success: false, | ||
| 80 | message: error instanceof Error ? error.message : 'Unknown error during import' | ||
| 81 | }) | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | // Clean up temporary files | ||
| 86 | for (const tempFile of tempFiles) { | ||
| 87 | try { | ||
| 88 | await unlink(tempFile) | ||
| 89 | } catch (error) { | ||
| 90 | console.error(`Failed to delete temp file ${tempFile}:`, error) | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | // Revalidate the target path to refresh the directory listing | ||
| 95 | revalidatePath(`/drive${targetPath}`) | ||
| 96 | revalidatePath('/drive') | ||
| 97 | |||
| 98 | // Check if any uploads succeeded | ||
| 99 | const successfulUploads = uploadResults.filter(result => result.success) | ||
| 100 | const failedUploads = uploadResults.filter(result => !result.success) | ||
| 101 | |||
| 102 | return NextResponse.json({ | ||
| 103 | success: true, | ||
| 104 | message: `${successfulUploads.length} files uploaded successfully${failedUploads.length > 0 ? `, ${failedUploads.length} failed` : ''}`, | ||
| 105 | results: uploadResults | ||
| 106 | }) | ||
| 107 | |||
| 108 | } catch (error) { | ||
| 109 | // Clean up temporary files on error | ||
| 110 | for (const tempFile of tempFiles) { | ||
| 111 | try { | ||
| 112 | await unlink(tempFile) | ||
| 113 | } catch (cleanupError) { | ||
| 114 | console.error(`Failed to delete temp file during cleanup ${tempFile}:`, cleanupError) | ||
| 115 | } | ||
| 116 | } | ||
| 117 | throw error | ||
| 118 | } | ||
| 119 | |||
| 120 | } catch (error) { | ||
| 121 | console.error('Upload error:', error) | ||
| 122 | return NextResponse.json( | ||
| 123 | { error: error instanceof Error ? error.message : 'Internal server error' }, | ||
| 124 | { status: 500 } | ||
| 125 | ) | ||
| 126 | } | ||
| 127 | } \ No newline at end of file | ||
