2026-01-07 17:29:00 -06:00
|
|
|
const nodemailer = require('nodemailer');
|
2026-01-08 09:52:20 -06:00
|
|
|
const { SESv2Client, SendEmailCommand } = require('@aws-sdk/client-sesv2');
|
2026-01-07 17:29:00 -06:00
|
|
|
|
2026-01-08 09:55:28 -06:00
|
|
|
const sesClient = new SESv2Client({
|
2026-01-07 17:29:00 -06:00
|
|
|
region: process.env.AWS_REGION,
|
|
|
|
|
credentials: {
|
|
|
|
|
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
|
|
|
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Create the transporter
|
|
|
|
|
const transporter = nodemailer.createTransport({
|
2026-01-08 09:55:28 -06:00
|
|
|
SES: { sesClient, SendEmailCommand },
|
2026-01-07 17:29:00 -06:00
|
|
|
});
|
|
|
|
|
|
2026-01-08 13:22:32 -06:00
|
|
|
// Helper function to get color based on time of day
|
2026-01-08 15:18:15 -06:00
|
|
|
// hour parameter should be the local hour (0-23) from the client
|
|
|
|
|
function getColorForTime(hour) {
|
2026-01-08 13:22:32 -06:00
|
|
|
if (hour >= 5 && hour < 10) {
|
|
|
|
|
// Morning - orange
|
2026-01-08 15:18:15 -06:00
|
|
|
return '#FF9800';
|
2026-01-08 13:22:32 -06:00
|
|
|
} else if (hour >= 10 && hour < 18) {
|
|
|
|
|
// Afternoon - blue
|
2026-01-08 15:18:15 -06:00
|
|
|
return '#2196F3';
|
2026-01-08 13:22:32 -06:00
|
|
|
} else {
|
|
|
|
|
// Evening/Night - purple
|
2026-01-08 15:18:15 -06:00
|
|
|
return '#471189';
|
2026-01-08 13:22:32 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-07 17:29:00 -06:00
|
|
|
// Helper function to send email
|
2026-01-08 15:18:15 -06:00
|
|
|
async function sendVerificationEmail(toEmail, token, name, localHour = new Date().getHours()) {
|
|
|
|
|
const primaryColor = getColorForTime(localHour);
|
2026-01-07 17:29:00 -06:00
|
|
|
const verificationLink = `https://wahwa.com/verify-email?token=${token}`;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const info = await transporter.sendMail({
|
|
|
|
|
from: `"BlindMaster" <${process.env.EMAIL_FROM}>`, // Sender address
|
|
|
|
|
to: toEmail,
|
|
|
|
|
subject: "Verify your BlindMaster account",
|
|
|
|
|
html: `
|
2026-01-08 11:51:31 -06:00
|
|
|
<!DOCTYPE html>
|
|
|
|
|
<html>
|
|
|
|
|
<head>
|
|
|
|
|
<meta charset="UTF-8">
|
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
|
<link href="https://fonts.googleapis.com/css2?family=ABeeZee:ital@0;1&display=swap" rel="stylesheet">
|
|
|
|
|
</head>
|
|
|
|
|
<body style="margin: 0; padding: 0; background-color: #f5f5f5; font-family: 'ABeeZee', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;">
|
|
|
|
|
<table width="100%" cellpadding="0" cellspacing="0" style="background-color: #f5f5f5; padding: 40px 0;">
|
|
|
|
|
<tr>
|
|
|
|
|
<td align="center">
|
|
|
|
|
<table width="600" cellpadding="0" cellspacing="0" style="background-color: #ffffff; border-radius: 12px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); overflow: hidden; max-width: 600px;">
|
|
|
|
|
|
|
|
|
|
<!-- Header with brand color -->
|
|
|
|
|
<tr>
|
2026-01-08 15:21:40 -06:00
|
|
|
<td align="center" style="background-color: ${primaryColor}; padding: 40px 20px;">
|
2026-01-08 15:35:32 -06:00
|
|
|
<h1 style="margin: 0; color: #ffffff; font-size: 32px; font-weight: bold; letter-spacing: 0.5px;">BlindMaster</h1>
|
|
|
|
|
<p style="margin: 10px 0 0 0; color: #ffffff; font-size: 14px; opacity: 0.95;">Smart Home Automation</p>
|
2026-01-08 11:51:31 -06:00
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<!-- Welcome message -->
|
|
|
|
|
<tr>
|
|
|
|
|
<td style="padding: 50px 40px 30px 40px; text-align: center;">
|
2026-01-08 15:35:32 -06:00
|
|
|
<h2 style="margin: 0 0 20px 0; color: #333333; font-size: 28px; font-weight: normal;">
|
|
|
|
|
Welcome${name && name.trim() ? `, <span style="color: ${primaryColor};">${name.trim()}</span>` : ''}!
|
2026-01-08 11:51:31 -06:00
|
|
|
</h2>
|
2026-01-08 15:35:32 -06:00
|
|
|
<p style="margin: 0 0 30px 0; color: #666666; font-size: 16px; line-height: 1.6;">
|
2026-01-08 11:51:31 -06:00
|
|
|
Thank you for joining BlindMaster! To electrify your blinds, please verify your email address 🥹
|
|
|
|
|
</p>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<!-- CTA Button -->
|
|
|
|
|
<tr>
|
|
|
|
|
<td align="center" style="padding: 0 40px 40px 40px;">
|
|
|
|
|
<a href="${verificationLink}"
|
2026-01-08 15:35:32 -06:00
|
|
|
style="display: inline-block; padding: 16px 48px; background-color: ${primaryColor}; color: #ffffff; text-decoration: none; border-radius: 8px; font-size: 16px; font-weight: bold; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); transition: all 0.3s ease;">
|
2026-01-08 11:51:31 -06:00
|
|
|
Verify Email Address
|
|
|
|
|
</a>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<!-- Divider -->
|
|
|
|
|
<tr>
|
|
|
|
|
<td style="padding: 0 40px;">
|
|
|
|
|
<div style="border-top: 1px solid #e0e0e0;"></div>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<!-- Footer info -->
|
|
|
|
|
<tr>
|
|
|
|
|
<td style="padding: 30px 40px; text-align: center;">
|
2026-01-08 15:35:32 -06:00
|
|
|
<p style="margin: 0 0 10px 0; color: #999999; font-size: 13px; line-height: 1.5;">
|
2026-01-08 11:51:31 -06:00
|
|
|
This verification link will expire in <strong style="color: #666666;">24 hours</strong>.
|
|
|
|
|
</p>
|
2026-01-08 15:35:32 -06:00
|
|
|
<p style="margin: 0; color: #999999; font-size: 13px; line-height: 1.5;">
|
2026-01-08 11:51:31 -06:00
|
|
|
If you didn't create a BlindMaster account, please ignore this email!!!
|
|
|
|
|
</p>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<!-- Footer bar -->
|
|
|
|
|
<tr>
|
|
|
|
|
<td align="center" style="background-color: #f9f9f9; padding: 25px 40px;">
|
2026-01-08 15:35:32 -06:00
|
|
|
<p style="margin: 0; color: #999999; font-size: 12px;">
|
2026-01-08 11:51:31 -06:00
|
|
|
© 2026 BlindMaster. All rights reserved.
|
|
|
|
|
</p>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
</table>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
</body>
|
|
|
|
|
</html>
|
2026-01-07 17:29:00 -06:00
|
|
|
`,
|
|
|
|
|
});
|
|
|
|
|
console.log("Email sent successfully:", info.messageId);
|
|
|
|
|
return true;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Error sending email:", error);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-08 13:06:20 -06:00
|
|
|
// Helper function to send password reset email
|
2026-01-08 15:18:15 -06:00
|
|
|
async function sendPasswordResetEmail(toEmail, code, name, localHour = new Date().getHours()) {
|
|
|
|
|
const primaryColor = getColorForTime(localHour);
|
2026-01-08 13:22:32 -06:00
|
|
|
|
2026-01-08 13:06:20 -06:00
|
|
|
try {
|
|
|
|
|
const info = await transporter.sendMail({
|
|
|
|
|
from: `"BlindMaster" <${process.env.EMAIL_FROM}>`,
|
|
|
|
|
to: toEmail,
|
|
|
|
|
subject: "Reset your BlindMaster password",
|
|
|
|
|
html: `
|
|
|
|
|
<!DOCTYPE html>
|
|
|
|
|
<html>
|
|
|
|
|
<head>
|
|
|
|
|
<meta charset="UTF-8">
|
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
|
<link href="https://fonts.googleapis.com/css2?family=ABeeZee:ital@0;1&display=swap" rel="stylesheet">
|
|
|
|
|
</head>
|
|
|
|
|
<body style="margin: 0; padding: 0; background-color: #f5f5f5; font-family: 'ABeeZee', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;">
|
|
|
|
|
<table width="100%" cellpadding="0" cellspacing="0" style="background-color: #f5f5f5; padding: 40px 0;">
|
|
|
|
|
<tr>
|
|
|
|
|
<td align="center">
|
|
|
|
|
<table width="600" cellpadding="0" cellspacing="0" style="background-color: #ffffff; border-radius: 12px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); overflow: hidden; max-width: 600px;">
|
|
|
|
|
|
|
|
|
|
<!-- Header with brand color -->
|
|
|
|
|
<tr>
|
2026-01-08 15:21:40 -06:00
|
|
|
<td align="center" style="background-color: ${primaryColor}; padding: 40px 20px;">
|
2026-01-08 15:35:32 -06:00
|
|
|
<h1 style="margin: 0; color: #ffffff; font-size: 32px; font-weight: bold; letter-spacing: 0.5px;">BlindMaster</h1>
|
|
|
|
|
<p style="margin: 10px 0 0 0; color: #ffffff; font-size: 14px; opacity: 0.95;">Smart Home Automation</p>
|
2026-01-08 13:06:20 -06:00
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<!-- Message -->
|
|
|
|
|
<tr>
|
|
|
|
|
<td style="padding: 50px 40px 30px 40px; text-align: center;">
|
2026-01-08 15:35:32 -06:00
|
|
|
<h2 style="margin: 0 0 20px 0; color: #333333; font-size: 28px; font-weight: normal;">
|
2026-01-08 13:06:20 -06:00
|
|
|
Password Reset Request
|
|
|
|
|
</h2>
|
2026-01-08 15:35:32 -06:00
|
|
|
<p style="margin: 0 0 30px 0; color: #666666; font-size: 16px; line-height: 1.6;">
|
2026-01-08 13:06:20 -06:00
|
|
|
${name && name.trim() ? `Hi ${name.trim()}, we` : 'We'} received a request to reset your password. Use the code below to continue:
|
|
|
|
|
</p>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<!-- Code Display -->
|
|
|
|
|
<tr>
|
|
|
|
|
<td align="center" style="padding: 0 40px 40px 40px;">
|
2026-01-08 15:18:15 -06:00
|
|
|
<div style="display: inline-block; padding: 20px 40px; background-color: #f9f9f9; border-radius: 8px; border: 2px solid ${primaryColor};">
|
|
|
|
|
<p style="margin: 0; color: ${primaryColor}; font-size: 36px; font-weight: bold; letter-spacing: 8px; font-family: 'Courier New', monospace;">
|
2026-01-08 13:06:20 -06:00
|
|
|
${code}
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<!-- Divider -->
|
|
|
|
|
<tr>
|
|
|
|
|
<td style="padding: 0 40px;">
|
|
|
|
|
<div style="border-top: 1px solid #e0e0e0;"></div>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<!-- Footer info -->
|
|
|
|
|
<tr>
|
|
|
|
|
<td style="padding: 30px 40px; text-align: center;">
|
2026-01-08 15:35:32 -06:00
|
|
|
<p style="margin: 0 0 10px 0; color: #999999; font-size: 13px; line-height: 1.5;">
|
2026-01-08 13:06:20 -06:00
|
|
|
This code will expire in <strong style="color: #666666;">15 minutes</strong>.
|
|
|
|
|
</p>
|
2026-01-08 15:35:32 -06:00
|
|
|
<p style="margin: 0; color: #999999; font-size: 13px; line-height: 1.5;">
|
2026-01-08 13:06:20 -06:00
|
|
|
If you didn't request a password reset, please ignore this email.
|
|
|
|
|
</p>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<!-- Footer bar -->
|
|
|
|
|
<tr>
|
|
|
|
|
<td align="center" style="background-color: #f9f9f9; padding: 25px 40px;">
|
2026-01-08 15:35:32 -06:00
|
|
|
<p style="margin: 0; color: #999999; font-size: 12px;">
|
2026-01-08 13:06:20 -06:00
|
|
|
© 2026 BlindMaster. All rights reserved.
|
|
|
|
|
</p>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
</table>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
</body>
|
|
|
|
|
</html>
|
|
|
|
|
`,
|
|
|
|
|
});
|
|
|
|
|
console.log("Password reset email sent successfully:", info.messageId);
|
|
|
|
|
return true;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Error sending password reset email:", error);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module.exports = { sendVerificationEmail, sendPasswordResetEmail };
|