🛂 Prevent duplicate email sends and limit to 5 max email sending in one go

This commit is contained in:
Baptiste Arnaud 2024-10-11 12:16:58 +02:00
parent 5fb10d2ffb
commit a6db54c441
No known key found for this signature in database

View File

@ -1,4 +1,4 @@
import { defaultSendEmailOptions } from "@typebot.io/blocks-integrations/sendEmail/constants";
import { TRPCError } from "@trpc/server";
import type {
SendEmailBlock,
SmtpCredentials,
@ -35,6 +35,10 @@ import { defaultFrom, defaultTransportOptions } from "./constants";
export const sendEmailSuccessDescription = "Email successfully sent";
export const sendEmailErrorDescription = "Email not sent";
let prevHash: string | undefined;
let emailSendingCount = 0;
const maxEmailSending = 5;
export const executeSendEmailBlock = async (
state: SessionState,
block: SendEmailBlock,
@ -42,7 +46,11 @@ export const executeSendEmailBlock = async (
const logs: ChatLog[] = [];
const { options } = block;
if (!state.typebotsQueue[0]) throw new Error("No typebot in queue");
const { typebot, resultId, answers } = state.typebotsQueue[0];
const {
typebot: { id, variables },
resultId,
answers,
} = state.typebotsQueue[0];
const isPreview = !resultId;
if (isPreview)
return {
@ -55,12 +63,12 @@ export const executeSendEmailBlock = async (
],
};
const bodyUniqueVariable = findUniqueVariable(typebot.variables)(
const bodyUniqueVariable = findUniqueVariable(variables)(
options?.body,
)?.value;
const body = bodyUniqueVariable
? stringifyUniqueVariableValueAsHtml(bodyUniqueVariable)
: parseVariables(typebot.variables, { isInsideHtml: !options?.isBodyCode })(
: parseVariables(variables, { isInsideHtml: !options?.isBodyCode })(
options?.body ?? "",
);
@ -75,26 +83,28 @@ export const executeSendEmailBlock = async (
});
return { outgoingEdgeId: block.outgoingEdgeId, logs };
}
if (emailSendingCount >= maxEmailSending)
throw new TRPCError({
code: "FORBIDDEN",
message: "Attempt to send more than 5 emails",
});
try {
const sendEmailLogs = await sendEmail({
typebot,
typebot: { id, variables },
answers,
credentialsId: options.credentialsId,
recipients: options.recipients.map(parseVariables(typebot.variables)),
recipients: options.recipients.map(parseVariables(variables)),
subject: options.subject
? parseVariables(typebot.variables)(options?.subject)
? parseVariables(variables)(options?.subject)
: undefined,
body,
cc: options.cc
? options.cc.map(parseVariables(typebot.variables))
: undefined,
bcc: options.bcc
? options.bcc.map(parseVariables(typebot.variables))
: undefined,
cc: options.cc ? options.cc.map(parseVariables(variables)) : undefined,
bcc: options.bcc ? options.bcc.map(parseVariables(variables)) : undefined,
replyTo: options.replyTo
? parseVariables(typebot.variables)(options.replyTo)
? parseVariables(variables)(options.replyTo)
: undefined,
fileUrls: getFileUrls(typebot.variables)(options.attachmentsVariableId),
fileUrls: getFileUrls(variables)(options.attachmentsVariableId),
isCustomBody: options.isCustomBody,
isBodyCode: options.isBodyCode,
});
@ -107,6 +117,8 @@ export const executeSendEmailBlock = async (
});
}
emailSendingCount += 1;
return { outgoingEdgeId: block.outgoingEdgeId, logs };
};
@ -133,7 +145,7 @@ const sendEmail = async ({
replyTo: string | undefined;
isBodyCode: boolean | undefined;
isCustomBody: boolean | undefined;
typebot: TypebotInSession;
typebot: Pick<TypebotInSession, "id" | "variables">;
answers: AnswerInSessionState[];
fileUrls?: string | string[];
}): Promise<ChatLog[] | undefined> => {
@ -191,6 +203,15 @@ const sendEmail = async ({
attachments: await parseAttachments(fileUrls, typebot.id),
...emailBody,
};
const hash = JSON.stringify(email);
if (prevHash && prevHash === hash)
throw new TRPCError({
code: "FORBIDDEN",
message: "Attempt to send the same email twice",
});
prevHash = hash;
try {
await transporter.sendMail(email);
logs.push({
@ -251,7 +272,7 @@ const getEmailBody = async ({
typebot,
answersInSession,
}: {
typebot: TypebotInSession;
typebot: Pick<TypebotInSession, "id" | "variables">;
answersInSession: AnswerInSessionState[];
} & Pick<
NonNullable<SendEmailBlock["options"]>,