feat: add auto-reply email to contact form submitter
- Add sendAutoReply() to email service with branded HTML template - Auto-reply sends a professional thank-you with 1-business-day SLA - Includes urgent contact info (phone + email) in a styled callout - Wired into POST /api/contact alongside the internal notification - Both emails are fire-and-forget (don't block API response)
This commit is contained in:
parent
b9dbd59e7d
commit
78961331d1
@ -1,7 +1,7 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
import { dbRun, dbAll, dbGet } from '../db/database.js';
|
import { dbRun, dbAll, dbGet } from '../db/database.js';
|
||||||
import { generateId, ApiResponse, asyncHandler } from '../utils/helpers.js';
|
import { generateId, ApiResponse, asyncHandler } from '../utils/helpers.js';
|
||||||
import { sendContactEmail } from '../services/email.js';
|
import { sendContactEmail, sendAutoReply } from '../services/email.js';
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ router.post('/', asyncHandler(async (req, res) => {
|
|||||||
|
|
||||||
console.log(`New contact submission: ${submissionId} from ${email}`);
|
console.log(`New contact submission: ${submissionId} from ${email}`);
|
||||||
|
|
||||||
// Send email notification (fire-and-forget — don't block the response)
|
// Send email notification to you (fire-and-forget)
|
||||||
sendContactEmail({ name: name.trim(), email: email.trim().toLowerCase(), company: company?.trim(), message: message.trim() })
|
sendContactEmail({ name: name.trim(), email: email.trim().toLowerCase(), company: company?.trim(), message: message.trim() })
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
@ -78,6 +78,19 @@ router.post('/', asyncHandler(async (req, res) => {
|
|||||||
console.error(`[Contact] Email error for submission ${submissionId}:`, err.message);
|
console.error(`[Contact] Email error for submission ${submissionId}:`, err.message);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Send auto-reply to the submitter (fire-and-forget)
|
||||||
|
sendAutoReply({ name: name.trim(), email: email.trim().toLowerCase() })
|
||||||
|
.then((result) => {
|
||||||
|
if (result.success) {
|
||||||
|
console.log(`[Contact] Auto-reply sent for submission ${submissionId}`);
|
||||||
|
} else {
|
||||||
|
console.warn(`[Contact] Auto-reply failed for submission ${submissionId}: ${result.error}`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(`[Contact] Auto-reply error for submission ${submissionId}:`, err.message);
|
||||||
|
});
|
||||||
|
|
||||||
res.status(201).json(ApiResponse(true, {
|
res.status(201).json(ApiResponse(true, {
|
||||||
id: submissionId,
|
id: submissionId,
|
||||||
message: 'Thank you for your message! We\'ll get back to you soon.'
|
message: 'Thank you for your message! We\'ll get back to you soon.'
|
||||||
|
|||||||
@ -132,6 +132,84 @@ export const sendContactEmail = async ({ name, email, company, message }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send an auto-reply thank-you email to the person who submitted the form.
|
||||||
|
*
|
||||||
|
* @param {Object} params
|
||||||
|
* @param {string} params.name - Submitter's name
|
||||||
|
* @param {string} params.email - Submitter's email
|
||||||
|
* @returns {Promise<{success: boolean, messageId?: string, error?: string}>}
|
||||||
|
*/
|
||||||
|
export const sendAutoReply = async ({ name, email }) => {
|
||||||
|
try {
|
||||||
|
const transporter = createTransport();
|
||||||
|
const from = getFromAddress();
|
||||||
|
|
||||||
|
const info = await transporter.sendMail({
|
||||||
|
from: `"Moxiegen" <${from}>`,
|
||||||
|
to: email,
|
||||||
|
subject: 'Thank you for contacting Moxiegen',
|
||||||
|
text: [
|
||||||
|
`Dear ${name},`,
|
||||||
|
'',
|
||||||
|
'Thank you for reaching out to Moxiegen. We have received your inquiry',
|
||||||
|
'and our team is currently reviewing your message.',
|
||||||
|
'',
|
||||||
|
'We understand the importance of your request and will make every effort',
|
||||||
|
'to respond to you as promptly as possible. In most cases, you can expect',
|
||||||
|
'to hear back from us within one business day.',
|
||||||
|
'',
|
||||||
|
'If your matter is urgent, please feel free to call us directly at',
|
||||||
|
'+1 (855) 246-6943 or email info@moxiegen.com.',
|
||||||
|
'',
|
||||||
|
'Thank you for your interest in Moxiegen. We look forward to speaking with you.',
|
||||||
|
'',
|
||||||
|
'Best regards,',
|
||||||
|
'The Moxiegen Team',
|
||||||
|
'https://moxiegen.com',
|
||||||
|
].join('\n'),
|
||||||
|
html: `
|
||||||
|
<div style="font-family: Arial, Helvetica, sans-serif; max-width: 600px; margin: 0 auto; padding: 0;">
|
||||||
|
<div style="background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%); padding: 40px 40px 30px 40px; border-radius: 12px 12px 0 0;">
|
||||||
|
<h1 style="color: #00f0c8; font-size: 28px; margin: 0 0 4px 0; font-weight: 600;">Moxiegen</h1>
|
||||||
|
<p style="color: #94a3b8; font-size: 14px; margin: 0;">Algorithmic Enhancement for Enterprise AI</p>
|
||||||
|
</div>
|
||||||
|
<div style="background: #ffffff; padding: 40px; border-radius: 0 0 12px 12px; color: #334155;">
|
||||||
|
<p style="font-size: 18px; margin: 0 0 20px 0;">Dear ${escapeHtml(name)},</p>
|
||||||
|
<p style="font-size: 16px; line-height: 1.6; margin: 0 0 16px 0;">
|
||||||
|
Thank you for reaching out to <strong>Moxiegen</strong>. We have received your inquiry and our team is currently reviewing your message.
|
||||||
|
</p>
|
||||||
|
<p style="font-size: 16px; line-height: 1.6; margin: 0 0 16px 0;">
|
||||||
|
We understand the importance of your request and will make every effort to respond to you as promptly as possible. In most cases, you can expect to hear back from us within <strong>one business day</strong>.
|
||||||
|
</p>
|
||||||
|
<div style="background: #f0fdf4; border-left: 4px solid #00f0c8; padding: 16px 20px; border-radius: 0 8px 8px 0; margin: 24px 0;">
|
||||||
|
<p style="font-size: 15px; color: #334155; margin: 0 0 6px 0;"><strong>Need a faster response?</strong></p>
|
||||||
|
<p style="font-size: 15px; color: #334155; margin: 0;">
|
||||||
|
Call us at <a href="tel:+18552466943" style="color: #00f0c8; text-decoration: none;">+1 (855) 246-6943</a> or email
|
||||||
|
<a href="mailto:info@moxiegen.com" style="color: #00f0c8; text-decoration: none;">info@moxiegen.com</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<p style="font-size: 16px; line-height: 1.6; margin: 0 0 8px 0;">
|
||||||
|
Thank you for your interest in Moxiegen. We look forward to speaking with you.
|
||||||
|
</p>
|
||||||
|
<p style="font-size: 16px; margin: 32px 0 0 0; color: #334155;">Best regards,</p>
|
||||||
|
<p style="font-size: 16px; margin: 4px 0 0 0; color: #334155;"><strong>The Moxiegen Team</strong></p>
|
||||||
|
</div>
|
||||||
|
<div style="text-align: center; padding: 24px 0;">
|
||||||
|
<a href="https://moxiegen.com" style="color: #00f0c8; text-decoration: none; font-size: 14px;">moxiegen.com</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`[Email] Auto-reply sent to ${email} (messageId: ${info.messageId})`);
|
||||||
|
return { success: true, messageId: info.messageId };
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[Email] Failed to send auto-reply:', err.message);
|
||||||
|
return { success: false, error: err.message };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Minimal HTML entity escaping for user-submitted content.
|
* Minimal HTML entity escaping for user-submitted content.
|
||||||
* @param {string} str
|
* @param {string} str
|
||||||
@ -146,4 +224,4 @@ function escapeHtml(str) {
|
|||||||
.replace(/'/g, ''');
|
.replace(/'/g, ''');
|
||||||
}
|
}
|
||||||
|
|
||||||
export default { verifyTransport, sendContactEmail };
|
export default { verifyTransport, sendContactEmail, sendAutoReply };
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user