Add gallery routes
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
const express = require('express');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const { db } = require('../database/db');
|
||||
const archiver = require('archiver');
|
||||
const path = require('path');
|
||||
const router = express.Router();
|
||||
|
||||
// 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: true }).first();
|
||||
|
||||
if (!event) {
|
||||
return res.status(404).json({ error: 'Gallery not found or expired' });
|
||||
}
|
||||
|
||||
req.event = event;
|
||||
next();
|
||||
} catch (error) {
|
||||
res.status(401).json({ error: 'Invalid token' });
|
||||
}
|
||||
}
|
||||
|
||||
// Get gallery info
|
||||
router.get('/:slug/info', async (req, res) => {
|
||||
try {
|
||||
const { slug } = req.params;
|
||||
|
||||
const event = await db('events')
|
||||
.where({ slug })
|
||||
.select('event_name', 'event_type', 'event_date', 'expires_at', 'is_active')
|
||||
.first();
|
||||
|
||||
if (!event) {
|
||||
return res.status(404).json({ error: 'Gallery not found' });
|
||||
}
|
||||
|
||||
res.json({
|
||||
...event,
|
||||
is_expired: !event.is_active || new Date(event.expires_at) < new Date()
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to fetch gallery info' });
|
||||
}
|
||||
});
|
||||
|
||||
// Get all photos
|
||||
router.get('/:slug/photos', verifyGalleryAccess, async (req, res) => {
|
||||
try {
|
||||
const photos = await db('photos')
|
||||
.where('event_id', req.event.id)
|
||||
.orderBy('uploaded_at', 'desc');
|
||||
|
||||
// Log view
|
||||
await db('access_logs').insert({
|
||||
event_id: req.event.id,
|
||||
ip_address: req.ip,
|
||||
user_agent: req.headers['user-agent'],
|
||||
action: 'view'
|
||||
});
|
||||
|
||||
res.json({
|
||||
event: {
|
||||
id: req.event.id,
|
||||
event_name: req.event.event_name,
|
||||
event_type: req.event.event_type,
|
||||
event_date: req.event.event_date,
|
||||
welcome_message: req.event.welcome_message,
|
||||
color_theme: req.event.color_theme,
|
||||
expires_at: req.event.expires_at
|
||||
},
|
||||
photos: photos.map(photo => ({
|
||||
id: photo.id,
|
||||
filename: photo.filename,
|
||||
url: `/photos/${req.event.slug}/${photo.path}`,
|
||||
thumbnail_url: photo.thumbnail_path ? `/photos/${photo.thumbnail_path}` : null,
|
||||
type: photo.type,
|
||||
size: photo.size_bytes,
|
||||
uploaded_at: photo.uploaded_at
|
||||
}))
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to fetch photos' });
|
||||
}
|
||||
});
|
||||
|
||||
// Download single photo
|
||||
router.get('/:slug/download/:photoId', verifyGalleryAccess, async (req, res) => {
|
||||
try {
|
||||
const { photoId } = req.params;
|
||||
|
||||
const photo = await db('photos')
|
||||
.where({ id: photoId, event_id: req.event.id })
|
||||
.first();
|
||||
|
||||
if (!photo) {
|
||||
return res.status(404).json({ error: 'Photo not found' });
|
||||
}
|
||||
|
||||
// Update download count
|
||||
await db('photos').where('id', photoId).increment('download_count', 1);
|
||||
|
||||
// Log download
|
||||
await db('access_logs').insert({
|
||||
event_id: req.event.id,
|
||||
ip_address: req.ip,
|
||||
user_agent: req.headers['user-agent'],
|
||||
action: 'download',
|
||||
photo_id: photoId
|
||||
});
|
||||
|
||||
const filePath = path.join(__dirname, '../../../storage/events/active', req.event.slug, photo.path);
|
||||
res.download(filePath, photo.filename);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to download photo' });
|
||||
}
|
||||
});
|
||||
|
||||
// Download all photos as ZIP
|
||||
router.get('/:slug/download-all', verifyGalleryAccess, async (req, res) => {
|
||||
try {
|
||||
const photos = await db('photos').where('event_id', req.event.id);
|
||||
|
||||
if (photos.length === 0) {
|
||||
return res.status(404).json({ error: 'No photos found' });
|
||||
}
|
||||
|
||||
res.setHeader('Content-Type', 'application/zip');
|
||||
res.setHeader('Content-Disposition', `attachment; filename="${req.event.slug}.zip"`);
|
||||
|
||||
const archive = archiver('zip', { zlib: { level: 5 } });
|
||||
archive.on('error', (err) => {
|
||||
throw err;
|
||||
});
|
||||
|
||||
archive.pipe(res);
|
||||
|
||||
// Add photos to archive
|
||||
for (const photo of photos) {
|
||||
const filePath = path.join(__dirname, '../../../storage/events/active', req.event.slug, photo.path);
|
||||
archive.file(filePath, { name: photo.path });
|
||||
}
|
||||
|
||||
await archive.finalize();
|
||||
|
||||
// Log bulk download
|
||||
await db('access_logs').insert({
|
||||
event_id: req.event.id,
|
||||
ip_address: req.ip,
|
||||
user_agent: req.headers['user-agent'],
|
||||
action: 'download_all'
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to create download archive' });
|
||||
}
|
||||
});
|
||||
|
||||
// Get photo stats
|
||||
router.get('/:slug/stats', verifyGalleryAccess, async (req, res) => {
|
||||
try {
|
||||
const totalPhotos = await db('photos')
|
||||
.where('event_id', req.event.id)
|
||||
.count('id as count')
|
||||
.first();
|
||||
|
||||
const totalViews = await db('access_logs')
|
||||
.where('event_id', req.event.id)
|
||||
.where('action', 'view')
|
||||
.count('id as count')
|
||||
.first();
|
||||
|
||||
const totalDownloads = await db('photos')
|
||||
.where('event_id', req.event.id)
|
||||
.sum('download_count as total')
|
||||
.first();
|
||||
|
||||
const uniqueVisitors = await db('access_logs')
|
||||
.where('event_id', req.event.id)
|
||||
.countDistinct('ip_address as count')
|
||||
.first();
|
||||
|
||||
res.json({
|
||||
total_photos: totalPhotos.count,
|
||||
total_views: totalViews.count,
|
||||
total_downloads: totalDownloads.total || 0,
|
||||
unique_visitors: uniqueVisitors.count
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to fetch stats' });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
Reference in New Issue
Block a user