Merge pull request #218 from the-luap/fix/optional-email-event-creation
fix: respect optional email settings in event creation
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
exports.up = async function(knex) {
|
||||
await knex.schema.alterTable('events', (table) => {
|
||||
table.string('host_email', 255).nullable().alter();
|
||||
table.string('admin_email', 255).nullable().alter();
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = async function(knex) {
|
||||
await knex.schema.alterTable('events', (table) => {
|
||||
table.string('host_email', 255).notNullable().defaultTo('').alter();
|
||||
table.string('admin_email', 255).notNullable().defaultTo('').alter();
|
||||
});
|
||||
};
|
||||
@@ -162,10 +162,10 @@ router.post('/', adminAuth, requirePermission('events.create'), [
|
||||
return true;
|
||||
}),
|
||||
body('event_name').notEmpty().trim(),
|
||||
body('event_date').optional().isDate(),
|
||||
body('event_date').optional({ values: 'falsy' }).isDate(),
|
||||
body('customer_name').optional().trim(),
|
||||
body('customer_email').optional().isEmail().normalizeEmail(),
|
||||
body('admin_email').optional().isEmail().normalizeEmail(),
|
||||
body('customer_email').optional({ values: 'falsy' }).isEmail().normalizeEmail(),
|
||||
body('admin_email').optional({ values: 'falsy' }).isEmail().normalizeEmail(),
|
||||
body('require_password').optional().isBoolean(),
|
||||
body('password').optional().isString().custom((value, { req }) => {
|
||||
const input = req.body.require_password;
|
||||
@@ -392,9 +392,9 @@ router.post('/', adminAuth, requirePermission('events.create'), [
|
||||
event_name,
|
||||
event_date: event_date || null,
|
||||
...(customerColumnsAvailable ? { customer_name: customerName, customer_email: customerEmail } : {}),
|
||||
host_name: customerName,
|
||||
host_email: customerEmail,
|
||||
admin_email,
|
||||
host_name: customerName || null,
|
||||
host_email: customerEmail || null,
|
||||
admin_email: admin_email || null,
|
||||
password_hash,
|
||||
welcome_message,
|
||||
color_theme,
|
||||
@@ -446,28 +446,30 @@ router.post('/', adminAuth, requirePermission('events.create'), [
|
||||
{ type: 'admin', id: req.admin.id, name: req.admin.username }
|
||||
);
|
||||
|
||||
// Queue creation email
|
||||
// Queue creation email (only if there is a recipient)
|
||||
// Language detection is handled by email processor
|
||||
|
||||
await db('email_queue').insert({
|
||||
event_id: eventId,
|
||||
recipient_email: customerEmail,
|
||||
email_type: 'gallery_created',
|
||||
email_data: JSON.stringify({
|
||||
customer_name: customerName,
|
||||
customer_email: customerEmail,
|
||||
host_name: customerName || (customerEmail ? customerEmail.split('@')[0] : null),
|
||||
event_name,
|
||||
event_date: event_date, // Pass raw date - will be formatted by email processor
|
||||
gallery_link: shareUrl,
|
||||
gallery_password: requirePassword ? password : 'No password required',
|
||||
expiry_date: expires_at ? expires_at.toISOString() : null, // Pass ISO string - will be formatted by email processor
|
||||
welcome_message: welcome_message || ''
|
||||
}),
|
||||
status: 'pending',
|
||||
created_at: new Date()
|
||||
// scheduled_at will use default value
|
||||
});
|
||||
|
||||
if (customerEmail) {
|
||||
await db('email_queue').insert({
|
||||
event_id: eventId,
|
||||
recipient_email: customerEmail,
|
||||
email_type: 'gallery_created',
|
||||
email_data: JSON.stringify({
|
||||
customer_name: customerName,
|
||||
customer_email: customerEmail,
|
||||
host_name: customerName || (customerEmail ? customerEmail.split('@')[0] : null),
|
||||
event_name,
|
||||
event_date: event_date, // Pass raw date - will be formatted by email processor
|
||||
gallery_link: shareUrl,
|
||||
gallery_password: requirePassword ? password : 'No password required',
|
||||
expiry_date: expires_at ? expires_at.toISOString() : null, // Pass ISO string - will be formatted by email processor
|
||||
welcome_message: welcome_message || ''
|
||||
}),
|
||||
status: 'pending',
|
||||
created_at: new Date()
|
||||
// scheduled_at will use default value
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
id: eventId,
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
import { test, expect, Page } from '@playwright/test';
|
||||
|
||||
const ADMIN_EMAIL = process.env.ADMIN_EMAIL || 'admin@example.com';
|
||||
const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD || 'Admin!234';
|
||||
|
||||
async function getAdminToken(page: Page): Promise<string> {
|
||||
const res = await page.request.post('/api/auth/admin/login', {
|
||||
data: { username: ADMIN_EMAIL, password: ADMIN_PASSWORD },
|
||||
});
|
||||
expect(res.ok()).toBeTruthy();
|
||||
const body = await res.json();
|
||||
expect(body.token).toBeTruthy();
|
||||
return body.token;
|
||||
}
|
||||
|
||||
async function updateEventSettings(
|
||||
page: Page,
|
||||
token: string,
|
||||
settings: Record<string, boolean>
|
||||
) {
|
||||
const res = await page.request.put('/api/admin/settings/general', {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
data: settings,
|
||||
});
|
||||
expect(res.ok()).toBeTruthy();
|
||||
}
|
||||
|
||||
test.describe('Optional email fields in event creation (#217)', () => {
|
||||
test('event creation succeeds with empty emails when set to optional', async ({ page }) => {
|
||||
const token = await getAdminToken(page);
|
||||
|
||||
// Disable email requirements
|
||||
await updateEventSettings(page, token, {
|
||||
event_require_customer_email: false,
|
||||
event_require_admin_email: false,
|
||||
});
|
||||
|
||||
try {
|
||||
// Create event with empty email fields
|
||||
const eventRes = await page.request.post('/api/admin/events', {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
data: {
|
||||
event_type: 'wedding',
|
||||
event_name: `E2E Optional Emails ${Date.now()}`,
|
||||
event_date: new Date(Date.now() + 7 * 86400000).toISOString().slice(0, 10),
|
||||
customer_name: 'Test Host',
|
||||
customer_email: '',
|
||||
admin_email: '',
|
||||
password: 'TestPass123!',
|
||||
expiration_days: 30,
|
||||
},
|
||||
});
|
||||
|
||||
const body = await eventRes.json();
|
||||
expect(eventRes.ok(), `Expected 200 but got ${eventRes.status()}: ${JSON.stringify(body)}`).toBeTruthy();
|
||||
expect(body.id).toBeTruthy();
|
||||
|
||||
// Cleanup: delete the created event
|
||||
await page.request.delete(`/api/admin/events/${body.id}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
} finally {
|
||||
// Revert settings to required
|
||||
await updateEventSettings(page, token, {
|
||||
event_require_customer_email: true,
|
||||
event_require_admin_email: true,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test('event creation still fails with empty emails when set to required', async ({ page }) => {
|
||||
const token = await getAdminToken(page);
|
||||
|
||||
// Ensure email requirements are enabled
|
||||
await updateEventSettings(page, token, {
|
||||
event_require_customer_email: true,
|
||||
event_require_admin_email: true,
|
||||
});
|
||||
|
||||
const eventRes = await page.request.post('/api/admin/events', {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
data: {
|
||||
event_type: 'wedding',
|
||||
event_name: `E2E Required Emails ${Date.now()}`,
|
||||
event_date: new Date(Date.now() + 7 * 86400000).toISOString().slice(0, 10),
|
||||
customer_name: 'Test Host',
|
||||
customer_email: '',
|
||||
admin_email: '',
|
||||
password: 'TestPass123!',
|
||||
expiration_days: 30,
|
||||
},
|
||||
});
|
||||
|
||||
expect(eventRes.status()).toBe(400);
|
||||
const body = await eventRes.json();
|
||||
const paths = body.errors.map((e: { path: string }) => e.path);
|
||||
expect(paths).toContain('customer_email');
|
||||
expect(paths).toContain('admin_email');
|
||||
});
|
||||
|
||||
test('event creation succeeds with missing email fields when optional', async ({ page }) => {
|
||||
const token = await getAdminToken(page);
|
||||
|
||||
// Disable email requirements
|
||||
await updateEventSettings(page, token, {
|
||||
event_require_customer_email: false,
|
||||
event_require_admin_email: false,
|
||||
});
|
||||
|
||||
try {
|
||||
// Create event without email fields at all (undefined, not empty string)
|
||||
const eventRes = await page.request.post('/api/admin/events', {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
data: {
|
||||
event_type: 'wedding',
|
||||
event_name: `E2E Missing Emails ${Date.now()}`,
|
||||
event_date: new Date(Date.now() + 7 * 86400000).toISOString().slice(0, 10),
|
||||
customer_name: 'Test Host',
|
||||
password: 'TestPass123!',
|
||||
expiration_days: 30,
|
||||
},
|
||||
});
|
||||
|
||||
const body = await eventRes.json();
|
||||
expect(eventRes.ok(), `Expected 200 but got ${eventRes.status()}: ${JSON.stringify(body)}`).toBeTruthy();
|
||||
expect(body.id).toBeTruthy();
|
||||
|
||||
// Cleanup
|
||||
await page.request.delete(`/api/admin/events/${body.id}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
} finally {
|
||||
await updateEventSettings(page, token, {
|
||||
event_require_customer_email: true,
|
||||
event_require_admin_email: true,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user