Fix gallery login errors and photo upload issues

- Fix photo upload cross-device link error
  - Changed fs.rename to fs.copyFile + fs.unlink to handle Docker volume mounts
  - This fixes EXDEV errors when uploading photos from /tmp to storage

- Improve gallery login error handling
  - Add console logging for debugging
  - Prevent form refresh with stopPropagation
  - Show specific error messages based on status codes
  - Keep password field populated on error for retry
  - Map 404 to gallery not found message

- Enhanced upload error messages
  - Show specific error message for each failed file
  - Display backend error messages in toast notifications

These fixes resolve:
1. Photo uploads failing silently with cross-device link errors
2. Login errors not displaying and form refreshing
3. Upload errors not showing user-friendly messages

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-07-09 09:09:34 +02:00
parent 9c1e79b5a5
commit 8231f2b60d
3 changed files with 21 additions and 5 deletions
+3 -1
View File
@@ -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);
@@ -78,9 +78,13 @@ export const UserPhotoUpload: React.FC<UserPhotoUploadProps> = ({
},
});
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}`);
}
}
+13 -3
View File
@@ -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);
}