From a9c276198644b48b27268c36c50d15d902a6c7b6 Mon Sep 17 00:00:00 2001 From: paul Date: Wed, 16 Jul 2025 16:45:43 +0200 Subject: [PATCH] fix: add language detection from app_settings and fix activity_logs schema - Enhanced language detection priority in email processor: 1. Event-specific language 2. App settings general_default_language (NEW) 3. Email config default_language 4. Domain-based detection - Fixed activity_logs insertion error by using logActivity function - Fixed 500 error on resend email endpoint - Emails now respect language selected in settings page --- backend/scripts/check-email-templates.js | 66 ++++++++++++++ backend/scripts/check-german-templates.js | 53 +++++++++++ backend/scripts/fix-final-german-templates.js | 88 +++++++++++++++++++ backend/scripts/test-german-emails.js | 85 ++++++++++++++++++ backend/src/routes/adminEvents.js | 45 +++++++--- backend/src/services/emailProcessor.js | 16 +++- 6 files changed, 340 insertions(+), 13 deletions(-) create mode 100644 backend/scripts/check-email-templates.js create mode 100644 backend/scripts/check-german-templates.js create mode 100644 backend/scripts/fix-final-german-templates.js create mode 100644 backend/scripts/test-german-emails.js diff --git a/backend/scripts/check-email-templates.js b/backend/scripts/check-email-templates.js new file mode 100644 index 0000000..8f160cc --- /dev/null +++ b/backend/scripts/check-email-templates.js @@ -0,0 +1,66 @@ +const { db } = require('../src/database/db'); + +async function checkEmailTemplates() { + try { + console.log('=== Email Templates Check ===\n'); + + // 1. Check table columns + console.log('1. Checking email_templates table structure...'); + + // Check which columns exist + const columnChecks = [ + 'subject', 'subject_en', 'subject_de', + 'body_html', 'body_html_en', 'body_html_de', + 'body_text', 'body_text_en', 'body_text_de' + ]; + + const existingColumns = []; + for (const col of columnChecks) { + const exists = await db.schema.hasColumn('email_templates', col); + if (exists) existingColumns.push(col); + } + + console.log(' Existing columns:', existingColumns.join(', ')); + + // 2. Get all templates + console.log('\n2. Current email templates:'); + const templates = await db('email_templates').select('*'); + + for (const template of templates) { + console.log(`\n Template: ${template.template_key}`); + console.log(' -------------------'); + + // Check which fields have content + const fields = ['subject', 'subject_en', 'subject_de', + 'body_html', 'body_html_en', 'body_html_de', + 'body_text', 'body_text_en', 'body_text_de']; + + for (const field of fields) { + if (template[field]) { + const preview = template[field].substring(0, 50) + '...'; + console.log(` ${field}: ${preview}`); + } + } + + // Check for German translations + const hasGermanSubject = template.subject_de || template.body_html_de; + console.log(` Has German translation: ${hasGermanSubject ? 'YES' : 'NO'}`); + } + + // 3. Summary + console.log('\n3. Summary:'); + const totalTemplates = templates.length; + const templatesWithGerman = templates.filter(t => t.subject_de || t.body_html_de).length; + console.log(` Total templates: ${totalTemplates}`); + console.log(` Templates with German: ${templatesWithGerman}`); + console.log(` Missing German: ${totalTemplates - templatesWithGerman}`); + + await db.destroy(); + } catch (error) { + console.error('Error:', error); + await db.destroy(); + process.exit(1); + } +} + +checkEmailTemplates(); \ No newline at end of file diff --git a/backend/scripts/check-german-templates.js b/backend/scripts/check-german-templates.js new file mode 100644 index 0000000..7583db8 --- /dev/null +++ b/backend/scripts/check-german-templates.js @@ -0,0 +1,53 @@ +const { db } = require('../src/database/db'); + +async function checkGermanTemplates() { + try { + console.log('=== German Email Template Content Check ===\n'); + + const templates = await db('email_templates').select('*'); + + for (const template of templates) { + console.log(`\nTemplate: ${template.template_key}`); + console.log('====================================='); + + // Check German subject + console.log('\nGERMAN SUBJECT:'); + console.log(template.subject_de || 'MISSING'); + + // Check if German HTML body has English content + console.log('\nGERMAN HTML BODY:'); + const germanHtml = template.body_html_de || ''; + + // Check for English phrases in German template + const englishPhrases = [ + 'Dear', 'Gallery', 'has been', 'Your photo', 'successfully', + 'Details:', 'Link:', 'Password:', 'Expires:', 'Event Date:', + 'Thank you', 'Best regards', 'View Gallery', 'days' + ]; + + const foundEnglish = englishPhrases.filter(phrase => + germanHtml.toLowerCase().includes(phrase.toLowerCase()) + ); + + if (foundEnglish.length > 0) { + console.log('⚠️ Found English phrases in German template:', foundEnglish.join(', ')); + } + + // Show first 500 chars of German HTML + console.log(germanHtml.substring(0, 500) + '...\n'); + + // Check German text body + console.log('GERMAN TEXT BODY:'); + const germanText = template.body_text_de || ''; + console.log(germanText.substring(0, 300) + '...\n'); + } + + await db.destroy(); + } catch (error) { + console.error('Error:', error); + await db.destroy(); + process.exit(1); + } +} + +checkGermanTemplates(); \ No newline at end of file diff --git a/backend/scripts/fix-final-german-templates.js b/backend/scripts/fix-final-german-templates.js new file mode 100644 index 0000000..6473a86 --- /dev/null +++ b/backend/scripts/fix-final-german-templates.js @@ -0,0 +1,88 @@ +const { db } = require('../src/database/db'); + +async function fixFinalGermanTemplates() { + try { + console.log('Fixing remaining English words in German templates...\n'); + + // Get all templates + const templates = await db('email_templates').select('*'); + + for (const template of templates) { + let updated = false; + let updates = {}; + + // Fix subject_de + if (template.subject_de) { + updates.subject_de = template.subject_de; + } + + // Fix body_html_de + if (template.body_html_de) { + let html = template.body_html_de; + + // Replace English words with German + html = html.replace(/Gallery-Details:/g, 'Galerie-Details:'); + html = html.replace(/Galerie-Details:/g, 'Galerie-Details:'); + html = html.replace(/Details:/g, 'Details:'); + html = html.replace(/Link:/g, 'Link:'); + html = html.replace(/Gallery-Link:/g, 'Galerie-Link:'); + html = html.replace(/Galerie-Link:/g, 'Galerie-Link:'); + html = html.replace(/Archive-Details:/g, 'Archiv-Details:'); + html = html.replace(/Archiv-Details:/g, 'Archiv-Details:'); + + if (html !== template.body_html_de) { + updates.body_html_de = html; + updated = true; + } + } + + // Fix body_text_de + if (template.body_text_de) { + let text = template.body_text_de; + + text = text.replace(/Gallery-Details:/g, 'Galerie-Details:'); + text = text.replace(/Galerie-Details:/g, 'Galerie-Details:'); + text = text.replace(/Details:/g, 'Details:'); + text = text.replace(/Link:/g, 'Link:'); + text = text.replace(/Gallery-Link:/g, 'Galerie-Link:'); + text = text.replace(/Galerie-Link:/g, 'Galerie-Link:'); + text = text.replace(/Archive-Details:/g, 'Archiv-Details:'); + text = text.replace(/Archiv-Details:/g, 'Archiv-Details:'); + + if (text !== template.body_text_de) { + updates.body_text_de = text; + updated = true; + } + } + + // Also update the non-language-specific fields to match German + if (template.body_html_de) { + updates.body_html = template.body_html_de; + } + if (template.body_text_de) { + updates.body_text = template.body_text_de; + } + if (template.subject_de) { + updates.subject = template.subject_de; + } + + if (updated || Object.keys(updates).length > 0) { + await db('email_templates') + .where('template_key', template.template_key) + .update(updates); + console.log(`✅ Updated ${template.template_key}`); + } else { + console.log(`⏭️ No changes needed for ${template.template_key}`); + } + } + + console.log('\nDone!'); + await db.destroy(); + } catch (error) { + console.error('Error:', error); + await db.destroy(); + process.exit(1); + } +} + +fixFinalGermanTemplates(); \ No newline at end of file diff --git a/backend/scripts/test-german-emails.js b/backend/scripts/test-german-emails.js new file mode 100644 index 0000000..8b34f5b --- /dev/null +++ b/backend/scripts/test-german-emails.js @@ -0,0 +1,85 @@ +const { db } = require('../src/database/db'); +const { processTemplate } = require('../src/services/emailProcessor'); + +async function testGermanEmails() { + try { + console.log('=== Testing German Email Templates ===\n'); + + // Test variables + const testVars = { + host_name: 'Max Mustermann', + event_name: 'Hochzeit Schmidt', + event_date: '15.07.2024', + gallery_link: 'https://example.com/gallery/test', + gallery_password: 'test1234', + expiry_date: '15.08.2024', + days_remaining: '7', + welcome_message: 'Herzlich willkommen zu unserer Hochzeitsgalerie!', + archive_size: '250 MB', + archive_date: '16.08.2024', + photo_count: '347', + admin_email: 'support@example.com', + eventId: 1 + }; + + const templates = await db('email_templates').select('*'); + + for (const template of templates) { + console.log(`\n========== ${template.template_key.toUpperCase()} ==========`); + + // Process German version + const germanResult = await processGermanTemplate(template, testVars); + + console.log('\n--- GERMAN VERSION ---'); + console.log('Subject:', germanResult.subject); + console.log('\nHTML Preview (first 500 chars):'); + console.log(germanResult.htmlBody.substring(0, 500) + '...\n'); + + // Check for any remaining English text + const englishWords = ['Dear', 'Gallery', 'Details:', 'Link:', 'Password:', 'days', 'Thank you']; + const foundEnglish = englishWords.filter(word => + germanResult.htmlBody.includes(word) || germanResult.subject.includes(word) + ); + + if (foundEnglish.length > 0) { + console.log('⚠️ WARNING: Found English words:', foundEnglish.join(', ')); + } else { + console.log('✅ No English words found in German template'); + } + } + + await db.destroy(); + } catch (error) { + console.error('Error:', error); + await db.destroy(); + process.exit(1); + } +} + +async function processGermanTemplate(template, variables) { + // Process template as German + const subjectField = 'subject_de'; + const htmlField = 'body_html_de'; + const textField = 'body_text_de'; + + let subject = template[subjectField] || template.subject || ''; + let htmlBody = template[htmlField] || template.body_html || ''; + let textBody = template[textField] || template.body_text || ''; + + // Replace variables + Object.keys(variables).forEach(key => { + const regex = new RegExp(`{{${key}}}`, 'g'); + subject = subject.replace(regex, variables[key]); + htmlBody = htmlBody.replace(regex, variables[key]); + textBody = textBody.replace(regex, variables[key]); + }); + + // Handle conditionals (simplified) + htmlBody = htmlBody.replace(/{{#if welcome_message}}[\s\S]*?{{\/if}}/g, (match) => { + return variables.welcome_message ? match.replace(/{{#if welcome_message}}|{{\/if}}/g, '') : ''; + }); + + return { subject, htmlBody, textBody }; +} + +testGermanEmails(); \ No newline at end of file diff --git a/backend/src/routes/adminEvents.js b/backend/src/routes/adminEvents.js index fe6408b..2be8bd6 100644 --- a/backend/src/routes/adminEvents.js +++ b/backend/src/routes/adminEvents.js @@ -578,31 +578,54 @@ router.post('/:id/resend-email', adminAuth, async (req, res) => { return res.status(404).json({ error: 'Event not found' }); } - // Format dates properly for the email + // Get the language preference + let language = 'en'; + try { + // First check app_settings for general_default_language + const langSetting = await db('app_settings') + .where('setting_key', 'general_default_language') + .first(); + + if (langSetting && langSetting.setting_value) { + language = langSetting.setting_value; + } + } catch (err) { + console.warn('Could not fetch language setting:', err); + } + + // Format dates based on language const eventDate = new Date(event.event_date); const expiryDate = new Date(event.expires_at); + const dateLocale = language === 'de' ? 'de-DE' : 'en-US'; + + // Prepare password text based on language + const passwordText = language === 'de' + ? '(Aus Sicherheitsgründen nicht angezeigt)' + : '(Not shown for security reasons)'; // Queue the email await queueEmail(id, event.host_email, 'gallery_created', { host_name: event.host_name || event.host_email.split('@')[0], event_name: event.event_name, - event_date: eventDate.toLocaleDateString('de-DE'), // Using German date format + event_date: eventDate.toLocaleDateString(dateLocale), gallery_link: event.share_link, - gallery_password: '(Aus Sicherheitsgründen nicht angezeigt)', // Security: don't show password in resent emails - expiry_date: expiryDate.toLocaleDateString('de-DE'), + gallery_password: passwordText, + expiry_date: expiryDate.toLocaleDateString(dateLocale), welcome_message: event.welcome_message || '', eventId: id }); - // Log the activity - wrap in try-catch to prevent failure if activity logging fails + // Log the activity using the proper schema try { - await db('activity_logs').insert({ - event_id: id, + await logActivity('email_resent', { + email_type: 'gallery_created', + recipient: event.host_email, ip_address: req.ip || '0.0.0.0', - user_agent: req.get('user-agent') || 'Unknown', - action_type: 'email_resent', - action_details: JSON.stringify({ email_type: 'gallery_created' }), - timestamp: new Date() + user_agent: req.get('user-agent') || 'Unknown' + }, id, { + type: 'admin', + id: req.admin.id, + name: req.admin.username }); } catch (logError) { console.error('Warning: Failed to log activity:', logError); diff --git a/backend/src/services/emailProcessor.js b/backend/src/services/emailProcessor.js index f6d7526..07f8125 100644 --- a/backend/src/services/emailProcessor.js +++ b/backend/src/services/emailProcessor.js @@ -73,7 +73,19 @@ async function getRecipientLanguage(email, eventId = null) { } } - // Second priority: Check email configs for default language + // Second priority: Check app_settings for general default language + try { + const langSetting = await db('app_settings') + .where('setting_key', 'general_default_language') + .first(); + if (langSetting && langSetting.setting_value) { + return langSetting.setting_value; + } + } catch (error) { + logger.error('Error fetching app settings language:', error); + } + + // Third priority: Check email configs for default language try { const emailConfig = await db('email_configs').first(); if (emailConfig && emailConfig.default_language) { @@ -83,7 +95,7 @@ async function getRecipientLanguage(email, eventId = null) { logger.error('Error fetching email config language:', error); } - // Third priority: Check if the email domain suggests German + // Fourth priority: Check if the email domain suggests German if (email) { const germanDomains = ['.de', '.at', '.ch', '.li']; const domain = email.toLowerCase();