diff --git a/backend/src/middleware/gallery.js b/backend/src/middleware/gallery.js index 99482cb..1852629 100644 --- a/backend/src/middleware/gallery.js +++ b/backend/src/middleware/gallery.js @@ -11,7 +11,13 @@ async function verifyGalleryAccess(req, res, next) { } const decoded = jwt.verify(token, process.env.JWT_SECRET); - const event = await db('events').where({ id: decoded.eventId, is_active: formatBoolean(true) }).first(); + const event = await db('events') + .where({ + id: decoded.eventId, + is_active: formatBoolean(true), + is_archived: formatBoolean(false) + }) + .first(); if (!event) { return res.status(404).json({ error: 'Gallery not found or expired' }); diff --git a/backend/src/routes/gallery.js b/backend/src/routes/gallery.js index 450824c..cda8014 100644 --- a/backend/src/routes/gallery.js +++ b/backend/src/routes/gallery.js @@ -6,35 +6,11 @@ const archiver = require('archiver'); const path = require('path'); const router = express.Router(); const watermarkService = require('../services/watermarkService'); +const { verifyGalleryAccess } = require('../middleware/gallery'); // Get storage path from environment or default const getStoragePath = () => process.env.STORAGE_PATH || path.join(__dirname, '../../../storage'); -// Middleware to verify gallery access -async function verifyGalleryAccess(req, res, next) { - try { - const token = req.headers.authorization?.split(' ')[1]; - if (!token) { - return res.status(401).json({ error: 'No token provided' }); - } - - const decoded = jwt.verify(token, process.env.JWT_SECRET); - const event = await db('events') - .where({ id: decoded.eventId, is_active: formatBoolean(true), is_archived: formatBoolean(false) }) - .first(); - - if (!event) { - return res.status(404).json({ error: 'Gallery not found or expired' }); - } - - req.event = event; - next(); - } catch (error) { - console.error('Error verifying gallery access:', error); - res.status(401).json({ error: 'Invalid token', details: error.message }); - } -} - // Verify share token router.get('/:slug/verify-token/:token', async (req, res) => { try { diff --git a/frontend/src/components/gallery/GalleryView.tsx b/frontend/src/components/gallery/GalleryView.tsx index 5607695..8a783fe 100644 --- a/frontend/src/components/gallery/GalleryView.tsx +++ b/frontend/src/components/gallery/GalleryView.tsx @@ -52,7 +52,7 @@ export const GalleryView: React.FC = ({ slug, event }) => { const { watermarkEnabled } = useWatermarkSettings(); // Fetch photos - const { data, isLoading, error } = useGalleryPhotos(slug); + const { data, isLoading, error, refetch } = useGalleryPhotos(slug); // Debug logging useEffect(() => { @@ -294,11 +294,20 @@ export const GalleryView: React.FC = ({ slug, event }) => { } if (error || !data) { + // Check if it's an authentication error (401) + const is401Error = (error as any)?.response?.status === 401; + + if (is401Error) { + // Authentication failed - logout and let the parent component handle re-authentication + logout(); + return null; + } + return (

{t('gallery.failedToLoad')}

-
diff --git a/frontend/src/config/api.ts b/frontend/src/config/api.ts index eb69dda..39572af 100644 --- a/frontend/src/config/api.ts +++ b/frontend/src/config/api.ts @@ -32,14 +32,24 @@ api.interceptors.request.use( config.headers.Authorization = `Bearer ${token}`; } } else { - // For gallery routes, get the slug from the URL path - const pathParts = window.location.pathname.split('/'); - if (pathParts[1] === 'gallery' && pathParts[2]) { - const gallerySlug = pathParts[2]; + // For gallery routes, try to extract slug from the request URL first + const galleryMatch = config.url?.match(/\/gallery\/([^\/]+)/); + if (galleryMatch && galleryMatch[1]) { + const gallerySlug = galleryMatch[1]; const token = localStorage.getItem(`gallery_token_${gallerySlug}`); if (token) { config.headers.Authorization = `Bearer ${token}`; } + } else { + // Fallback to getting slug from the current page URL + const pathParts = window.location.pathname.split('/'); + if (pathParts[1] === 'gallery' && pathParts[2]) { + const gallerySlug = pathParts[2]; + const token = localStorage.getItem(`gallery_token_${gallerySlug}`); + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } + } } } @@ -73,21 +83,34 @@ api.interceptors.response.use( } if (error.response?.status === 401) { - // Redirect to appropriate login + // Check if it's an admin route const isAdminRoute = error.config?.url?.includes('/admin'); + const currentPath = window.location.pathname; + if (isAdminRoute) { // Clear admin token on unauthorized Cookies.remove(ADMIN_TOKEN_KEY); - window.location.href = '/admin/login'; + // Only redirect if we're not already on the admin login page + if (!currentPath.includes('/admin/login')) { + window.location.href = '/admin/login'; + } } else { - // For gallery routes, clear gallery-specific token and redirect - const currentPath = window.location.pathname; - const pathParts = currentPath.split('/'); - if (pathParts[1] === 'gallery' && pathParts[2]) { - const gallerySlug = pathParts[2]; - localStorage.removeItem(`gallery_token_${gallerySlug}`); - localStorage.removeItem(`gallery_event_${gallerySlug}`); - window.location.href = `/gallery/${gallerySlug}`; + // For gallery routes, check if the error is from a gallery API call + const galleryMatch = error.config?.url?.match(/\/gallery\/([^\/]+)/); + + // Don't redirect if we're on any gallery page (to avoid redirect loops during login) + if (currentPath.startsWith('/gallery/')) { + // If we have a gallery match from the API URL, clear that specific gallery's token + if (galleryMatch && galleryMatch[1]) { + const gallerySlug = galleryMatch[1]; + localStorage.removeItem(`gallery_token_${gallerySlug}`); + localStorage.removeItem(`gallery_event_${gallerySlug}`); + } + // Don't redirect - let the component handle the auth state + } else { + // We're not on a gallery page but got a 401 from a gallery API + // This shouldn't happen in normal flow, but if it does, redirect to homepage + window.location.href = '/'; } } } diff --git a/frontend/src/hooks/useGallery.ts b/frontend/src/hooks/useGallery.ts index 498855f..f84f6d3 100644 --- a/frontend/src/hooks/useGallery.ts +++ b/frontend/src/hooks/useGallery.ts @@ -18,6 +18,8 @@ export const useGalleryPhotos = (slug: string, enabled: boolean = true) => { enabled, retry: 1, staleTime: 5 * 60 * 1000, // 5 minutes + // Add a small delay to ensure auth token is properly set + retryDelay: 100, }); };