Fix general settings route and req.user references
- Update frontend settings service to use correct /api/admin/settings/general route - Fix all req.user to req.admin references in adminSettings.js - Ensures settings can be saved without authentication errors
This commit is contained in:
@@ -7,6 +7,7 @@ const archiveRoutes = require('./adminArchives');
|
||||
const emailRoutes = require('./adminEmail');
|
||||
const settingsRoutes = require('./adminSettings');
|
||||
const eventsRoutes = require('./adminEvents');
|
||||
const photosRoutes = require('./adminPhotos');
|
||||
|
||||
// Mount sub-routers
|
||||
router.use('/dashboard', dashboardRoutes);
|
||||
@@ -14,5 +15,6 @@ router.use('/archives', archiveRoutes);
|
||||
router.use('/email', emailRoutes);
|
||||
router.use('/settings', settingsRoutes);
|
||||
router.use('/events', eventsRoutes);
|
||||
router.use('/events', photosRoutes);
|
||||
|
||||
module.exports = router;
|
||||
@@ -86,7 +86,7 @@ router.post('/config', [
|
||||
await logActivity('email_config_updated',
|
||||
{ smtp_host, from_email },
|
||||
null,
|
||||
{ type: 'admin', id: req.user.id, name: req.user.username }
|
||||
{ type: 'admin', id: req.admin.id, name: req.admin.username }
|
||||
);
|
||||
|
||||
res.json({ message: 'Email configuration updated successfully' });
|
||||
@@ -224,8 +224,8 @@ router.put('/templates/:key', [
|
||||
await db('activity_logs').insert({
|
||||
activity_type: 'email_template_updated',
|
||||
actor_type: 'admin',
|
||||
actor_id: req.user.id,
|
||||
actor_name: req.user.username,
|
||||
actor_id: req.admin.id,
|
||||
actor_name: req.admin.username,
|
||||
metadata: JSON.stringify({ template_key: req.params.key })
|
||||
});
|
||||
|
||||
|
||||
@@ -284,7 +284,7 @@ router.put('/:id', adminAuth, [
|
||||
await logActivity('event_updated',
|
||||
{ changes: Object.keys(updates), eventName: event.event_name },
|
||||
id,
|
||||
{ type: 'admin', id: req.user.id, name: req.user.username }
|
||||
{ type: 'admin', id: req.admin.id, name: req.admin.username }
|
||||
);
|
||||
|
||||
res.json({ message: 'Event updated successfully' });
|
||||
@@ -315,7 +315,7 @@ router.delete('/:id', adminAuth, async (req, res) => {
|
||||
await logActivity('event_deleted',
|
||||
{ event_name: event.event_name },
|
||||
null,
|
||||
{ type: 'admin', id: req.user.id, name: req.user.username }
|
||||
{ type: 'admin', id: req.admin.id, name: req.admin.username }
|
||||
);
|
||||
|
||||
res.json({ message: 'Event deleted successfully' });
|
||||
@@ -347,7 +347,7 @@ router.post('/:id/toggle-status', adminAuth, async (req, res) => {
|
||||
await logActivity(newStatus ? 'event_activated' : 'event_deactivated',
|
||||
{ eventName: event.event_name },
|
||||
id,
|
||||
{ type: 'admin', id: req.user.id, name: req.user.username }
|
||||
{ type: 'admin', id: req.admin.id, name: req.admin.username }
|
||||
);
|
||||
|
||||
res.json({
|
||||
@@ -387,7 +387,7 @@ router.post('/:id/archive', adminAuth, async (req, res) => {
|
||||
await logActivity('event_archived',
|
||||
{ eventName: event.event_name },
|
||||
id,
|
||||
{ type: 'admin', id: req.user.id, name: req.user.username }
|
||||
{ type: 'admin', id: req.admin.id, name: req.admin.username }
|
||||
);
|
||||
|
||||
res.json({ message: 'Event archived successfully' });
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
const express = require('express');
|
||||
const multer = require('multer');
|
||||
const path = require('path');
|
||||
const fs = require('fs').promises;
|
||||
const { db, logActivity } = require('../database/db');
|
||||
const { adminAuth } = require('../middleware/auth');
|
||||
const { generateThumbnail } = require('../services/imageProcessor');
|
||||
const router = express.Router();
|
||||
|
||||
// Get storage path from environment or default
|
||||
const getStoragePath = () => process.env.STORAGE_PATH || path.join(__dirname, '../../../storage');
|
||||
|
||||
// Configure multer for file uploads
|
||||
const storage = multer.diskStorage({
|
||||
destination: async (req, file, cb) => {
|
||||
const { eventId } = req.params;
|
||||
const { type = 'individual' } = req.body;
|
||||
|
||||
try {
|
||||
// Get event details
|
||||
const event = await db('events').where({ id: eventId }).first();
|
||||
if (!event) {
|
||||
return cb(new Error('Event not found'));
|
||||
}
|
||||
|
||||
// Create destination path
|
||||
const photoType = type === 'collage' ? 'collages' : 'individual';
|
||||
const destPath = path.join(getStoragePath(), 'events/active', event.slug, photoType);
|
||||
|
||||
// Ensure directory exists
|
||||
await fs.mkdir(destPath, { recursive: true });
|
||||
|
||||
cb(null, destPath);
|
||||
} catch (error) {
|
||||
cb(error);
|
||||
}
|
||||
},
|
||||
filename: (req, file, cb) => {
|
||||
// Generate unique filename
|
||||
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
|
||||
const ext = path.extname(file.originalname);
|
||||
const name = path.basename(file.originalname, ext);
|
||||
cb(null, `${name}-${uniqueSuffix}${ext}`);
|
||||
}
|
||||
});
|
||||
|
||||
const upload = multer({
|
||||
storage: storage,
|
||||
limits: {
|
||||
fileSize: 50 * 1024 * 1024, // 50MB limit
|
||||
},
|
||||
fileFilter: (req, file, cb) => {
|
||||
// Accept images only
|
||||
const allowedTypes = /jpeg|jpg|png|webp/;
|
||||
const extname = allowedTypes.test(path.extname(file.originalname).toLowerCase());
|
||||
const mimetype = allowedTypes.test(file.mimetype);
|
||||
|
||||
if (mimetype && extname) {
|
||||
return cb(null, true);
|
||||
} else {
|
||||
cb(new Error('Only JPEG, PNG and WebP images are allowed'));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Upload photos for an event
|
||||
router.post('/:eventId/upload', adminAuth, upload.array('photos', 20), async (req, res) => {
|
||||
try {
|
||||
const { eventId } = req.params;
|
||||
const { type = 'individual' } = req.body;
|
||||
|
||||
// Verify event exists and admin has access
|
||||
const event = await db('events').where({ id: eventId }).first();
|
||||
if (!event) {
|
||||
return res.status(404).json({ error: 'Event not found' });
|
||||
}
|
||||
|
||||
if (!req.files || req.files.length === 0) {
|
||||
return res.status(400).json({ error: 'No files uploaded' });
|
||||
}
|
||||
|
||||
const uploadedPhotos = [];
|
||||
|
||||
// Process each uploaded file
|
||||
for (const file of req.files) {
|
||||
try {
|
||||
// Generate thumbnail
|
||||
const thumbnailPath = await generateThumbnail(file.path);
|
||||
|
||||
// Calculate relative paths
|
||||
const storagePath = getStoragePath();
|
||||
const relativePath = path.relative(path.join(storagePath, 'events/active'), file.path);
|
||||
const relativeThumbPath = thumbnailPath ? path.relative(path.join(storagePath, 'events/active'), thumbnailPath) : null;
|
||||
|
||||
// Add to database
|
||||
const [photoId] = await db('photos').insert({
|
||||
event_id: eventId,
|
||||
filename: file.filename,
|
||||
path: relativePath,
|
||||
thumbnail_path: relativeThumbPath,
|
||||
type: type === 'collage' ? 'collage' : 'individual',
|
||||
size_bytes: file.size
|
||||
});
|
||||
|
||||
uploadedPhotos.push({
|
||||
id: photoId,
|
||||
filename: file.filename,
|
||||
size: file.size,
|
||||
type
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`Error processing file ${file.filename}:`, error);
|
||||
// Continue with other files
|
||||
}
|
||||
}
|
||||
|
||||
// Log activity
|
||||
await logActivity('photos_uploaded',
|
||||
{ count: uploadedPhotos.length, eventName: event.event_name },
|
||||
eventId,
|
||||
{ type: 'admin', id: req.admin.id, name: req.admin.username }
|
||||
);
|
||||
|
||||
res.json({
|
||||
message: `Successfully uploaded ${uploadedPhotos.length} photos`,
|
||||
photos: uploadedPhotos
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error uploading photos:', error);
|
||||
res.status(500).json({ error: 'Failed to upload photos' });
|
||||
}
|
||||
});
|
||||
|
||||
// Delete a photo
|
||||
router.delete('/:eventId/photos/:photoId', adminAuth, async (req, res) => {
|
||||
try {
|
||||
const { eventId, photoId } = req.params;
|
||||
|
||||
// Get photo details
|
||||
const photo = await db('photos')
|
||||
.where({ id: photoId, event_id: eventId })
|
||||
.first();
|
||||
|
||||
if (!photo) {
|
||||
return res.status(404).json({ error: 'Photo not found' });
|
||||
}
|
||||
|
||||
// Delete physical files
|
||||
const storagePath = getStoragePath();
|
||||
const photoPath = path.join(storagePath, 'events/active', photo.path);
|
||||
|
||||
try {
|
||||
await fs.unlink(photoPath);
|
||||
} catch (error) {
|
||||
console.error('Error deleting photo file:', error);
|
||||
}
|
||||
|
||||
// Delete thumbnail if exists
|
||||
if (photo.thumbnail_path) {
|
||||
const thumbPath = path.join(storagePath, 'events/active', photo.thumbnail_path);
|
||||
try {
|
||||
await fs.unlink(thumbPath);
|
||||
} catch (error) {
|
||||
console.error('Error deleting thumbnail:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove from database
|
||||
await db('photos').where({ id: photoId }).delete();
|
||||
|
||||
// Log activity
|
||||
const event = await db('events').where({ id: eventId }).first();
|
||||
await logActivity('photo_deleted',
|
||||
{ filename: photo.filename, eventName: event.event_name },
|
||||
eventId,
|
||||
{ type: 'admin', id: req.admin.id, name: req.admin.username }
|
||||
);
|
||||
|
||||
res.json({ message: 'Photo deleted successfully' });
|
||||
} catch (error) {
|
||||
console.error('Error deleting photo:', error);
|
||||
res.status(500).json({ error: 'Failed to delete photo' });
|
||||
}
|
||||
});
|
||||
|
||||
// Get all photos for an event
|
||||
router.get('/:eventId/photos', adminAuth, async (req, res) => {
|
||||
try {
|
||||
const { eventId } = req.params;
|
||||
const { type } = req.query;
|
||||
|
||||
let query = db('photos').where({ event_id: eventId });
|
||||
|
||||
if (type) {
|
||||
query = query.where({ type });
|
||||
}
|
||||
|
||||
const photos = await query.orderBy('uploaded_at', 'desc');
|
||||
|
||||
res.json({
|
||||
photos: photos.map(photo => ({
|
||||
id: photo.id,
|
||||
filename: photo.filename,
|
||||
url: `/photos/${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) {
|
||||
console.error('Error fetching photos:', error);
|
||||
res.status(500).json({ error: 'Failed to fetch photos' });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -118,8 +118,8 @@ router.put('/branding', adminAuth, async (req, res) => {
|
||||
await db('activity_logs').insert({
|
||||
activity_type: 'branding_updated',
|
||||
actor_type: 'admin',
|
||||
actor_id: req.user.id,
|
||||
actor_name: req.user.username,
|
||||
actor_id: req.admin.id,
|
||||
actor_name: req.admin.username,
|
||||
metadata: JSON.stringify({ company_name })
|
||||
});
|
||||
|
||||
@@ -215,8 +215,8 @@ router.put('/theme', adminAuth, async (req, res) => {
|
||||
await db('activity_logs').insert({
|
||||
activity_type: 'theme_updated',
|
||||
actor_type: 'admin',
|
||||
actor_id: req.user.id,
|
||||
actor_name: req.user.username,
|
||||
actor_id: req.admin.id,
|
||||
actor_name: req.admin.username,
|
||||
metadata: JSON.stringify({ theme_name: themeSettings.name || 'custom' })
|
||||
});
|
||||
|
||||
@@ -252,8 +252,8 @@ router.put('/general', adminAuth, async (req, res) => {
|
||||
await db('activity_logs').insert({
|
||||
activity_type: 'general_settings_updated',
|
||||
actor_type: 'admin',
|
||||
actor_id: req.user.id,
|
||||
actor_name: req.user.username,
|
||||
actor_id: req.admin.id,
|
||||
actor_name: req.admin.username,
|
||||
metadata: JSON.stringify({ settings_count: Object.keys(settings).length })
|
||||
});
|
||||
|
||||
@@ -289,8 +289,8 @@ router.put('/security', adminAuth, async (req, res) => {
|
||||
await db('activity_logs').insert({
|
||||
activity_type: 'security_settings_updated',
|
||||
actor_type: 'admin',
|
||||
actor_id: req.user.id,
|
||||
actor_name: req.user.username,
|
||||
actor_id: req.admin.id,
|
||||
actor_name: req.admin.username,
|
||||
metadata: JSON.stringify({ settings_count: Object.keys(settings).length })
|
||||
});
|
||||
|
||||
|
||||
@@ -30,23 +30,63 @@ async function verifyGalleryAccess(req, res, next) {
|
||||
}
|
||||
}
|
||||
|
||||
// Get gallery info
|
||||
router.get('/:slug/info', async (req, res) => {
|
||||
// Verify share token
|
||||
router.get('/:slug/verify-token/:token', async (req, res) => {
|
||||
try {
|
||||
const { slug } = req.params;
|
||||
const { slug, token } = req.params;
|
||||
|
||||
const event = await db('events')
|
||||
.where({ slug })
|
||||
.select('event_name', 'event_type', 'event_date', 'expires_at', 'is_active')
|
||||
.where({ slug, is_active: true })
|
||||
.select('id', 'share_link')
|
||||
.first();
|
||||
|
||||
if (!event) {
|
||||
return res.status(404).json({ error: 'Gallery not found' });
|
||||
}
|
||||
|
||||
// Extract token from share link and verify
|
||||
const expectedToken = event.share_link.split('/').pop();
|
||||
if (token !== expectedToken) {
|
||||
return res.status(404).json({ error: 'Invalid gallery link' });
|
||||
}
|
||||
|
||||
res.json({ valid: true });
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to verify token' });
|
||||
}
|
||||
});
|
||||
|
||||
// Get gallery info (with optional token verification)
|
||||
router.get('/:slug/info', async (req, res) => {
|
||||
try {
|
||||
const { slug } = req.params;
|
||||
const { token } = req.query;
|
||||
|
||||
const event = await db('events')
|
||||
.where({ slug })
|
||||
.select('event_name', 'event_type', 'event_date', 'expires_at', 'is_active', 'share_link')
|
||||
.first();
|
||||
|
||||
if (!event) {
|
||||
return res.status(404).json({ error: 'Gallery not found' });
|
||||
}
|
||||
|
||||
// If token provided, verify it matches the share link
|
||||
if (token) {
|
||||
const expectedToken = event.share_link.split('/').pop();
|
||||
if (token !== expectedToken) {
|
||||
return res.status(404).json({ error: 'Invalid gallery link' });
|
||||
}
|
||||
}
|
||||
|
||||
res.json({
|
||||
...event,
|
||||
is_expired: !event.is_active || new Date(event.expires_at) < new Date()
|
||||
event_name: event.event_name,
|
||||
event_type: event.event_type,
|
||||
event_date: event.event_date,
|
||||
expires_at: event.expires_at,
|
||||
is_active: event.is_active,
|
||||
is_expired: !event.is_active || new Date(event.expires_at) < new Date(),
|
||||
requires_password: true
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to fetch gallery info' });
|
||||
|
||||
Reference in New Issue
Block a user