176 lines
5.8 KiB
TypeScript
176 lines
5.8 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';
|
|
|
|
// 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) {
|
|
try {
|
|
const { authorized, userId } = await checkAuth(request);
|
|
if (!authorized || !userId) {
|
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
}
|
|
|
|
// Parse the 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;
|
|
|
|
if (!missionId || !type || !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
|
|
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
|
|
if (mission.creatorId !== userId) {
|
|
return NextResponse.json({ error: 'Not authorized to upload to this mission' }, { status: 403 });
|
|
}
|
|
|
|
if (type === 'logo') {
|
|
// Upload logo file to Minio
|
|
const { filePath } = await uploadMissionLogo(userId, missionId, file);
|
|
|
|
// Update mission record with logo path
|
|
await prisma.mission.update({
|
|
where: { id: missionId },
|
|
data: { logo: filePath }
|
|
});
|
|
|
|
return NextResponse.json({ success: true, filePath });
|
|
}
|
|
else if (type === 'attachment') {
|
|
// Upload attachment file to Minio
|
|
const { filename, filePath, fileType, fileSize } = await uploadMissionAttachment(
|
|
userId,
|
|
missionId,
|
|
file
|
|
);
|
|
|
|
// Create attachment record in database
|
|
const attachment = await prisma.attachment.create({
|
|
data: {
|
|
filename,
|
|
filePath,
|
|
fileType,
|
|
fileSize,
|
|
missionId,
|
|
uploaderId: userId
|
|
}
|
|
});
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
attachment: {
|
|
id: attachment.id,
|
|
filename: attachment.filename,
|
|
filePath: attachment.filePath,
|
|
fileType: attachment.fileType,
|
|
fileSize: attachment.fileSize,
|
|
createdAt: attachment.createdAt
|
|
}
|
|
});
|
|
}
|
|
else {
|
|
return NextResponse.json({ error: 'Invalid upload type' }, { status: 400 });
|
|
}
|
|
} catch (error) {
|
|
console.error('Error uploading file:', error);
|
|
return NextResponse.json({
|
|
error: 'Internal server error',
|
|
details: error instanceof Error ? error.message : String(error)
|
|
}, { status: 500 });
|
|
}
|
|
}
|