fix: gallery thumbnails not loading (404 errors) #96

The gallery thumbnail endpoint was returning 404 when thumbnail_path
was null or the file didn't exist, unlike the admin endpoint which
generates thumbnails on demand using ensureThumbnail().

- Import ensureThumbnail from imageProcessor
- Use ensureThumbnail() in gallery thumbnail route to generate
  thumbnails on demand if they don't exist
- This matches the admin endpoint behavior

Fixes #96
This commit is contained in:
Paul Nothaft
2026-01-15 11:22:13 +01:00
parent 0e3b50d1b6
commit e3c3c4c951
+18 -17
View File
@@ -12,6 +12,7 @@ const { resolvePhotoFilePath } = require('../services/photoResolver');
const { getEventShareToken, resolveShareIdentifier, buildShareLinkVariants } = require('../services/shareLinkService'); const { getEventShareToken, resolveShareIdentifier, buildShareLinkVariants } = require('../services/shareLinkService');
const { handleAsync } = require('../utils/routeHelpers'); const { handleAsync } = require('../utils/routeHelpers');
const { NotFoundError } = require('../utils/errors'); const { NotFoundError } = require('../utils/errors');
const { ensureThumbnail } = require('../services/imageProcessor');
// Get storage path from environment or default // Get storage path from environment or default
const getStoragePath = () => process.env.STORAGE_PATH || path.join(__dirname, '../../storage'); const getStoragePath = () => process.env.STORAGE_PATH || path.join(__dirname, '../../storage');
@@ -787,30 +788,30 @@ router.get('/:slug/photo/:photoId',
); );
// Serve thumbnail // Serve thumbnail
router.get('/:slug/thumbnail/:photoId', router.get('/:slug/thumbnail/:photoId',
verifyGalleryAccess, verifyGalleryAccess,
async (req, res) => { async (req, res) => {
try { try {
const { photoId } = req.params; const { photoId } = req.params;
const photo = await db('photos') const photo = await db('photos')
.where({ id: photoId, event_id: req.event.id }) .where({ id: photoId, event_id: req.event.id })
.first(); .first();
if (!photo || !photo.thumbnail_path) { if (!photo) {
return res.status(404).json({ error: 'Thumbnail not found' }); return res.status(404).json({ error: 'Photo not found' });
} }
const thumbPath = path.join(getStoragePath(), photo.thumbnail_path); // Ensure thumbnail exists and is valid, regenerate if needed
const thumbnailPath = await ensureThumbnail(photo);
// Check if file exists
const fs = require('fs').promises; if (!thumbnailPath) {
try { logger.error(`Failed to generate thumbnail for photo ${photoId}`);
await fs.access(thumbPath); return res.status(404).json({ error: 'Thumbnail generation failed' });
} catch (error) {
return res.status(404).json({ error: 'Thumbnail file not found' });
} }
const thumbPath = path.join(getStoragePath(), thumbnailPath);
// Log thumbnail access // Log thumbnail access
await secureImageService.logImageAccess( await secureImageService.logImageAccess(
photoId, photoId,
@@ -818,7 +819,7 @@ router.get('/:slug/thumbnail/:photoId',
req.clientInfo, req.clientInfo,
'thumbnail' 'thumbnail'
); );
// Set appropriate headers with enhanced security // Set appropriate headers with enhanced security
res.set({ res.set({
'Content-Type': 'image/jpeg', 'Content-Type': 'image/jpeg',
@@ -827,7 +828,7 @@ router.get('/:slug/thumbnail/:photoId',
'X-Content-Type-Options': 'nosniff', 'X-Content-Type-Options': 'nosniff',
'X-Protected-Thumbnail': 'true' 'X-Protected-Thumbnail': 'true'
}); });
// Send file // Send file
res.sendFile(path.resolve(thumbPath)); res.sendFile(path.resolve(thumbPath));
} catch (error) { } catch (error) {