diff --git a/beacon/app/api/SendEmails/route.js b/beacon/app/api/SendEmails/route.js
index 047fe9e..27ebba3 100644
--- a/beacon/app/api/SendEmails/route.js
+++ b/beacon/app/api/SendEmails/route.js
@@ -3,6 +3,7 @@ import { SESClient, SendEmailCommand } from '@aws-sdk/client-ses';
import * as XLSX from 'xlsx';
import { promises as fs } from 'fs';
import path from 'path';
+import pLimit from 'p-limit';
export async function POST(request) {
// No tempDir needed; use in-memory buffers
@@ -112,94 +113,162 @@ export async function POST(request) {
const results = [];
let processedCount = 0;
- for (const row of data) {
- try {
- processedCount++;
- console.log(`Processing email ${processedCount}/${data.length} for ${row.Email}`);
-
- // Skip rows with missing email or name
- if (!row.Email || !row.Name) {
- results.push({
- email: row.Email || 'Unknown',
- name: row.Name || 'Unknown',
- status: 'skipped',
- message: 'Missing email or name'
- });
- continue;
- }
-
- // Replace placeholders in template
- let personalizedTemplate = template;
-
- // Replace {{Recipient_name}} with the actual name
- personalizedTemplate = personalizedTemplate.replace(/{{Recipient_name}}/g, row.Name);
-
- // Replace any other placeholders that might exist
- Object.keys(row).forEach(key => {
- const placeholder = new RegExp(`{{${key}}}`, 'g');
- personalizedTemplate = personalizedTemplate.replace(placeholder, row[key]);
- });
-
- const isPlainTextOnly = false; // We always send as HTML now since rich text editor produces HTML
-
- // Create SES email parameters
- const emailParams = {
- Source: senderEmail,
- Destination: {
- ToAddresses: [row.Email],
- },
- Message: {
- Subject: {
- Data: subject,
- Charset: 'UTF-8',
- },
- Body: {
- Html: {
- Data: personalizedTemplate,
- Charset: 'UTF-8',
+ // Instead of for...of
+ const limit = pLimit(12); // 12 concurrent sends (SES default rate)
+
+ // for (const row of data) {
+ // try {
+ // processedCount++;
+ // console.log(`Processing email ${processedCount}/${data.length} for ${row.Email}`);
+
+ // // Skip rows with missing email or name
+ // if (!row.Email || !row.Name) {
+ // results.push({
+ // email: row.Email || 'Unknown',
+ // name: row.Name || 'Unknown',
+ // status: 'skipped',
+ // message: 'Missing email or name'
+ // });
+ // continue;
+ // }
+
+ // // Replace placeholders in template
+ // let personalizedTemplate = template;
+
+ // // Replace {{Recipient_name}} with the actual name
+ // personalizedTemplate = personalizedTemplate.replace(/{{Recipient_name}}/g, row.Name);
+
+ // // Replace any other placeholders that might exist
+ // Object.keys(row).forEach(key => {
+ // const placeholder = new RegExp(`{{${key}}}`, 'g');
+ // personalizedTemplate = personalizedTemplate.replace(placeholder, row[key]);
+ // });
+
+ // const isPlainTextOnly = false; // We always send as HTML now since rich text editor produces HTML
+
+ // // Create SES email parameters
+ // const emailParams = {
+ // Source: senderEmail,
+ // Destination: {
+ // ToAddresses: [row.Email],
+ // },
+ // Message: {
+ // Subject: {
+ // Data: subject,
+ // Charset: 'UTF-8',
+ // },
+ // Body: {
+ // Html: {
+ // Data: personalizedTemplate,
+ // Charset: 'UTF-8',
+ // },
+ // },
+ // },
+ // };
+
+ // // Send email
+ // const command = new SendEmailCommand(emailParams);
+ // const response = await sesClient.send(command);
+
+ // results.push({
+ // email: row.Email,
+ // name: row.Name,
+ // status: 'success',
+ // message: 'Email sent successfully',
+ // messageId: response.MessageId
+ // });
+
+ // // Add a small delay between emails to avoid rate limiting
+ // if (processedCount < data.length) {
+ // await new Promise(resolve => setTimeout(resolve, 100));
+ // }
+
+ // } catch (error) {
+ // console.error(`Error sending email to ${row.Email}:`, error);
+
+ // // Provide specific error messages
+ // let errorMessage = error.message || 'Unknown error occurred';
+ // if (error.message.includes('Email address is not verified')) {
+ // errorMessage = 'Email address not verified in AWS SES';
+ // } else if (error.message.includes('AccessDenied')) {
+ // errorMessage = 'AWS SES permission denied - check IAM permissions';
+ // } else if (error.message.includes('InvalidParameterValue')) {
+ // errorMessage = 'Invalid email format or parameters';
+ // }
+
+ // results.push({
+ // email: row.Email,
+ // name: row.Name,
+ // status: 'error',
+ // message: errorMessage
+ // });
+ // }
+ // }
+
+ await Promise.all(
+ data.map((row, idx) =>
+ limit(async () => {
+ try {
+ processedCount++;
+ console.log(`Processing email ${processedCount}/${data.length} for ${row.Email}`);
+
+ if (!row.Email || !row.Name) {
+ results.push({
+ email: row.Email || 'Unknown',
+ name: row.Name || 'Unknown',
+ status: 'skipped',
+ message: 'Missing email or name'
+ });
+ return;
+ }
+
+ // Replace placeholders
+ let personalizedTemplate = template.replace(/{{Recipient_name}}/g, row.Name);
+ Object.keys(row).forEach(key => {
+ const placeholder = new RegExp(`{{${key}}}`, 'g');
+ personalizedTemplate = personalizedTemplate.replace(placeholder, row[key]);
+ });
+
+ const emailParams = {
+ Source: senderEmail,
+ Destination: { ToAddresses: [row.Email] },
+ Message: {
+ Subject: { Data: subject, Charset: 'UTF-8' },
+ Body: { Html: { Data: personalizedTemplate, Charset: 'UTF-8' } }
},
- },
- },
- };
-
- // Send email
- const command = new SendEmailCommand(emailParams);
- const response = await sesClient.send(command);
-
- results.push({
- email: row.Email,
- name: row.Name,
- status: 'success',
- message: 'Email sent successfully',
- messageId: response.MessageId
- });
-
- // Add a small delay between emails to avoid rate limiting
- if (processedCount < data.length) {
- await new Promise(resolve => setTimeout(resolve, 100));
- }
-
- } catch (error) {
- console.error(`Error sending email to ${row.Email}:`, error);
-
- // Provide specific error messages
- let errorMessage = error.message || 'Unknown error occurred';
- if (error.message.includes('Email address is not verified')) {
- errorMessage = 'Email address not verified in AWS SES';
- } else if (error.message.includes('AccessDenied')) {
- errorMessage = 'AWS SES permission denied - check IAM permissions';
- } else if (error.message.includes('InvalidParameterValue')) {
- errorMessage = 'Invalid email format or parameters';
- }
-
- results.push({
- email: row.Email,
- name: row.Name,
- status: 'error',
- message: errorMessage
- });
- }
- }
+ };
+
+ const command = new SendEmailCommand(emailParams);
+ const response = await sesClient.send(command);
+
+ results.push({
+ email: row.Email,
+ name: row.Name,
+ status: 'success',
+ message: 'Email sent successfully',
+ messageId: response.MessageId
+ });
+
+ } catch (error) {
+ console.error(`Error sending email to ${row.Email}:`, error);
+ let errorMessage = error.message || 'Unknown error occurred';
+ if (error.message.includes('Email address is not verified')) {
+ errorMessage = 'Email address not verified in AWS SES';
+ } else if (error.message.includes('AccessDenied')) {
+ errorMessage = 'AWS SES permission denied - check IAM permissions';
+ } else if (error.message.includes('InvalidParameterValue')) {
+ errorMessage = 'Invalid email format or parameters';
+ }
+ results.push({
+ email: row.Email,
+ name: row.Name,
+ status: 'error',
+ message: error.message || 'Unknown error occurred'
+ });
+ }
+ })
+ )
+ );
// Calculate summary
const successCount = results.filter(r => r.status === 'success').length;
diff --git a/beacon/public/sample-data.xlsx b/beacon/public/sample-data.xlsx
index b82cfe6..451439d 100644
Binary files a/beacon/public/sample-data.xlsx and b/beacon/public/sample-data.xlsx differ
diff --git a/beacon/templates/RSVP.html b/beacon/templates/RSVP.html
new file mode 100644
index 0000000..6a629c6
--- /dev/null
+++ b/beacon/templates/RSVP.html
@@ -0,0 +1,176 @@
+
+
+
+
+
+
+
+ Figma Fiesta 2.0 — RSVP
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Hello {{Recipient_name}},
+
+
You’re invited to Figma Fiesta 2.0 — a creative design event hosted by
+ GitHub Community SRM. Join us for an exciting session filled with design insights
+ and fun!
+
+
+
+ Date: 14th October
+ Time: 8:30 AM - 5:00 PM
+ Venue: iMac Lab C, Tech Park 1, SRM Institute of Science and Technology
+
+
+
+
+ RSVP before 8 PM tonight!
+ Only 60 seats are available. Entry is strictly limited to RSVP’d participants.
+ Upon registration, you will receive a QR ticket via email.
+
+
+
+
+ RSVP Now
+
+
+
RSVP quickly to secure your spot! Only registered attendees with a QR ticket will be
+ allowed entry.
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/beacon/templates/onboarding.html b/beacon/templates/onboarding.html
new file mode 100644
index 0000000..605ebeb
--- /dev/null
+++ b/beacon/templates/onboarding.html
@@ -0,0 +1,169 @@
+
+
+
+
+
+
+
+
+
+ 🎉 Congratulations – You’re Selected for GitHub Community SRM!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ GitHub Community SRM
+
+ |
+
+
+
+
+ Congratulations! Welcome to GitHub Community SRM
+
+
+ Dear candidate,
+ |
+
+
+ |
+
+ We are thrilled to announce that you have been selected to join GitHub Community
+ SRM!
+ Your talent and dedication have shone through, and we’re excited to welcome you to our
+ vibrant community.
+
+
+ To take the next step, please join our exclusive WhatsApp group where
+ we’ll share important updates, resources, and the next stages of your journey with us.
+
+ |
+
+
+ |
+
+ Join Our WhatsApp Group
+
+
+ |
+
+
+ |
+
+ This is a significant milestone, and we’re eager to see the contributions you’ll bring
+ to GitHub Community SRM. Congratulations again on your selection!
+
+
+ Warm regards,
+ GitHub Community SRM
+
+ |
+
+
+ |
+
+
+
+
+
\ No newline at end of file
diff --git a/beacon/templates/postpone.html b/beacon/templates/postpone.html
new file mode 100644
index 0000000..6e3ae91
--- /dev/null
+++ b/beacon/templates/postpone.html
@@ -0,0 +1,311 @@
+
+
+
+
+
+
+
+
+
+ Important Update: Interviews Postponed
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+
+ GitHub Community SRM – All Domains
+
+
+ Important Update
+
+
+ Interviews Postponed
+
+ |
+
+
+
+
+ |
+
+ Dear {{Recipient_name}},
+
+
+
+
+ New Interview Dates
+
+
+ Monday & Tuesday
+ September 15th & 16th, 2025
+
+
+
+
+ Due to Aaruush, all interviews scheduled for
+ today and tomorrow (11th and 12th September)
+ have been canceled and postponed to Monday and Tuesday (15th and 16th
+ September).
+
+
+
+ This change applies to all domains.
+
+
+
+ 💡
+
+ Missed Your Interview?
+
+
+ If you missed your interview earlier for any reason, you can attend on
+ Monday or Tuesday (15th or 16th September) to complete your
+ interview process.
+
+
+
+
+ Further details, if any, will be shared shortly. Thank you for your patience and
+ understanding.
+
+
+
+
+
+ Interview Details
+
+
+
+
+ |
+ 🏢
+
+ Venue
+
+ TP 301, TP 302
+ |
+
+ ⏰
+
+ Timing
+
+ From 5:20 PM
+ |
+
+
+
+
+
+
+
+ Need Help? Contact Us
+
+
+
+
+
+
+
+
+ See you soon and best of luck!
+
+
+ Best regards,
+ GitHub Community SRM
+
+
+ |
+
+
+
+
+
+
+ |
+ You're receiving this email because you're part of the GitHub Community SRM recruitment
+ process.
+ |
+
+
+ |
+
+
+
+
+
\ No newline at end of file
diff --git a/beacon/templates/task-invite-fixed.html b/beacon/templates/task-invite-fixed.html
deleted file mode 100644
index e69de29..0000000
diff --git a/beacon/templates/update.html b/beacon/templates/update.html
new file mode 100644
index 0000000..fc5e780
--- /dev/null
+++ b/beacon/templates/update.html
@@ -0,0 +1,320 @@
+
+
+
+
+
+
+
+
+
+ Follow-up: Interview Postponement — Walk-in Details
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+
+ GitHub Community SRM – All Domains
+
+
+ Follow-up
+
+
+ Interview Walk-in Details
+
+ |
+
+
+
+
+ |
+
+ Dear {{Recipient_name}},
+
+
+
+
+ New Interview Dates
+
+
+ Monday & Tuesday
+ September 15th & 16th, 2025
+
+
+
+
+ Following our previous email: interviews on 11th and
+ 12th September
+ were canceled and are moved to Monday and Tuesday (15th
+ and 16th September).
+
+
+
+ This change applies to all domains.
+
+
+
+ 💡
+
+ Missed Your Interview?
+
+
+ If you missed your interview earlier for any reason, you can attend on
+ Monday or Tuesday (15th or 16th September) to complete your
+ interview process.
+
+
+
+
+ If you have already completed your interview, there is no need to come
+ again.
+ Candidates who missed or have not yet given their
+ interview can
+ directly walk in on Monday or Tuesday (15th or 16th September)
+ from 5:20 PM.
+
+
+
+ Further details, if any, will be shared shortly. Thank you for your patience and
+ understanding.
+
+
+
+
+
+ Interview Details
+
+
+
+
+ |
+ 🏢
+
+ Venue
+
+ TP 301, TP 302
+ |
+
+ ⏰
+
+ Timing
+
+ From 5:20 PM
+ |
+
+
+
+
+
+
+
+ Need Help? Contact Us
+
+
+
+
+
+
+
+
+ See you soon and best of luck!
+
+
+ Best regards,
+ GitHub Community SRM
+
+
+ |
+
+
+
+
+
+
+ |
+ You're receiving this email because you're part of the GitHub Community SRM recruitment
+ process.
+ |
+
+
+ |
+
+
+
+
+
\ No newline at end of file