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:
@@ -63,7 +63,9 @@ async function processUploadedPhotos(files, eventId, uploadedBy = 'admin', categ
|
|||||||
await fs.mkdir(destPath, { recursive: true });
|
await fs.mkdir(destPath, { recursive: true });
|
||||||
|
|
||||||
const newPath = path.join(destPath, newFilename);
|
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
|
// Generate thumbnail
|
||||||
const thumbnailPath = await generateThumbnail(newPath);
|
const thumbnailPath = await generateThumbnail(newPath);
|
||||||
|
|||||||
@@ -78,9 +78,13 @@ export const UserPhotoUpload: React.FC<UserPhotoUploadProps> = ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
successCount++;
|
successCount++;
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
console.error(`Failed to upload ${file.name}:`, error);
|
console.error(`Failed to upload ${file.name}:`, error);
|
||||||
failedCount++;
|
failedCount++;
|
||||||
|
|
||||||
|
// Show specific error message
|
||||||
|
const errorMessage = error.response?.data?.error || error.message || 'Upload failed';
|
||||||
|
toast.error(`${file.name}: ${errorMessage}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ export const GalleryPage: React.FC = () => {
|
|||||||
|
|
||||||
const handleLogin = async (e: React.FormEvent) => {
|
const handleLogin = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
e.stopPropagation(); // Prevent any bubbling
|
||||||
|
|
||||||
if (!password.trim()) {
|
if (!password.trim()) {
|
||||||
setLoginError(t('auth.pleaseEnterPassword'));
|
setLoginError(t('auth.pleaseEnterPassword'));
|
||||||
return;
|
return;
|
||||||
@@ -66,13 +68,17 @@ export const GalleryPage: React.FC = () => {
|
|||||||
success: true
|
success: true
|
||||||
});
|
});
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
console.error('Login error:', error);
|
||||||
const errorMessage = error.response?.data?.error || 'Invalid password';
|
const errorMessage = error.response?.data?.error || 'Invalid password';
|
||||||
|
const statusCode = error.response?.status;
|
||||||
|
|
||||||
// Map backend error messages to user-friendly translations
|
// 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'));
|
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'));
|
setLoginError(t('auth.tooManyAttempts'));
|
||||||
|
} else if (statusCode === 404) {
|
||||||
|
setLoginError(t('errors.galleryNotFound'));
|
||||||
} else {
|
} else {
|
||||||
setLoginError(t('auth.invalidPassword'));
|
setLoginError(t('auth.invalidPassword'));
|
||||||
}
|
}
|
||||||
@@ -80,8 +86,12 @@ export const GalleryPage: React.FC = () => {
|
|||||||
// Track failed password entry
|
// Track failed password entry
|
||||||
analyticsService.trackGalleryEvent('password_entry', {
|
analyticsService.trackGalleryEvent('password_entry', {
|
||||||
gallery: slug,
|
gallery: slug,
|
||||||
success: false
|
success: false,
|
||||||
|
statusCode
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Keep the password field to allow retry
|
||||||
|
// Do not clear the password
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoggingIn(false);
|
setIsLoggingIn(false);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user