import { NextResponse } from 'next/server'; import { getServerSession } from 'next-auth'; import { authOptions } from "@/app/api/auth/options"; import { PrismaClient } from '@prisma/client'; import { prisma } from '@/lib/prisma'; import { uploadMissionLogo, uploadMissionAttachment, generateMissionLogoUploadUrl, generateMissionAttachmentUploadUrl } from '@/lib/mission-uploads'; import { getPublicUrl, S3_CONFIG } from '@/lib/s3'; // Helper function to check authentication async function checkAuth(request: Request) { const session = await getServerSession(authOptions); if (!session?.user?.id) { console.error('Unauthorized access attempt:', { url: request.url, method: request.method, headers: Object.fromEntries(request.headers) }); return { authorized: false, userId: null }; } return { authorized: true, userId: session.user.id }; } // Generate presigned URL for direct upload to S3/Minio export async function GET(request: Request) { try { const { authorized, userId } = await checkAuth(request); if (!authorized || !userId) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); } const { searchParams } = new URL(request.url); const missionId = searchParams.get('missionId'); const type = searchParams.get('type'); // 'logo' or 'attachment' const filename = searchParams.get('filename'); if (!missionId || !type || !filename) { return NextResponse.json({ error: 'Missing required parameters', required: { missionId: true, type: true, filename: true }, received: { missionId: !!missionId, type: !!type, filename: !!filename } }, { status: 400 }); } // Verify that the mission exists and user has access to it const mission = await prisma.mission.findUnique({ where: { id: missionId }, select: { id: true, creatorId: true } }); if (!mission) { return NextResponse.json({ error: 'Mission not found' }, { status: 404 }); } // Currently only allow creator to upload files // You can modify this to include other roles if needed if (mission.creatorId !== userId) { return NextResponse.json({ error: 'Not authorized to upload to this mission' }, { status: 403 }); } let result; if (type === 'logo') { // For logo, we expect filename to contain the file extension (e.g., '.jpg') const fileExtension = filename.substring(filename.lastIndexOf('.')); result = await generateMissionLogoUploadUrl(userId, missionId, fileExtension); } else if (type === 'attachment') { result = await generateMissionAttachmentUploadUrl(userId, missionId, filename); } else { return NextResponse.json({ error: 'Invalid upload type' }, { status: 400 }); } return NextResponse.json(result); } catch (error) { console.error('Error generating upload URL:', error); return NextResponse.json({ error: 'Internal server error', details: error instanceof Error ? error.message : String(error) }, { status: 500 }); } } // Handle file upload (server-side) export async function POST(request: Request) { console.log('=== File upload request received ==='); try { console.log('Checking authentication...'); const { authorized, userId } = await checkAuth(request); if (!authorized || !userId) { console.log('Authentication failed'); return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); } console.log('User authenticated:', userId); // Parse the form data console.log('Parsing form data...'); const formData = await request.formData(); const missionId = formData.get('missionId') as string; const type = formData.get('type') as string; // 'logo' or 'attachment' const file = formData.get('file') as File; console.log('Form data received:', { missionId, type, fileExists: !!file, fileName: file?.name, fileSize: file?.size, fileType: file?.type }); if (!missionId || !type || !file) { console.log('Missing required fields:', { missionId: !!missionId, type: !!type, file: !!file }); return NextResponse.json({ error: 'Missing required fields', required: { missionId: true, type: true, file: true }, received: { missionId: !!missionId, type: !!type, file: !!file } }, { status: 400 }); } // Verify that the mission exists and user has access to it console.log('Verifying mission access...'); const mission = await prisma.mission.findUnique({ where: { id: missionId }, select: { id: true, creatorId: true } }); if (!mission) { console.log('Mission not found:', missionId); return NextResponse.json({ error: 'Mission not found' }, { status: 404 }); } // Currently only allow creator to upload files if (mission.creatorId !== userId) { console.log('User not authorized to upload to this mission', { userId, creatorId: mission.creatorId }); return NextResponse.json({ error: 'Not authorized to upload to this mission' }, { status: 403 }); } console.log('Mission access verified'); if (type === 'logo') { console.log('Processing logo upload...'); try { // Upload logo file to Minio const { filePath } = await uploadMissionLogo(userId, missionId, file); console.log('Logo uploaded successfully to path:', filePath); // Generate public URL const publicUrl = getPublicUrl(filePath, S3_CONFIG.bucket); console.log('Public URL for logo:', publicUrl); // Update mission record with logo path console.log('Updating mission record with logo path...'); await prisma.mission.update({ where: { id: missionId }, data: { logo: filePath } }); console.log('Mission record updated'); return NextResponse.json({ success: true, filePath, publicUrl }); } catch (logoError) { console.error('Error in logo upload process:', logoError); return NextResponse.json({ error: 'Logo upload failed', details: logoError instanceof Error ? logoError.message : String(logoError) }, { status: 500 }); } } else if (type === 'attachment') { // Upload attachment file to Minio console.log('Processing attachment upload...'); try { const { filename, filePath, fileType, fileSize } = await uploadMissionAttachment( userId, missionId, file ); console.log('Attachment uploaded successfully to path:', filePath); // Generate public URL const publicUrl = getPublicUrl(filePath, S3_CONFIG.bucket); console.log('Public URL for attachment:', publicUrl); // Create attachment record in database console.log('Creating attachment record in database...'); const attachment = await prisma.attachment.create({ data: { filename, filePath, fileType, fileSize, missionId, uploaderId: userId } }); console.log('Attachment record created:', attachment.id); return NextResponse.json({ success: true, attachment: { id: attachment.id, filename: attachment.filename, filePath: attachment.filePath, publicUrl, fileType: attachment.fileType, fileSize: attachment.fileSize, createdAt: attachment.createdAt } }); } catch (attachmentError) { console.error('Error in attachment upload process:', attachmentError); return NextResponse.json({ error: 'Attachment upload failed', details: attachmentError instanceof Error ? attachmentError.message : String(attachmentError) }, { status: 500 }); } } else { console.log('Invalid upload type:', type); return NextResponse.json({ error: 'Invalid upload type' }, { status: 400 }); } } catch (error) { console.error('Unhandled error in upload process:', error); return NextResponse.json({ error: 'Internal server error', details: error instanceof Error ? error.message : String(error) }, { status: 500 }); } }