fix: handle email templates schema variations in production
Test and Lint / backend-test (push) Successful in 1m11s
continuous-integration/drone/push Build is passing
Test and Lint / frontend-test (push) Successful in 2m20s
Version and Release / version-bump (push) Successful in 34s
Version and Release / trigger-drone (push) Successful in 3s
Test and Lint / backend-test (push) Successful in 1m11s
continuous-integration/drone/push Build is passing
Test and Lint / frontend-test (push) Successful in 2m20s
Version and Release / version-bump (push) Successful in 34s
Version and Release / trigger-drone (push) Successful in 3s
- Update migration to detect and handle both old and new email template schemas - Fix migration to insert into correct columns based on existing schema - Update adminEmail routes to handle both schema formats gracefully - Add proper fallbacks for German language columns This ensures the application works whether the language migration has been applied or not, preventing null constraint violations. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -3,11 +3,20 @@ exports.up = async function(knex) {
|
|||||||
const templates = await knex('email_templates').select('template_key');
|
const templates = await knex('email_templates').select('template_key');
|
||||||
const existingKeys = templates.map(t => t.template_key);
|
const existingKeys = templates.map(t => t.template_key);
|
||||||
|
|
||||||
|
// Check which columns exist in the table
|
||||||
|
const hasSubjectEn = await knex.schema.hasColumn('email_templates', 'subject_en');
|
||||||
|
const hasSubject = await knex.schema.hasColumn('email_templates', 'subject');
|
||||||
|
|
||||||
|
// Determine which columns to use based on schema
|
||||||
|
const subjectCol = hasSubjectEn ? 'subject_en' : 'subject';
|
||||||
|
const bodyHtmlCol = hasSubjectEn ? 'body_html_en' : 'body_html';
|
||||||
|
const bodyTextCol = hasSubjectEn ? 'body_text_en' : 'body_text';
|
||||||
|
|
||||||
const defaultTemplates = [
|
const defaultTemplates = [
|
||||||
{
|
{
|
||||||
template_key: 'gallery_created',
|
template_key: 'gallery_created',
|
||||||
subject: 'Your Photo Gallery is Ready!',
|
[subjectCol]: 'Your Photo Gallery is Ready!',
|
||||||
body_html: `<h2>Gallery Created Successfully</h2>
|
[bodyHtmlCol]: `<h2>Gallery Created Successfully</h2>
|
||||||
<p>Dear {{host_name}},</p>
|
<p>Dear {{host_name}},</p>
|
||||||
<p>Your photo gallery "{{event_name}}" has been created successfully!</p>
|
<p>Your photo gallery "{{event_name}}" has been created successfully!</p>
|
||||||
<p><strong>Gallery Details:</strong></p>
|
<p><strong>Gallery Details:</strong></p>
|
||||||
@@ -18,39 +27,39 @@ exports.up = async function(knex) {
|
|||||||
<li>Expires: {{expiry_date}}</li>
|
<li>Expires: {{expiry_date}}</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Share this link and password with your guests to allow them to view and download photos.</p>`,
|
<p>Share this link and password with your guests to allow them to view and download photos.</p>`,
|
||||||
body_text: 'Gallery Created Successfully\n\nDear {{host_name}},\n\nYour photo gallery "{{event_name}}" has been created successfully!',
|
[bodyTextCol]: 'Gallery Created Successfully\n\nDear {{host_name}},\n\nYour photo gallery "{{event_name}}" has been created successfully!',
|
||||||
variables: JSON.stringify(['host_name', 'event_name', 'event_date', 'gallery_link', 'gallery_password', 'expiry_date'])
|
variables: JSON.stringify(['host_name', 'event_name', 'event_date', 'gallery_link', 'gallery_password', 'expiry_date'])
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
template_key: 'expiration_warning',
|
template_key: 'expiration_warning',
|
||||||
subject: 'Your Photo Gallery Expires Soon',
|
[subjectCol]: 'Your Photo Gallery Expires Soon',
|
||||||
body_html: `<h2>Gallery Expiring Soon</h2>
|
[bodyHtmlCol]: `<h2>Gallery Expiring Soon</h2>
|
||||||
<p>Dear {{host_name}},</p>
|
<p>Dear {{host_name}},</p>
|
||||||
<p>Your photo gallery "{{event_name}}" will expire in {{days_remaining}} days.</p>
|
<p>Your photo gallery "{{event_name}}" will expire in {{days_remaining}} days.</p>
|
||||||
<p>After expiration, the gallery will be archived and no longer accessible to guests.</p>
|
<p>After expiration, the gallery will be archived and no longer accessible to guests.</p>
|
||||||
<p><a href="{{gallery_link}}">Visit Gallery</a></p>`,
|
<p><a href="{{gallery_link}}">Visit Gallery</a></p>`,
|
||||||
body_text: 'Gallery Expiring Soon\n\nDear {{host_name}},\n\nYour photo gallery "{{event_name}}" will expire in {{days_remaining}} days.',
|
[bodyTextCol]: 'Gallery Expiring Soon\n\nDear {{host_name}},\n\nYour photo gallery "{{event_name}}" will expire in {{days_remaining}} days.',
|
||||||
variables: JSON.stringify(['host_name', 'event_name', 'days_remaining', 'gallery_link'])
|
variables: JSON.stringify(['host_name', 'event_name', 'days_remaining', 'gallery_link'])
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
template_key: 'gallery_expired',
|
template_key: 'gallery_expired',
|
||||||
subject: 'Your Photo Gallery Has Expired',
|
[subjectCol]: 'Your Photo Gallery Has Expired',
|
||||||
body_html: `<h2>Gallery Expired</h2>
|
[bodyHtmlCol]: `<h2>Gallery Expired</h2>
|
||||||
<p>Dear {{host_name}},</p>
|
<p>Dear {{host_name}},</p>
|
||||||
<p>Your photo gallery "{{event_name}}" has expired and been archived.</p>
|
<p>Your photo gallery "{{event_name}}" has expired and been archived.</p>
|
||||||
<p>The photos are safely stored in our archive system. If you need access to the archived photos, please contact support.</p>`,
|
<p>The photos are safely stored in our archive system. If you need access to the archived photos, please contact support.</p>`,
|
||||||
body_text: 'Gallery Expired\n\nDear {{host_name}},\n\nYour photo gallery "{{event_name}}" has expired and been archived.',
|
[bodyTextCol]: 'Gallery Expired\n\nDear {{host_name}},\n\nYour photo gallery "{{event_name}}" has expired and been archived.',
|
||||||
variables: JSON.stringify(['host_name', 'event_name'])
|
variables: JSON.stringify(['host_name', 'event_name'])
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
template_key: 'archive_complete',
|
template_key: 'archive_complete',
|
||||||
subject: 'Gallery Archive Complete',
|
[subjectCol]: 'Gallery Archive Complete',
|
||||||
body_html: `<h2>Archive Complete</h2>
|
[bodyHtmlCol]: `<h2>Archive Complete</h2>
|
||||||
<p>Dear {{host_name}},</p>
|
<p>Dear {{host_name}},</p>
|
||||||
<p>Your photo gallery "{{event_name}}" has been successfully archived.</p>
|
<p>Your photo gallery "{{event_name}}" has been successfully archived.</p>
|
||||||
<p>Archive size: {{archive_size}}</p>
|
<p>Archive size: {{archive_size}}</p>
|
||||||
<p>The archive is stored securely and can be retrieved if needed.</p>`,
|
<p>The archive is stored securely and can be retrieved if needed.</p>`,
|
||||||
body_text: 'Archive Complete\n\nDear {{host_name}},\n\nYour photo gallery "{{event_name}}" has been successfully archived.',
|
[bodyTextCol]: 'Archive Complete\n\nDear {{host_name}},\n\nYour photo gallery "{{event_name}}" has been successfully archived.',
|
||||||
variables: JSON.stringify(['host_name', 'event_name', 'archive_size'])
|
variables: JSON.stringify(['host_name', 'event_name', 'archive_size'])
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@@ -58,6 +67,20 @@ exports.up = async function(knex) {
|
|||||||
// Insert missing templates
|
// Insert missing templates
|
||||||
for (const template of defaultTemplates) {
|
for (const template of defaultTemplates) {
|
||||||
if (!existingKeys.includes(template.template_key)) {
|
if (!existingKeys.includes(template.template_key)) {
|
||||||
|
// If we have language columns, also set German versions with same content
|
||||||
|
if (hasSubjectEn) {
|
||||||
|
template.subject_de = template[subjectCol];
|
||||||
|
template.body_html_de = template[bodyHtmlCol];
|
||||||
|
template.body_text_de = template[bodyTextCol];
|
||||||
|
|
||||||
|
// Also ensure we have the basic columns if they exist
|
||||||
|
if (hasSubject) {
|
||||||
|
template.subject = template[subjectCol];
|
||||||
|
template.body_html = template[bodyHtmlCol];
|
||||||
|
template.body_text = template[bodyTextCol];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await knex('email_templates').insert(template);
|
await knex('email_templates').insert(template);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -193,20 +193,34 @@ router.get('/templates/:key', adminAuth, async (req, res) => {
|
|||||||
return res.status(404).json({ error: 'Template not found' });
|
return res.status(404).json({ error: 'Template not found' });
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json({
|
// Handle both old and new schema formats
|
||||||
|
const response = {
|
||||||
id: template.id,
|
id: template.id,
|
||||||
template_key: template.template_key,
|
template_key: template.template_key,
|
||||||
// English versions
|
|
||||||
subject_en: template.subject_en || template.subject,
|
|
||||||
body_html_en: template.body_html_en || template.body_html,
|
|
||||||
body_text_en: template.body_text_en || template.body_text,
|
|
||||||
// German versions
|
|
||||||
subject_de: template.subject_de || template.subject_en || template.subject,
|
|
||||||
body_html_de: template.body_html_de || template.body_html_en || template.body_html,
|
|
||||||
body_text_de: template.body_text_de || template.body_text_en || template.body_text,
|
|
||||||
variables: template.variables ? JSON.parse(template.variables) : [],
|
variables: template.variables ? JSON.parse(template.variables) : [],
|
||||||
updated_at: template.updated_at
|
updated_at: template.updated_at
|
||||||
});
|
};
|
||||||
|
|
||||||
|
// Check which columns exist and use them appropriately
|
||||||
|
if (template.subject_en !== undefined) {
|
||||||
|
// New schema with language columns
|
||||||
|
response.subject_en = template.subject_en;
|
||||||
|
response.body_html_en = template.body_html_en;
|
||||||
|
response.body_text_en = template.body_text_en;
|
||||||
|
response.subject_de = template.subject_de;
|
||||||
|
response.body_html_de = template.body_html_de;
|
||||||
|
response.body_text_de = template.body_text_de;
|
||||||
|
} else {
|
||||||
|
// Old schema - use basic columns for both languages
|
||||||
|
response.subject_en = template.subject;
|
||||||
|
response.body_html_en = template.body_html;
|
||||||
|
response.body_text_en = template.body_text;
|
||||||
|
response.subject_de = template.subject;
|
||||||
|
response.body_html_de = template.body_html;
|
||||||
|
response.body_text_de = template.body_text;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json(response);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Email template fetch error:', error);
|
console.error('Email template fetch error:', error);
|
||||||
res.status(500).json({ error: 'Failed to fetch email template' });
|
res.status(500).json({ error: 'Failed to fetch email template' });
|
||||||
@@ -237,7 +251,18 @@ router.put('/templates/:key', [
|
|||||||
updated_at: new Date()
|
updated_at: new Date()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Only update provided fields
|
// Check which columns exist in the database
|
||||||
|
const template = await db('email_templates')
|
||||||
|
.where('template_key', req.params.key)
|
||||||
|
.first();
|
||||||
|
|
||||||
|
if (!template) {
|
||||||
|
return res.status(404).json({ error: 'Template not found' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine schema type and update accordingly
|
||||||
|
if (template.subject_en !== undefined) {
|
||||||
|
// New schema with language columns
|
||||||
if (subject_en !== undefined) updateData.subject_en = subject_en;
|
if (subject_en !== undefined) updateData.subject_en = subject_en;
|
||||||
if (subject_de !== undefined) updateData.subject_de = subject_de;
|
if (subject_de !== undefined) updateData.subject_de = subject_de;
|
||||||
if (body_html_en !== undefined) updateData.body_html_en = body_html_en;
|
if (body_html_en !== undefined) updateData.body_html_en = body_html_en;
|
||||||
@@ -245,6 +270,21 @@ router.put('/templates/:key', [
|
|||||||
if (body_text_en !== undefined) updateData.body_text_en = body_text_en || '';
|
if (body_text_en !== undefined) updateData.body_text_en = body_text_en || '';
|
||||||
if (body_text_de !== undefined) updateData.body_text_de = body_text_de || '';
|
if (body_text_de !== undefined) updateData.body_text_de = body_text_de || '';
|
||||||
|
|
||||||
|
// Also update basic columns if they exist
|
||||||
|
if (template.subject !== undefined) {
|
||||||
|
updateData.subject = subject_en || updateData.subject_en;
|
||||||
|
updateData.body_html = body_html_en || updateData.body_html_en;
|
||||||
|
updateData.body_text = body_text_en || updateData.body_text_en || '';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Old schema - only update basic columns
|
||||||
|
if (subject_en !== undefined) {
|
||||||
|
updateData.subject = subject_en;
|
||||||
|
updateData.body_html = body_html_en;
|
||||||
|
updateData.body_text = body_text_en || '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const updated = await db('email_templates')
|
const updated = await db('email_templates')
|
||||||
.where('template_key', req.params.key)
|
.where('template_key', req.params.key)
|
||||||
.update(updateData);
|
.update(updateData);
|
||||||
|
|||||||
Reference in New Issue
Block a user