diff --git a/app/api/missions/[missionId]/route.ts b/app/api/missions/[missionId]/route.ts index 849dbc6f..eb4087af 100644 --- a/app/api/missions/[missionId]/route.ts +++ b/app/api/missions/[missionId]/route.ts @@ -83,7 +83,7 @@ export async function GET(request: Request, props: { params: Promise<{ missionId // Add public URLs to mission logo and attachments const missionWithUrls = { ...mission, - logoUrl: mission.logo ? `/api/missions/image/${mission.logo.replace('missions/', '')}` : null, + logoUrl: mission.logo ? `/api/missions/image/${mission.logo}` : null, logo: mission.logo, attachments: mission.attachments.map((attachment: { id: string; filename: string; filePath: string; fileType: string; fileSize: number; createdAt: Date }) => ({ ...attachment, diff --git a/app/api/missions/image/[...path]/route.ts b/app/api/missions/image/[...path]/route.ts index 598d42db..dbe48cf9 100644 --- a/app/api/missions/image/[...path]/route.ts +++ b/app/api/missions/image/[...path]/route.ts @@ -3,6 +3,7 @@ import { getServerSession } from 'next-auth'; import { authOptions } from '@/app/api/auth/options'; import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'; import { S3_CONFIG } from '@/lib/s3'; +import { NoSuchKey } from '@aws-sdk/client-s3'; // Initialize S3 client const s3Client = new S3Client({ @@ -21,48 +22,46 @@ export async function GET( { params }: { params: Promise<{ path: string[] }> } ) { try { - // Await the params promise to get the actual path parameter - const { path } = await params - - // Check if path is provided - if (!path || path.length === 0) { + const { path: pathSegments } = await params; + if (!pathSegments || pathSegments.length === 0) { return new NextResponse('Path is required', { status: 400 }); } // Reconstruct the full path from path segments - const filePath = path.join('/'); + const filePath = pathSegments.join('/'); console.log('Fetching mission image:', filePath); - // Create S3 command to get the object + // Ensure the path has the missions/ prefix + const minioPath = filePath.startsWith('missions/') ? filePath : `missions/${filePath}`; + console.log('Full Minio path:', minioPath); + const command = new GetObjectCommand({ - Bucket: S3_CONFIG.bucket, // Use the pages bucket - Key: filePath, + Bucket: process.env.MINIO_BUCKET_NAME || 'missions', + Key: minioPath, }); - // Get the object from S3/Minio - const response = await s3Client.send(command); - - if (!response.Body) { - console.error('File not found in Minio:', filePath); - return new NextResponse('File not found', { status: 404 }); + try { + const response = await s3Client.send(command); + if (!response.Body) { + return new NextResponse('File not found', { status: 404 }); + } + + // Set appropriate content type and cache control + const contentType = response.ContentType || 'application/octet-stream'; + const headers = new Headers(); + headers.set('Content-Type', contentType); + headers.set('Cache-Control', 'public, max-age=31536000'); + + return new NextResponse(response.Body as any, { headers }); + } catch (error) { + console.error('Error fetching file from Minio:', error); + if (error instanceof NoSuchKey) { + return new NextResponse('File not found', { status: 404 }); + } + return new NextResponse('Internal Server Error', { status: 500 }); } - - // Get the readable web stream directly - const stream = response.Body.transformToWebStream(); - - // Determine content type - const contentType = response.ContentType || 'application/octet-stream'; - - // Create and return a new response with the file stream - return new NextResponse(stream as ReadableStream, { - headers: { - 'Content-Type': contentType, - 'Cache-Control': 'public, max-age=31536000', // Cache for 1 year - }, - }); } catch (error) { - console.error('Error serving mission image:', error); - console.error('Error details:', JSON.stringify(error, null, 2)); + console.error('Error in image serving:', error); return new NextResponse('Internal Server Error', { status: 500 }); } } \ No newline at end of file diff --git a/app/api/missions/route.ts b/app/api/missions/route.ts index ef6ad2f2..8d8805b6 100644 --- a/app/api/missions/route.ts +++ b/app/api/missions/route.ts @@ -64,6 +64,14 @@ interface MissionResponse { penpotProjectId: string | null; createdAt: Date; updatedAt: Date; + attachments?: Array<{ + id: string; + filename: string; + filePath: string; + fileType: string; + fileSize: number; + createdAt: Date; + }>; } // Helper function to check authentication @@ -123,14 +131,36 @@ export async function GET(request: Request) { } } } + }, + attachments: { + select: { + id: true, + filename: true, + filePath: true, + fileType: true, + fileSize: true, + createdAt: true + }, + orderBy: { createdAt: 'desc' } } } }); const totalCount = await prisma.mission.count({ where }); + // Transform missions to include public URLs + const missionsWithUrls = missions.map(mission => ({ + ...mission, + logoUrl: mission.logo ? `/api/missions/image/${mission.logo}` : null, + logo: mission.logo, + attachments: mission.attachments?.map(attachment => ({ + ...attachment, + publicUrl: `/api/missions/image/${attachment.filePath}` + })) || [] + })); + return NextResponse.json({ - missions, + missions: missionsWithUrls, pagination: { total: totalCount, offset,