Fix multiple production issues and add password change functionality

- Fixed frontend API URL configuration to use correct port 3002
- Fixed create event functionality by adding proper endpoint and fixing JSON parsing
- Fixed email settings save functionality by importing logActivity correctly
- Fixed admin settings save functionality by using api client instead of direct fetch
- Implemented password change functionality with modal and backend endpoint
- Added updated_at column to admin_users table
- Fixed all mock data issues - now using real backend data throughout

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-07-07 10:25:38 +02:00
parent f38a8ef598
commit 225d017718
16 changed files with 495 additions and 82 deletions
+133 -33
View File
@@ -1,8 +1,120 @@
const express = require('express');
const { body, query, validationResult } = require('express-validator');
const { db } = require('../database/db');
const { db, logActivity } = require('../database/db');
const { adminAuth } = require('../middleware/auth');
const router = express.Router();
const bcrypt = require('bcrypt');
const crypto = require('crypto');
const fs = require('fs').promises;
const path = require('path');
// Create new event
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').isLength({ min: 6 }),
body('expiration_days').isInt({ min: 1, max: 365 }).optional(),
body('welcome_message').optional().trim(),
body('color_theme').optional().trim()
], async (req, res) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const {
event_type,
event_name,
event_date,
host_email,
admin_email,
password,
welcome_message = '',
color_theme = 'default',
expiration_days = 30
} = req.body;
// 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
const password_hash = await bcrypt.hash(password, 10);
// Calculate expiration date
const expires_at = new Date();
expires_at.setDate(expires_at.getDate() + expiration_days);
// Create folder structure
const eventPath = path.join(__dirname, '../../../storage/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 [eventId] = await db('events').insert({
slug,
event_type,
event_name,
event_date,
host_email,
admin_email,
password_hash,
welcome_message,
color_theme,
share_link: shareLink,
expires_at,
created_at: new Date(),
updated_at: new Date()
});
// Log activity
await logActivity('event_created',
{ event_type, expires_at },
eventId,
{ type: 'admin', id: req.user.id, name: req.user.username }
);
// Queue creation email
await db('email_queue').insert({
event_id: eventId,
recipient_email: host_email,
email_type: 'creation',
email_data: JSON.stringify({
event_name,
share_link: shareLink,
password,
expires_at: expires_at.toISOString()
}),
created_at: new Date()
});
res.json({
id: eventId,
slug,
event_name,
event_type,
share_link: shareLink,
expires_at
});
} catch (error) {
console.error('Error creating event:', error);
res.status(500).json({ error: 'Failed to create event' });
}
});
// Get all events with pagination and filters
router.get('/', adminAuth, async (req, res) => {
@@ -164,15 +276,11 @@ router.put('/:id', adminAuth, [
});
// Log activity
await db.logActivity({
type: 'event_updated',
actorType: 'admin',
actorId: req.user.id,
actorName: req.user.username,
eventId: id,
eventName: event.event_name,
metadata: { changes: Object.keys(updates) }
});
await logActivity('event_updated',
{ changes: Object.keys(updates), eventName: event.event_name },
id,
{ type: 'admin', id: req.user.id, name: req.user.username }
);
res.json({ message: 'Event updated successfully' });
} catch (error) {
@@ -199,13 +307,11 @@ router.delete('/:id', adminAuth, async (req, res) => {
await db('events').where('id', id).del();
// Log activity
await db.logActivity({
type: 'event_deleted',
actorType: 'admin',
actorId: req.user.id,
actorName: req.user.username,
metadata: { event_name: event.event_name }
});
await logActivity('event_deleted',
{ event_name: event.event_name },
null,
{ type: 'admin', id: req.user.id, name: req.user.username }
);
res.json({ message: 'Event deleted successfully' });
} catch (error) {
@@ -233,14 +339,11 @@ router.post('/:id/toggle-status', adminAuth, async (req, res) => {
});
// Log activity
await db.logActivity({
type: newStatus ? 'event_activated' : 'event_deactivated',
actorType: 'admin',
actorId: req.user.id,
actorName: req.user.username,
eventId: id,
eventName: event.event_name
});
await logActivity(newStatus ? 'event_activated' : 'event_deactivated',
{ eventName: event.event_name },
id,
{ type: 'admin', id: req.user.id, name: req.user.username }
);
res.json({
message: `Event ${newStatus ? 'activated' : 'deactivated'} successfully`,
@@ -276,14 +379,11 @@ router.post('/:id/archive', adminAuth, async (req, res) => {
});
// Log activity
await db.logActivity({
type: 'event_archived',
actorType: 'admin',
actorId: req.user.id,
actorName: req.user.username,
eventId: id,
eventName: event.event_name
});
await logActivity('event_archived',
{ eventName: event.event_name },
id,
{ type: 'admin', id: req.user.id, name: req.user.username }
);
res.json({ message: 'Event archived successfully' });
} catch (error) {