f053f42b6d
Mirror to GitHub / mirror (push) Successful in 19s
Test and Lint / backend-test (push) Successful in 1m8s
Test and Lint / frontend-test (push) Successful in 2m11s
Version and Release / version-bump (push) Successful in 34s
Version and Release / trigger-drone (push) Successful in 3s
continuous-integration/drone/push Build is passing
Critical fixes for database compatibility issues:
INSERT operations:
- Fix all INSERT queries to use .returning('id')
- Handle both PostgreSQL (returns objects) and SQLite (returns IDs)
- Fixed in: events.js, adminArchives.js, adminEvents-enhanced.js, create-test-event.js
Date operations:
- Replace SQLite-specific db.raw("datetime('now', '+30 days')")
- Use JavaScript Date objects for cross-database compatibility
- Fixed in: adminArchives.js
Database utilities:
- Add dbCompat.js utility for handling database differences
- Provides consistent API for inserts, dates, booleans, and DB-specific operations
- Centralized database compatibility logic
Migration:
- Add migration 023 documenting PostgreSQL compatibility requirements
- Ensures future developers are aware of compatibility needs
This resolves all 'not iterable' errors and ensures the application
works correctly with both PostgreSQL (production) and SQLite (development).
124 lines
4.2 KiB
JavaScript
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 = 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' });
|
|
}
|
|
}); |