From dddfd5024bced192a2b050a361e55496f7461fec Mon Sep 17 00:00:00 2001 From: alma Date: Tue, 6 May 2025 10:59:38 +0200 Subject: [PATCH] missions carrousse --- README-MINIO-TROUBLESHOOTING.md | 115 ++++++++++++++++++++++++++ components/missions/file-upload.tsx | 120 ++++++++++++++++++++++------ scripts/test-minio-upload.js | 0 3 files changed, 211 insertions(+), 24 deletions(-) create mode 100644 README-MINIO-TROUBLESHOOTING.md mode change 100644 => 100755 scripts/test-minio-upload.js diff --git a/README-MINIO-TROUBLESHOOTING.md b/README-MINIO-TROUBLESHOOTING.md new file mode 100644 index 00000000..a21ad38b --- /dev/null +++ b/README-MINIO-TROUBLESHOOTING.md @@ -0,0 +1,115 @@ +# Minio Troubleshooting Guide + +This document outlines the fixes implemented for the mission file upload issues with Minio. + +## Problem Description + +Mission uploads (logo and attachments) were not working correctly: +- Files weren't appearing in Minio despite upload attempts +- Mission logos weren't displaying even though they were uploaded +- Participation field showed "Non spécifié" despite values in the database +- SDG/ODD icons weren't displaying correctly + +## Implemented Fixes + +### 1. Added URL Generation Function + +Added a `getPublicUrl` function in `lib/s3.ts` that properly constructs URLs for files stored in Minio: +```typescript +export function getPublicUrl(filePath: string): string { + if (!filePath) return ''; + if (filePath.startsWith('http')) return filePath; // Already a full URL + + // Remove leading slash if present + const cleanPath = filePath.startsWith('/') ? filePath.substring(1) : filePath; + + // Construct the full URL + const endpoint = S3_CONFIG.endpoint?.replace(/\/$/, ''); // Remove trailing slash if present + const bucket = S3_CONFIG.bucket; + + // Return original path if no endpoint is configured + if (!endpoint) return cleanPath; + + // Construct and return the full URL + return `${endpoint}/${bucket}/${cleanPath}`; +} +``` + +### 2. Updated Mission Display Page + +Modified `app/missions/page.tsx` to use the `getPublicUrl` function when displaying mission logos: +```tsx +{mission.logo ? ( + {mission.name} { + // Error handling... + }} + /> +) : null} +``` + +### 3. Enhanced Upload API + +Updated `/app/api/missions/upload/route.ts` to: +- Include additional logging +- Generate and return proper public URLs +- Improve error handling + +### 4. Enhanced Mission Detail API + +Modified `/app/api/missions/[missionId]/route.ts` to include public URLs in the response: +```typescript +const missionWithUrls = { + ...mission, + logoUrl: mission.logo ? getPublicUrl(mission.logo) : null, + attachments: mission.attachments.map((attachment) => ({ + ...attachment, + publicUrl: getPublicUrl(attachment.filePath) + })) +}; +``` + +### 5. Added Testing Tools + +1. Browser Console Utilities: + - `window.testMinioConnection()` - Test Minio connectivity + - `window.getMinioUrl(path)` - Generate a public URL for debugging + +2. Server-side Test Script: + - Created `scripts/test-minio-upload.js` to test uploads from the command line + - Tests uploading, downloading, and URL generation + +## How to Test + +1. **Using the browser console:** + ```javascript + // Test connection and list files + window.testMinioConnection() + + // Generate URL for a specific path + window.getMinioUrl('user-123/missions/456/logo.jpg') + ``` + +2. **Using the server-side script:** + ```bash + node scripts/test-minio-upload.js + ``` + +## Required Environment Variables + +Make sure these are properly set in your environment: +- `MINIO_S3_UPLOAD_BUCKET_URL` - The Minio endpoint URL +- `MINIO_AWS_REGION` - The AWS region (often 'us-east-1' for Minio) +- `MINIO_AWS_S3_UPLOAD_BUCKET_NAME` - The bucket name +- `MINIO_ACCESS_KEY` - Access key for Minio +- `MINIO_SECRET_KEY` - Secret key for Minio + +## Additional Notes + +1. The same Minio bucket is used for both Pages and Missions. +2. Pages functionality is working properly, suggesting the Minio configuration itself is correct. +3. Make sure that the bucket has proper permissions for public read access. +4. The URL paths for SDG/ODD icons were corrected to use `/F SDG Icons 2019 WEB/F-WEB-Goal-XX.png` \ No newline at end of file diff --git a/components/missions/file-upload.tsx b/components/missions/file-upload.tsx index bbaf7c8b..f8c0c900 100644 --- a/components/missions/file-upload.tsx +++ b/components/missions/file-upload.tsx @@ -1,6 +1,6 @@ "use client"; -import React, { useState, useRef } from 'react'; +import React, { useState, useRef, useEffect } from 'react'; import { UploadCloud, X, Check, Loader2 } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { useSession } from 'next-auth/react'; @@ -21,6 +21,15 @@ export function FileUpload({ maxSize = 5 * 1024 * 1024, // 5MB acceptedFileTypes = type === 'logo' ? 'image/*' : '.pdf,.doc,.docx,.xls,.xlsx,.jpg,.jpeg,.png' }: FileUploadProps) { + // Log props on init + console.log('FileUpload component initialized with props:', { + type, + missionId, + hasMissionId: !!missionId, + maxSize, + acceptedFileTypes + }); + const { data: session } = useSession(); const [isDragging, setIsDragging] = useState(false); const [isUploading, setIsUploading] = useState(false); @@ -28,6 +37,28 @@ export function FileUpload({ const [file, setFile] = useState(null); const [error, setError] = useState(null); const fileInputRef = useRef(null); + const isMounted = useRef(true); + + // Cleanup on unmount + useEffect(() => { + return () => { + isMounted.current = false; + }; + }, []); + + // Log when session changes + useEffect(() => { + console.log('Session state in FileUpload:', { + isSessionLoaded: !!session, + hasUser: !!session?.user, + userId: session?.user?.id + }); + }, [session]); + + // Log when missionId changes + useEffect(() => { + console.log('MissionId in FileUpload component:', missionId); + }, [missionId]); // Handle drag events const handleDragOver = (e: React.DragEvent) => { @@ -92,11 +123,41 @@ export function FileUpload({ }; const handleUpload = async () => { - if (!file || !session?.user?.id || !missionId) { - console.error('Upload failed: Missing required data', { - fileExists: !!file, - userIdExists: !!session?.user?.id, - missionIdExists: !!missionId + // Log all values to see what's missing + console.log('Debug upload parameters:', { + file, + sessionUser: session?.user, + userId: session?.user?.id, + missionId + }); + + // Check for missing parameters and provide specific feedback + if (!file) { + console.error('Upload failed: No file selected'); + toast({ + title: 'Upload failed', + description: 'No file selected. Please select a file first.', + variant: 'destructive', + }); + return; + } + + if (!session?.user?.id) { + console.error('Upload failed: No user session'); + toast({ + title: 'Authentication required', + description: 'You need to be logged in to upload files. Please log in and try again.', + variant: 'destructive', + }); + return; + } + + if (!missionId) { + console.error('Upload failed: Missing mission ID'); + toast({ + title: 'Upload failed', + description: 'Missing mission ID. Please refresh the page and try again.', + variant: 'destructive', }); return; } @@ -142,28 +203,39 @@ export function FileUpload({ const result = await response.json(); console.log('Upload successful, result:', result); - setProgress(100); - // Reset file after successful upload - setTimeout(() => { - setFile(null); - setIsUploading(false); - setProgress(0); + // Only update state if the component is still mounted + if (isMounted.current) { + setProgress(100); - // Call the callback if provided - if (onUploadComplete) { - onUploadComplete(result); - } - - toast({ - title: 'File uploaded successfully', - description: type === 'logo' ? 'Logo has been updated' : `${file.name} has been added to attachments`, - variant: 'default', - }); - }, 1000); + // Reset file after successful upload + setTimeout(() => { + if (isMounted.current) { + setFile(null); + setIsUploading(false); + setProgress(0); + + // Call the callback if provided + if (onUploadComplete) { + onUploadComplete(result); + } + } + + toast({ + title: 'File uploaded successfully', + description: type === 'logo' ? 'Logo has been updated' : `${file.name} has been added to attachments`, + variant: 'default', + }); + }, 1000); + } } catch (error) { console.error('Upload error details:', error); - setIsUploading(false); + + // Only update state if the component is still mounted + if (isMounted.current) { + setIsUploading(false); + } + toast({ title: 'Upload failed', description: error instanceof Error ? error.message : 'An error occurred during upload', diff --git a/scripts/test-minio-upload.js b/scripts/test-minio-upload.js old mode 100644 new mode 100755