diff --git a/.env.example b/.env.example index 227d7d00b2..773b5d9959 100644 --- a/.env.example +++ b/.env.example @@ -6,6 +6,7 @@ RESEND_API_KEY="" # API key from Resend for email authentication / invites RESEND_FROM_MARKETING="" RESEND_FROM_SYSTEM="" RESEND_FROM_DEFAULT="" +RESEND_FROM_TRUST_PORTAL="" # Sender for Trust Portal access/NDA emails (falls back to RESEND_FROM_SYSTEM) RESEND_TO_TEST="" RESEND_REPLY_TO_MARKETING="" REVALIDATION_SECRET="" # openssl rand -base64 32 diff --git a/apps/api/.env.example b/apps/api/.env.example index e2fea0ab10..9e8714d2b7 100644 --- a/apps/api/.env.example +++ b/apps/api/.env.example @@ -44,6 +44,7 @@ GROQ_API_KEY= RESEND_API_KEY= RESEND_FROM_SYSTEM= # e.g., noreply@mail.trycomp.ai RESEND_FROM_DEFAULT= # e.g., hello@mail.trycomp.ai +RESEND_FROM_TRUST_PORTAL= # e.g., Comp AI — sender for Trust Portal access/NDA emails (falls back to RESEND_FROM_SYSTEM) # Background checks BACKGROUND_CHECK_API_BASE_URL=https://glad-sturgeon-729.convex.site diff --git a/apps/api/src/email/email.controller.ts b/apps/api/src/email/email.controller.ts index 7acb18aa71..2b15718d12 100644 --- a/apps/api/src/email/email.controller.ts +++ b/apps/api/src/email/email.controller.ts @@ -29,15 +29,12 @@ export class EmailController { }) @ApiResponse({ status: 200, description: 'Email task triggered' }) async sendEmail(@Body() dto: SendEmailDto) { - const fromAddress = dto.system - ? (process.env.RESEND_FROM_SYSTEM ?? process.env.RESEND_FROM_DEFAULT) - : (dto.from ?? process.env.RESEND_FROM_DEFAULT); - const handle = await tasks.trigger('send-email', { to: dto.to, subject: dto.subject, html: dto.html, - from: fromAddress, + from: dto.from, + channel: dto.system ? 'system' : 'default', cc: dto.cc, scheduledAt: dto.scheduledAt, attachments: dto.attachments, diff --git a/apps/api/src/email/trigger-email.ts b/apps/api/src/email/trigger-email.ts index 735c891825..79866968e2 100644 --- a/apps/api/src/email/trigger-email.ts +++ b/apps/api/src/email/trigger-email.ts @@ -1,15 +1,29 @@ import { render } from '@react-email/render'; import { tasks } from '@trigger.dev/sdk'; import type { ReactElement } from 'react'; -import type { sendEmailTask } from '../trigger/email/send-email'; +import type { EmailChannel, sendEmailTask } from '../trigger/email/send-email'; import type { EmailAttachment } from './resend'; +type TriggerEmailFlags = { + marketing?: boolean; + system?: boolean; + trustPortal?: boolean; +}; + +function resolveChannel(flags: TriggerEmailFlags): EmailChannel { + if (flags.trustPortal) return 'trustPortal'; + if (flags.marketing) return 'marketing'; + if (flags.system) return 'system'; + return 'default'; +} + export async function triggerEmail(params: { to: string; subject: string; react: ReactElement; marketing?: boolean; system?: boolean; + trustPortal?: boolean; cc?: string | string[]; scheduledAt?: string; attachments?: EmailAttachment[]; @@ -17,21 +31,13 @@ export async function triggerEmail(params: { try { const html = await render(params.react); - const fromMarketing = process.env.RESEND_FROM_MARKETING; - const fromSystem = process.env.RESEND_FROM_SYSTEM; - const fromDefault = process.env.RESEND_FROM_DEFAULT; - - const fromAddress = params.marketing - ? fromMarketing - : params.system - ? fromSystem - : fromDefault; + const channel = resolveChannel(params); const handle = await tasks.trigger('send-email', { to: params.to, subject: params.subject, html, - from: fromAddress ?? undefined, + channel, cc: params.cc, scheduledAt: params.scheduledAt, attachments: params.attachments?.map((att) => ({ diff --git a/apps/api/src/trigger/email/send-email.ts b/apps/api/src/trigger/email/send-email.ts index 212e87f1d4..d01181cee2 100644 --- a/apps/api/src/trigger/email/send-email.ts +++ b/apps/api/src/trigger/email/send-email.ts @@ -8,6 +8,36 @@ const emailQueue = queue({ concurrencyLimit: 10, }); +export const emailChannelSchema = z.enum([ + 'marketing', + 'system', + 'trustPortal', + 'default', +]); +export type EmailChannel = z.infer; + +function resolveFromAddressForChannel( + channel: EmailChannel | undefined, +): string | undefined { + const fromMarketing = process.env.RESEND_FROM_MARKETING; + const fromSystem = process.env.RESEND_FROM_SYSTEM; + const fromDefault = process.env.RESEND_FROM_DEFAULT; + const fromTrustPortal = process.env.RESEND_FROM_TRUST_PORTAL; + + switch (channel) { + case 'trustPortal': + return fromTrustPortal ?? fromSystem; + case 'marketing': + return fromMarketing; + case 'system': + return fromSystem; + case 'default': + return fromDefault; + default: + return undefined; + } +} + export const sendEmailTask = schemaTask({ id: 'send-email', queue: emailQueue, @@ -18,6 +48,7 @@ export const sendEmailTask = schemaTask({ to: z.string(), subject: z.string(), html: z.string(), + channel: emailChannelSchema.optional(), from: z.string().optional(), cc: z.union([z.string(), z.array(z.string())]).optional(), scheduledAt: z.string().optional(), @@ -40,11 +71,15 @@ export const sendEmailTask = schemaTask({ throw new Error('Resend not initialized - missing API key'); } + const toTest = process.env.RESEND_TO_TEST; const fromSystem = process.env.RESEND_FROM_SYSTEM; const fromDefault = process.env.RESEND_FROM_DEFAULT; - const toTest = process.env.RESEND_TO_TEST; - const fromAddress = params.from ?? fromSystem ?? fromDefault; + const fromAddress = + params.from ?? + resolveFromAddressForChannel(params.channel) ?? + fromSystem ?? + fromDefault; const toAddress = toTest ?? params.to; if (!fromAddress) { diff --git a/apps/api/src/trust-portal/email.service.ts b/apps/api/src/trust-portal/email.service.ts index 89d6809ad0..123ac32b60 100644 --- a/apps/api/src/trust-portal/email.service.ts +++ b/apps/api/src/trust-portal/email.service.ts @@ -25,7 +25,7 @@ export class TrustEmailService { organizationName, ndaSigningLink, }), - system: true, + trustPortal: true, }); this.logger.log(`NDA signing email sent to ${toEmail} (ID: ${id})`); @@ -49,7 +49,7 @@ export class TrustEmailService { expiresAt, portalUrl, }), - system: true, + trustPortal: true, }); this.logger.log(`Access granted email sent to ${toEmail} (ID: ${id})`); @@ -73,7 +73,7 @@ export class TrustEmailService { accessLink, expiresAt, }), - system: true, + trustPortal: true, }); this.logger.log(`Access reclaim email sent to ${toEmail} (ID: ${id})`); @@ -115,7 +115,7 @@ export class TrustEmailService { requestedDurationDays, reviewUrl, }), - system: true, + trustPortal: true, }); this.logger.log(