NeahNew/app/api/missions/upload/route.ts
2025-05-06 10:51:17 +02:00

235 lines
8.2 KiB
TypeScript

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 } 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);
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);
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 });
}
}