Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cfaee103b6 | |||
| c0e346992d | |||
| 04f45a16c9 | |||
| efad1da74d |
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "picpeak-backend",
|
||||
"version": "1.0.30",
|
||||
"version": "1.0.32",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "picpeak-backend",
|
||||
"version": "1.0.30",
|
||||
"version": "1.0.32",
|
||||
"dependencies": {
|
||||
"adm-zip": "^0.5.16",
|
||||
"archiver": "^5.3.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "picpeak-backend",
|
||||
"version": "1.0.30",
|
||||
"version": "1.0.32",
|
||||
"description": "Backend for PicPeak event photo sharing platform",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
|
||||
+6
-3
@@ -146,14 +146,17 @@ const setCorsHeaders = (req, res, next) => {
|
||||
// Import secure static middleware
|
||||
const secureStatic = require('./src/middleware/secureStatic');
|
||||
|
||||
// Get storage path from environment or use default
|
||||
const storagePath = process.env.STORAGE_PATH || path.join(__dirname, '../storage');
|
||||
|
||||
// Static file serving for photos (protected)
|
||||
app.use('/photos', require('./src/middleware/photoAuth'), setCorsHeaders, secureStatic(path.join(__dirname, 'storage/events/active')));
|
||||
app.use('/photos', require('./src/middleware/photoAuth'), setCorsHeaders, secureStatic(path.join(storagePath, 'events/active')));
|
||||
|
||||
// Static file serving for thumbnails (protected)
|
||||
app.use('/thumbnails', require('./src/middleware/photoAuth'), setCorsHeaders, secureStatic(path.join(__dirname, 'storage/thumbnails')));
|
||||
app.use('/thumbnails', require('./src/middleware/photoAuth'), setCorsHeaders, secureStatic(path.join(storagePath, 'thumbnails')));
|
||||
|
||||
// Static file serving for uploads (public - logos, favicons)
|
||||
app.use('/uploads', setCorsHeaders, secureStatic(path.join(__dirname, 'storage/uploads')));
|
||||
app.use('/uploads', setCorsHeaders, secureStatic(path.join(storagePath, 'uploads')));
|
||||
|
||||
// Health check endpoint
|
||||
app.get('/health', async (req, res) => {
|
||||
|
||||
@@ -8,6 +8,8 @@ async function photoAuth(req, res, next) {
|
||||
// Extract event slug from the path
|
||||
let eventSlug;
|
||||
|
||||
console.log('PhotoAuth middleware - path:', req.path);
|
||||
|
||||
// For thumbnails, we need to parse the filename to get the event info
|
||||
if (req.path.startsWith('/thumb_')) {
|
||||
// For now, we'll rely on JWT token for thumbnail access
|
||||
@@ -26,9 +28,22 @@ async function photoAuth(req, res, next) {
|
||||
|
||||
// Check if it's a gallery token
|
||||
if (decoded.type === 'gallery') {
|
||||
// For thumbnails, we accept any valid gallery token
|
||||
// For thumbnails, we need to verify the token is for a valid event
|
||||
if (!eventSlug) {
|
||||
const event = await db('events').where({ slug: decoded.eventSlug, is_active: formatBoolean(true) }).first();
|
||||
// Extract event ID from the decoded token
|
||||
if (decoded.eventId) {
|
||||
const event = await db('events')
|
||||
.where({ id: decoded.eventId, is_active: formatBoolean(true) })
|
||||
.first();
|
||||
if (event) {
|
||||
req.event = event;
|
||||
return next();
|
||||
}
|
||||
}
|
||||
// Fallback to slug
|
||||
const event = await db('events')
|
||||
.where({ slug: decoded.eventSlug, is_active: formatBoolean(true) })
|
||||
.first();
|
||||
if (event) {
|
||||
req.event = event;
|
||||
return next();
|
||||
@@ -36,7 +51,9 @@ async function photoAuth(req, res, next) {
|
||||
}
|
||||
// For regular photos, check if token matches the event
|
||||
else if (decoded.eventSlug === eventSlug) {
|
||||
const event = await db('events').where({ slug: eventSlug, is_active: formatBoolean(true) }).first();
|
||||
const event = await db('events')
|
||||
.where({ slug: eventSlug, is_active: formatBoolean(true) })
|
||||
.first();
|
||||
if (event) {
|
||||
req.event = event;
|
||||
return next();
|
||||
@@ -46,18 +63,12 @@ async function photoAuth(req, res, next) {
|
||||
|
||||
// Check if it's an admin token (admins can view all photos)
|
||||
if (decoded.type === 'admin') {
|
||||
if (!eventSlug) {
|
||||
// For thumbnails with admin token, allow access
|
||||
return next();
|
||||
}
|
||||
const event = await db('events').where({ slug: eventSlug }).first();
|
||||
if (event) {
|
||||
req.event = event;
|
||||
return next();
|
||||
}
|
||||
// For both thumbnails and photos with admin token, allow access
|
||||
return next();
|
||||
}
|
||||
} catch (err) {
|
||||
// Token invalid, fall through to password check
|
||||
console.error('JWT verification failed:', err.message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,8 +79,8 @@ async function photoAuth(req, res, next) {
|
||||
return res.status(401).json({ error: 'Authentication required' });
|
||||
}
|
||||
|
||||
// If no eventSlug (thumbnails), we require JWT token
|
||||
if (!eventSlug) {
|
||||
// If no eventSlug (thumbnails), and we don't have valid auth yet, deny access
|
||||
if (!eventSlug && !password) {
|
||||
return res.status(401).json({ error: 'Authentication required for thumbnails' });
|
||||
}
|
||||
|
||||
|
||||
@@ -552,8 +552,8 @@ router.get('/:eventId/photos', adminAuth, async (req, res) => {
|
||||
photos: photos.map(photo => ({
|
||||
id: photo.id,
|
||||
filename: photo.filename,
|
||||
url: `/api/admin/events/${eventId}/photo/${photo.id}`,
|
||||
thumbnail_url: photo.thumbnail_path ? `/api/admin/events/${eventId}/thumbnail/${photo.id}` : null,
|
||||
url: `/admin/events/${eventId}/photo/${photo.id}`,
|
||||
thumbnail_url: photo.thumbnail_path ? `/admin/events/${eventId}/thumbnail/${photo.id}` : null,
|
||||
type: photo.type,
|
||||
category_id: photo.category_id,
|
||||
category_name: photo.category_name,
|
||||
@@ -646,4 +646,24 @@ router.get('/:eventId/thumbnail/:photoId', adminAuth, async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Debug endpoint to check photo existence
|
||||
router.get('/:eventId/debug', adminAuth, async (req, res) => {
|
||||
try {
|
||||
const { eventId } = req.params;
|
||||
|
||||
const event = await db('events').where({ id: eventId }).first();
|
||||
const photoCount = await db('photos').where({ event_id: eventId }).count('id as count').first();
|
||||
const photos = await db('photos').where({ event_id: eventId }).limit(5);
|
||||
|
||||
res.json({
|
||||
event: event || 'Not found',
|
||||
photoCount: photoCount.count,
|
||||
samplePhotos: photos,
|
||||
storagePath: getStoragePath()
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -157,7 +157,7 @@ router.get('/:slug/photos', verifyGalleryAccess, async (req, res) => {
|
||||
id: photo.id,
|
||||
filename: photo.filename,
|
||||
url: `/photos/${photo.path}`,
|
||||
thumbnail_url: photo.thumbnail_path ? `/thumbnails/${path.basename(photo.thumbnail_path)}` : null,
|
||||
thumbnail_url: photo.thumbnail_path ? `/${photo.thumbnail_path}` : null,
|
||||
type: photo.type,
|
||||
category_id: photo.category_id,
|
||||
category_name: photo.category_name,
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "picpeak-frontend",
|
||||
"version": "1.0.30",
|
||||
"version": "1.0.32",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "picpeak-frontend",
|
||||
"version": "1.0.30",
|
||||
"version": "1.0.32",
|
||||
"dependencies": {
|
||||
"@tanstack/react-query": "^5.0.0",
|
||||
"@tiptap/extension-link": "^2.25.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "picpeak-frontend",
|
||||
"private": true,
|
||||
"version": "1.0.30",
|
||||
"version": "1.0.32",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
Reference in New Issue
Block a user