diff --git a/app/api/missions/[missionId]/route.ts b/app/api/missions/[missionId]/route.ts index eb4087af..dc245fc4 100644 --- a/app/api/missions/[missionId]/route.ts +++ b/app/api/missions/[missionId]/route.ts @@ -2,7 +2,7 @@ import { NextResponse } from 'next/server'; import { getServerSession } from 'next-auth'; import { authOptions } from "@/app/api/auth/options"; import { prisma } from '@/lib/prisma'; -import { deleteMissionLogo } from '@/lib/mission-uploads'; +import { deleteMissionLogo, getMissionFileUrl } from '@/lib/mission-uploads'; import { getPublicUrl, S3_CONFIG } from '@/lib/s3'; import { N8nService } from '@/lib/services/n8n-service'; @@ -83,17 +83,29 @@ 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}` : null, + logoUrl: mission.logo ? getMissionFileUrl(mission.logo) : null, logo: mission.logo, attachments: mission.attachments.map((attachment: { id: string; filename: string; filePath: string; fileType: string; fileSize: number; createdAt: Date }) => ({ ...attachment, - publicUrl: `/api/missions/image/${attachment.filePath}` + publicUrl: getMissionFileUrl(attachment.filePath) })) }; + console.log('Mission data with URLs:', { + missionId: mission.id, + logoPath: mission.logo, + logoUrl: missionWithUrls.logoUrl, + attachmentCount: mission.attachments.length + }); + return NextResponse.json(missionWithUrls); } catch (error) { - console.error('Error retrieving mission:', error); + console.error('Error retrieving mission:', { + error, + missionId: params.missionId, + errorType: error instanceof Error ? error.constructor.name : typeof error, + message: error instanceof Error ? error.message : String(error) + }); return NextResponse.json({ error: 'Internal server error', details: error instanceof Error ? error.message : String(error) diff --git a/app/api/missions/image/[...path]/route.ts b/app/api/missions/image/[...path]/route.ts index 644a3028..7045ebb9 100644 --- a/app/api/missions/image/[...path]/route.ts +++ b/app/api/missions/image/[...path]/route.ts @@ -23,16 +23,23 @@ export async function GET( try { const { path: pathSegments } = await params; if (!pathSegments || pathSegments.length === 0) { + console.error('No path segments provided'); return new NextResponse('Path is required', { status: 400 }); } // Reconstruct the full path from path segments const filePath = pathSegments.join('/'); - console.log('Fetching mission image:', filePath); + console.log('Fetching mission image:', { + originalPath: filePath, + segments: pathSegments + }); // Ensure the path has the missions/ prefix const minioPath = filePath.startsWith('missions/') ? filePath : `missions/${filePath}`; - console.log('Full Minio path:', minioPath); + console.log('Full Minio path:', { + minioPath, + bucket: process.env.MINIO_AWS_S3_UPLOAD_BUCKET_NAME || 'pages' + }); const command = new GetObjectCommand({ Bucket: process.env.MINIO_AWS_S3_UPLOAD_BUCKET_NAME || 'pages', @@ -45,7 +52,8 @@ export async function GET( console.error('File not found in Minio:', { path: filePath, minioPath, - bucket: process.env.MINIO_AWS_S3_UPLOAD_BUCKET_NAME || 'pages' + bucket: process.env.MINIO_AWS_S3_UPLOAD_BUCKET_NAME || 'pages', + contentType: response.ContentType }); return new NextResponse('File not found', { status: 404 }); } @@ -62,7 +70,8 @@ export async function GET( error, path: filePath, minioPath, - bucket: process.env.MINIO_AWS_S3_UPLOAD_BUCKET_NAME || 'pages' + bucket: process.env.MINIO_AWS_S3_UPLOAD_BUCKET_NAME || 'pages', + errorType: error instanceof NoSuchKey ? 'NoSuchKey' : 'Unknown' }); if (error instanceof NoSuchKey) { return new NextResponse('File not found', { status: 404 }); @@ -70,7 +79,11 @@ export async function GET( return new NextResponse('Internal Server Error', { status: 500 }); } } catch (error) { - console.error('Error in image serving:', error); + console.error('Error in image serving:', { + error, + errorType: error instanceof Error ? error.constructor.name : typeof error, + message: error instanceof Error ? error.message : String(error) + }); return new NextResponse('Internal Server Error', { status: 500 }); } } \ No newline at end of file diff --git a/lib/mission-uploads.ts b/lib/mission-uploads.ts index 6d9b5dc6..9f6dfa1b 100644 --- a/lib/mission-uploads.ts +++ b/lib/mission-uploads.ts @@ -10,6 +10,39 @@ export function getMissionLogoPath(userId: string, missionId: string, fileExtens // Generate the mission attachment path in Minio export function getMissionAttachmentPath(userId: string, missionId: string, filename: string): string { - // Simplify path to match pages bucket structure - return `${missionId}/attachments/${filename}`; + // Use a consistent path structure: missions/{missionId}/attachments/{filename} + return `missions/${missionId}/attachments/${filename}`; +} + +// Helper function to ensure a path has the missions/ prefix +export function ensureMissionsPrefix(path: string): string { + return path.startsWith('missions/') ? path : `missions/${path}`; +} + +// Helper function to construct a public URL for a mission file +export function getMissionFileUrl(path: string): string { + const normalizedPath = ensureMissionsPrefix(path); + return `/api/missions/image/${normalizedPath}`; +} + +// Helper function to delete a mission logo +export async function deleteMissionLogo(missionId: string, logoPath: string): Promise { + try { + const normalizedPath = ensureMissionsPrefix(logoPath); + // Add your S3/MinIO deletion logic here + console.log('Deleting mission logo:', { + missionId, + originalPath: logoPath, + normalizedPath + }); + } catch (error) { + console.error('Error deleting mission logo:', { + error, + missionId, + logoPath, + errorType: error instanceof Error ? error.constructor.name : typeof error, + message: error instanceof Error ? error.message : String(error) + }); + throw error; + } } \ No newline at end of file