diff --git a/backend/src/services/photoProcessor.js b/backend/src/services/photoProcessor.js index 0af62e5..5861d57 100644 --- a/backend/src/services/photoProcessor.js +++ b/backend/src/services/photoProcessor.js @@ -63,7 +63,9 @@ async function processUploadedPhotos(files, eventId, uploadedBy = 'admin', categ await fs.mkdir(destPath, { recursive: true }); const newPath = path.join(destPath, newFilename); - await fs.rename(file.path, newPath); + // Use copyFile and unlink instead of rename to avoid cross-device issues + await fs.copyFile(file.path, newPath); + await fs.unlink(file.path); // Generate thumbnail const thumbnailPath = await generateThumbnail(newPath); diff --git a/frontend/src/components/gallery/UserPhotoUpload.tsx b/frontend/src/components/gallery/UserPhotoUpload.tsx index bfb93b9..133efcf 100644 --- a/frontend/src/components/gallery/UserPhotoUpload.tsx +++ b/frontend/src/components/gallery/UserPhotoUpload.tsx @@ -78,9 +78,13 @@ export const UserPhotoUpload: React.FC = ({ }, }); successCount++; - } catch (error) { + } catch (error: any) { console.error(`Failed to upload ${file.name}:`, error); failedCount++; + + // Show specific error message + const errorMessage = error.response?.data?.error || error.message || 'Upload failed'; + toast.error(`${file.name}: ${errorMessage}`); } } diff --git a/frontend/src/pages/GalleryPage.tsx b/frontend/src/pages/GalleryPage.tsx index ddfeb34..470c19d 100644 --- a/frontend/src/pages/GalleryPage.tsx +++ b/frontend/src/pages/GalleryPage.tsx @@ -50,6 +50,8 @@ export const GalleryPage: React.FC = () => { const handleLogin = async (e: React.FormEvent) => { e.preventDefault(); + e.stopPropagation(); // Prevent any bubbling + if (!password.trim()) { setLoginError(t('auth.pleaseEnterPassword')); return; @@ -66,13 +68,17 @@ export const GalleryPage: React.FC = () => { success: true }); } catch (error: any) { + console.error('Login error:', error); const errorMessage = error.response?.data?.error || 'Invalid password'; + const statusCode = error.response?.status; // Map backend error messages to user-friendly translations - if (errorMessage.toLowerCase().includes('invalid password')) { + if (statusCode === 401 || errorMessage.toLowerCase().includes('invalid password')) { setLoginError(t('auth.wrongPassword')); - } else if (error.response?.status === 429 || errorMessage.toLowerCase().includes('too many')) { + } else if (statusCode === 429 || errorMessage.toLowerCase().includes('too many')) { setLoginError(t('auth.tooManyAttempts')); + } else if (statusCode === 404) { + setLoginError(t('errors.galleryNotFound')); } else { setLoginError(t('auth.invalidPassword')); } @@ -80,8 +86,12 @@ export const GalleryPage: React.FC = () => { // Track failed password entry analyticsService.trackGalleryEvent('password_entry', { gallery: slug, - success: false + success: false, + statusCode }); + + // Keep the password field to allow retry + // Do not clear the password } finally { setIsLoggingIn(false); }