Skip to content

Commit

Permalink
Merge branch 'main' into feature/quick-actions
Browse files Browse the repository at this point in the history
  • Loading branch information
rockingrohit9639 authored Jan 17, 2025
2 parents 0906550 + 2cac285 commit 5bb109c
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 35 deletions.
38 changes: 36 additions & 2 deletions app/emails/mail.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ export const sendEmail = async ({
try {
// send mail with defined transport object
await transporter.sendMail({
from: from || SMTP_FROM || `"Shelf" <no-reply@emails.shelf.nu>`, // sender address
...(replyTo && { replyTo }), // reply to
from: from || SMTP_FROM || `"Shelf" <updates@emails.shelf.nu>`, // sender address
replyTo: replyTo || "[email protected]", // reply to
to, // list of receivers
subject, // Subject line
text, // plain text body
Expand Down Expand Up @@ -80,3 +80,37 @@ export const sendEmail = async ({
// Preview only available when sending through an Ethereal account
// console.log("Preview URL: %s", nodemailer.getTestMessageUrl(info));
};

/** Utility function to add delay between operations */
async function delay(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}

/** Process emails in batches with rate limiting
* @param emails - Array of email configurations to send
* @param batchSize - Number of emails to process per batch (default: 2)
* @param delayMs - Milliseconds to wait between batches (default: 1000ms)
*/
export async function sendEmailsWithRateLimit(
emails: Array<{
to: string;
subject: string;
text: string;
html: string;
}>,
batchSize = 2,
delayMs = 1100
): Promise<void> {
for (let i = 0; i < emails.length; i += batchSize) {
// Process emails in batches of specified size
const batch = emails.slice(i, i + batchSize);

// Send emails in current batch concurrently
await Promise.all(batch.map((email) => sendEmail(email)));

// If there are more emails to process, add delay before next batch
if (i + batchSize < emails.length) {
await delay(delayMs);
}
}
}
57 changes: 25 additions & 32 deletions app/modules/booking/service.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type {
} from "@prisma/client";
import { db } from "~/database/db.server";
import { bookingUpdatesTemplateString } from "~/emails/bookings-updates-template";
import { sendEmail } from "~/emails/mail.server";
import { sendEmail, sendEmailsWithRateLimit } from "~/emails/mail.server";
import { getStatusClasses, isOneDayEvent } from "~/utils/calendar";
import { getDateTimeFormat } from "~/utils/client-hints";
import { calcTimeDifference } from "~/utils/date-fns";
Expand Down Expand Up @@ -1317,38 +1317,31 @@ export async function bulkDeleteBookings({
bookingsWithSchedulerReference.map((booking) => cancelScheduler(booking))
);

/** Sending mails to required users */
await Promise.all(
bookingsToSendEmail.map((b) => {
const subject = `Booking deleted (${b.name}) - shelf.nu`;
const text = deletedBookingEmailContent({
bookingName: b.name,
assetsCount: b.assets.length,
custodian:
`${b.custodianUser?.firstName} ${b.custodianUser?.lastName}` ||
(b.custodianTeamMember?.name as string),
from: b.from as Date,
to: b.to as Date,
bookingId: b.id,
hints,
});

const html = bookingUpdatesTemplateString({
booking: b,
heading: `Your booking as been deleted: "${b.name}"`,
assetCount: b.assets.length,
hints,
hideViewButton: true,
});
const emailConfigs = bookingsToSendEmail.map((b) => ({
to: b.custodianUser?.email ?? "",
subject: `Booking deleted (${b.name}) - shelf.nu`,
text: deletedBookingEmailContent({
bookingName: b.name,
assetsCount: b.assets.length,
custodian:
`${b.custodianUser?.firstName} ${b.custodianUser?.lastName}` ||
(b.custodianTeamMember?.name as string),
from: b.from as Date,
to: b.to as Date,
bookingId: b.id,
hints,
}),
html: bookingUpdatesTemplateString({
booking: b,
heading: `Your booking as been deleted: "${b.name}"`,
assetCount: b.assets.length,
hints,
hideViewButton: true,
}),
}));

return sendEmail({
to: b.custodianUser?.email ?? "",
subject,
text,
html,
});
})
);
// Send emails with rate limiting
return await sendEmailsWithRateLimit(emailConfigs);
} catch (cause) {
const message =
cause instanceof ShelfError
Expand Down
2 changes: 1 addition & 1 deletion app/routes/_layout+/account-details.general.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ export async function action({ context, request }: ActionFunctionArgs) {
}

void sendEmail({
to: ADMIN_EMAIL || `"Shelf" <no-reply@emails.shelf.nu>`,
to: ADMIN_EMAIL || `"Shelf" <updates@emails.shelf.nu>`,
subject: "Delete account request",
text: `User with id ${userId} and email ${payload.email} has requested to delete their account. \n User: ${SERVER_URL}/admin-dashboard/${userId} \n\n Reason: ${reason}\n\n`,
});
Expand Down

0 comments on commit 5bb109c

Please sign in to comment.