missions carrousse
This commit is contained in:
parent
76d4d55285
commit
dddfd5024b
115
README-MINIO-TROUBLESHOOTING.md
Normal file
115
README-MINIO-TROUBLESHOOTING.md
Normal file
@ -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 ? (
|
||||||
|
<img
|
||||||
|
src={mission.logo ? getPublicUrl(mission.logo) : ''}
|
||||||
|
alt={mission.name}
|
||||||
|
className="w-full h-full object-cover rounded-md border border-gray-200"
|
||||||
|
onError={(e) => {
|
||||||
|
// 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`
|
||||||
@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React, { useState, useRef } from 'react';
|
import React, { useState, useRef, useEffect } from 'react';
|
||||||
import { UploadCloud, X, Check, Loader2 } from 'lucide-react';
|
import { UploadCloud, X, Check, Loader2 } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { useSession } from 'next-auth/react';
|
import { useSession } from 'next-auth/react';
|
||||||
@ -21,6 +21,15 @@ export function FileUpload({
|
|||||||
maxSize = 5 * 1024 * 1024, // 5MB
|
maxSize = 5 * 1024 * 1024, // 5MB
|
||||||
acceptedFileTypes = type === 'logo' ? 'image/*' : '.pdf,.doc,.docx,.xls,.xlsx,.jpg,.jpeg,.png'
|
acceptedFileTypes = type === 'logo' ? 'image/*' : '.pdf,.doc,.docx,.xls,.xlsx,.jpg,.jpeg,.png'
|
||||||
}: FileUploadProps) {
|
}: FileUploadProps) {
|
||||||
|
// Log props on init
|
||||||
|
console.log('FileUpload component initialized with props:', {
|
||||||
|
type,
|
||||||
|
missionId,
|
||||||
|
hasMissionId: !!missionId,
|
||||||
|
maxSize,
|
||||||
|
acceptedFileTypes
|
||||||
|
});
|
||||||
|
|
||||||
const { data: session } = useSession();
|
const { data: session } = useSession();
|
||||||
const [isDragging, setIsDragging] = useState(false);
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
const [isUploading, setIsUploading] = useState(false);
|
const [isUploading, setIsUploading] = useState(false);
|
||||||
@ -28,6 +37,28 @@ export function FileUpload({
|
|||||||
const [file, setFile] = useState<File | null>(null);
|
const [file, setFile] = useState<File | null>(null);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
const fileInputRef = useRef<HTMLInputElement>(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
|
// Handle drag events
|
||||||
const handleDragOver = (e: React.DragEvent) => {
|
const handleDragOver = (e: React.DragEvent) => {
|
||||||
@ -92,11 +123,41 @@ export function FileUpload({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleUpload = async () => {
|
const handleUpload = async () => {
|
||||||
if (!file || !session?.user?.id || !missionId) {
|
// Log all values to see what's missing
|
||||||
console.error('Upload failed: Missing required data', {
|
console.log('Debug upload parameters:', {
|
||||||
fileExists: !!file,
|
file,
|
||||||
userIdExists: !!session?.user?.id,
|
sessionUser: session?.user,
|
||||||
missionIdExists: !!missionId
|
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;
|
return;
|
||||||
}
|
}
|
||||||
@ -142,10 +203,14 @@ export function FileUpload({
|
|||||||
|
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
console.log('Upload successful, result:', result);
|
console.log('Upload successful, result:', result);
|
||||||
|
|
||||||
|
// Only update state if the component is still mounted
|
||||||
|
if (isMounted.current) {
|
||||||
setProgress(100);
|
setProgress(100);
|
||||||
|
|
||||||
// Reset file after successful upload
|
// Reset file after successful upload
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
if (isMounted.current) {
|
||||||
setFile(null);
|
setFile(null);
|
||||||
setIsUploading(false);
|
setIsUploading(false);
|
||||||
setProgress(0);
|
setProgress(0);
|
||||||
@ -154,6 +219,7 @@ export function FileUpload({
|
|||||||
if (onUploadComplete) {
|
if (onUploadComplete) {
|
||||||
onUploadComplete(result);
|
onUploadComplete(result);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
title: 'File uploaded successfully',
|
title: 'File uploaded successfully',
|
||||||
@ -161,9 +227,15 @@ export function FileUpload({
|
|||||||
variant: 'default',
|
variant: 'default',
|
||||||
});
|
});
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Upload error details:', error);
|
console.error('Upload error details:', error);
|
||||||
|
|
||||||
|
// Only update state if the component is still mounted
|
||||||
|
if (isMounted.current) {
|
||||||
setIsUploading(false);
|
setIsUploading(false);
|
||||||
|
}
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
title: 'Upload failed',
|
title: 'Upload failed',
|
||||||
description: error instanceof Error ? error.message : 'An error occurred during upload',
|
description: error instanceof Error ? error.message : 'An error occurred during upload',
|
||||||
|
|||||||
0
scripts/test-minio-upload.js
Normal file → Executable file
0
scripts/test-minio-upload.js
Normal file → Executable file
Loading…
Reference in New Issue
Block a user