Skip to content

Commit 8f0bf65

Browse files
committed
supresses everywhere properly
1 parent c2d83dd commit 8f0bf65

5 files changed

Lines changed: 363 additions & 54 deletions

File tree

apps/web/src/app/(dashboard)/suppressions/suppression-list.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ export default function SuppressionList() {
158158
<TableBody>
159159
{suppressionsQuery.isLoading ? (
160160
<TableRow className="h-32">
161-
<TableCell colSpan={5} className="text-center py-4">
161+
<TableCell colSpan={4} className="text-center py-4">
162162
<Spinner
163163
className="w-6 h-6 mx-auto"
164164
innerSvgClass="stroke-primary"
@@ -167,7 +167,7 @@ export default function SuppressionList() {
167167
</TableRow>
168168
) : suppressionsQuery.data?.suppressions.length === 0 ? (
169169
<TableRow className="h-32">
170-
<TableCell colSpan={5} className="text-center py-4">
170+
<TableCell colSpan={4} className="text-center py-4">
171171
No suppressed emails found
172172
</TableCell>
173173
</TableRow>

apps/web/src/server/api/routers/suppression.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,9 @@ export const suppressionRouter = createTRPCRouter({
8484
)
8585
.mutation(async ({ ctx, input }) => {
8686
return SuppressionService.addMultipleSuppressions(
87-
input.emails.map((email) => ({
88-
email,
89-
teamId: ctx.team.id,
90-
reason: input.reason,
91-
}))
87+
ctx.team.id,
88+
input.emails,
89+
input.reason
9290
);
9391
}),
9492

apps/web/src/server/service/campaign-service.ts

Lines changed: 101 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
} from "../queue/queue-constants";
1919
import { logger } from "../logger/log";
2020
import { createWorkerHandler, TeamJob } from "../queue/bullmq-context";
21+
import { SuppressionService } from "./suppression-service";
2122

2223
export async function sendCampaign(id: string) {
2324
let campaign = await db.campaign.findUnique({
@@ -253,6 +254,35 @@ async function processContactEmail(jobData: CampaignEmailJob) {
253254

254255
const unsubscribeUrl = createUnsubUrl(contact.id, emailConfig.campaignId);
255256

257+
// Check for suppressed emails before processing
258+
const toEmails = [contact.email];
259+
const ccEmails = emailConfig.cc || [];
260+
const bccEmails = emailConfig.bcc || [];
261+
262+
// Collect all unique emails to check for suppressions
263+
const allEmailsToCheck = [
264+
...new Set([...toEmails, ...ccEmails, ...bccEmails]),
265+
];
266+
267+
const suppressionResults = await SuppressionService.checkMultipleEmails(
268+
allEmailsToCheck,
269+
emailConfig.teamId
270+
);
271+
272+
// Filter each field separately
273+
const filteredToEmails = toEmails.filter(
274+
(email) => !suppressionResults[email]
275+
);
276+
const filteredCcEmails = ccEmails.filter(
277+
(email) => !suppressionResults[email]
278+
);
279+
const filteredBccEmails = bccEmails.filter(
280+
(email) => !suppressionResults[email]
281+
);
282+
283+
// Check if the contact's email (TO recipient) is suppressed
284+
const isContactSuppressed = filteredToEmails.length === 0;
285+
256286
const html = await renderer.render({
257287
shouldReplaceVariableValues: true,
258288
variableValues: {
@@ -265,13 +295,80 @@ async function processContactEmail(jobData: CampaignEmailJob) {
265295
},
266296
});
267297

268-
// Create single email
298+
if (isContactSuppressed) {
299+
// Create suppressed email record
300+
logger.info(
301+
{
302+
contactEmail: contact.email,
303+
campaignId: emailConfig.campaignId,
304+
teamId: emailConfig.teamId,
305+
},
306+
"Contact email is suppressed. Creating suppressed email record."
307+
);
308+
309+
const email = await db.email.create({
310+
data: {
311+
to: toEmails,
312+
replyTo: emailConfig.replyTo,
313+
cc: ccEmails.length > 0 ? ccEmails : undefined,
314+
bcc: bccEmails.length > 0 ? bccEmails : undefined,
315+
from: emailConfig.from,
316+
subject: emailConfig.subject,
317+
html,
318+
text: emailConfig.previewText,
319+
teamId: emailConfig.teamId,
320+
campaignId: emailConfig.campaignId,
321+
contactId: contact.id,
322+
domainId: emailConfig.domainId,
323+
latestStatus: "SUPPRESSED",
324+
},
325+
});
326+
327+
await db.emailEvent.create({
328+
data: {
329+
emailId: email.id,
330+
status: "SUPPRESSED",
331+
data: {
332+
error: "Contact email is suppressed. No email sent.",
333+
},
334+
},
335+
});
336+
337+
return;
338+
}
339+
340+
// Log if any CC/BCC emails were filtered out
341+
if (ccEmails.length > filteredCcEmails.length) {
342+
logger.info(
343+
{
344+
originalCc: ccEmails,
345+
filteredCc: filteredCcEmails,
346+
campaignId: emailConfig.campaignId,
347+
teamId: emailConfig.teamId,
348+
},
349+
"Some CC recipients were suppressed and filtered out from campaign email."
350+
);
351+
}
352+
353+
if (bccEmails.length > filteredBccEmails.length) {
354+
logger.info(
355+
{
356+
originalBcc: bccEmails,
357+
filteredBcc: filteredBccEmails,
358+
campaignId: emailConfig.campaignId,
359+
teamId: emailConfig.teamId,
360+
},
361+
"Some BCC recipients were suppressed and filtered out from campaign email."
362+
);
363+
}
364+
365+
// Create email with filtered recipients
269366
const email = await db.email.create({
270367
data: {
271-
to: [contact.email],
368+
to: filteredToEmails,
272369
replyTo: emailConfig.replyTo,
273-
cc: emailConfig.cc,
274-
bcc: emailConfig.bcc,
370+
cc: filteredCcEmails.length > 0 ? filteredCcEmails : undefined,
371+
bcc: filteredBccEmails.length > 0 ? filteredBccEmails : undefined,
275372
from: emailConfig.from,
276373
subject: emailConfig.subject,
277374
html,

0 commit comments

Comments
 (0)