Files
picpeak/backend/src/routes/adminEvents-enhanced.js
T
paul ad75818566 fix: resolve multiple feedback management issues
- Hide "Manage Feedback" button when feedback is disabled for an event
- Fix 500 error on feedback API endpoint by adding null-safe operators
- Fix TypeError on analytics page by calculating average_rating in backend
- Fix password validation for event creation by properly awaiting async validation
- Add proper null checks and fallbacks for feedback statistics

These fixes ensure:
- Date passwords like "19.07.2025" work with simple password complexity settings
- Feedback management page loads without errors
- Analytics display correctly even with no feedback data

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-24 16:57:08 +02:00

124 lines
4.2 KiB
JavaScript

// This is a partial file showing the enhanced event creation with password validation
// Only the relevant parts are shown - merge with existing adminEvents.js
const { validatePasswordInContext, getBcryptRounds } = require('../utils/passwordValidation');
// Enhanced event creation with password validation
router.post('/', adminAuth, [
body('event_type').isIn(['wedding', 'birthday', 'corporate', 'other']),
body('event_name').notEmpty().trim(),
body('event_date').isDate(),
body('host_email').isEmail().normalizeEmail(),
body('admin_email').isEmail().normalizeEmail(),
body('password').notEmpty(), // Remove the weak isLength validation
body('expiration_days').isInt({ min: 1, max: 365 }).optional(),
body('welcome_message').optional().trim(),
body('color_theme').optional().trim(),
body('allow_user_uploads').optional().isBoolean().toBoolean(),
body('upload_category_id').optional({ nullable: true, checkFalsy: true }).isInt(),
body('host_name').notEmpty().trim()
], async (req, res) => {
try {
console.log('Create event request body:', req.body);
const errors = validationResult(req);
if (!errors.isEmpty()) {
console.error('Validation errors:', errors.array());
return res.status(400).json({ errors: errors.array() });
}
const {
event_type,
event_name,
event_date,
host_name,
host_email,
admin_email,
password,
welcome_message = '',
color_theme = null,
expiration_days = 30,
allow_user_uploads = false,
upload_category_id = null
} = req.body;
// Validate password strength for gallery
const passwordValidation = await validatePasswordInContext(password, 'gallery', {
eventName: event_name
});
if (!passwordValidation.valid) {
return res.status(400).json({
error: 'Password does not meet security requirements',
details: passwordValidation.errors,
score: passwordValidation.score,
feedback: passwordValidation.feedback
});
}
// Generate unique slug
const baseSlug = `${event_type}-${event_name.toLowerCase().replace(/[^a-z0-9]/g, '-')}-${event_date}`;
let slug = baseSlug;
let counter = 1;
while (await db('events').where({ slug }).first()) {
slug = `${baseSlug}-${counter}`;
counter++;
}
// Generate share link
const shareToken = crypto.randomBytes(16).toString('hex');
const shareLink = `${process.env.FRONTEND_URL}/gallery/${slug}/${shareToken}`;
// Hash password with configurable rounds
const password_hash = await bcrypt.hash(password, getBcryptRounds());
// Calculate expiration date (days after event date)
const expires_at = new Date(event_date);
expires_at.setDate(expires_at.getDate() + parseInt(expiration_days, 10));
// Create folder structure
const storagePath = process.env.STORAGE_PATH || path.join(__dirname, '../../../storage');
const eventPath = path.join(storagePath, 'events/active', slug);
await fs.mkdir(path.join(eventPath, 'collages'), { recursive: true });
await fs.mkdir(path.join(eventPath, 'individual'), { recursive: true });
// Insert into database
const insertResult = await db('events').insert({
slug,
event_type,
event_name,
event_date,
host_name,
host_email,
admin_email,
password_hash,
welcome_message,
color_theme,
share_link: shareLink,
expires_at: expires_at.toISOString(),
created_at: new Date().toISOString(),
allow_user_uploads,
upload_category_id
}).returning('id');
// Handle both PostgreSQL (returns array of objects) and SQLite (returns array of IDs)
const eventId = insertResult[0]?.id || insertResult[0];
// Log activity
await logActivity('event_created',
{
event_type,
expires_at,
password_strength: passwordValidation.score
},
eventId,
{ type: 'admin', id: req.admin.id, name: req.admin.username }
);
// Rest of the implementation remains the same...
// Queue creation email, etc.
} catch (error) {
console.error('Error creating event:', error);
res.status(500).json({ error: 'Failed to create event' });
}
});