diff --git a/apps/server/src/integrations/environment/environment.service.ts b/apps/server/src/integrations/environment/environment.service.ts index d9ef01abb..abee1966f 100644 --- a/apps/server/src/integrations/environment/environment.service.ts +++ b/apps/server/src/integrations/environment/environment.service.ts @@ -134,6 +134,17 @@ export class EnvironmentService { return this.configService.get('MAIL_FROM_NAME', 'Docmost'); } + getMailBlockedRecipientDomains(): string[] { + const raw = this.configService.get( + 'MAIL_BLOCKED_RECIPIENT_DOMAINS', + '', + ); + return raw + .split(',') + .map((d) => d.trim().toLowerCase()) + .filter(Boolean); + } + getSmtpHost(): string { return this.configService.get('SMTP_HOST'); } diff --git a/apps/server/src/integrations/mail/mail.service.ts b/apps/server/src/integrations/mail/mail.service.ts index dd71cb559..22f6916f5 100644 --- a/apps/server/src/integrations/mail/mail.service.ts +++ b/apps/server/src/integrations/mail/mail.service.ts @@ -17,6 +17,10 @@ export class MailService { ) {} async sendEmail(message: MailMessage): Promise { + if (this.isRecipientBlocked(message.to)) { + return; + } + if (message.template) { // in case this method is used directly. we do not send the tsx template from queue message.html = await render(message.template, { @@ -35,6 +39,10 @@ export class MailService { } async sendToQueue(message: MailMessage): Promise { + if (this.isRecipientBlocked(message.to)) { + return; + } + if (message.template) { // transform the React object because it gets lost when sent via the queue message.html = await render(message.template, { @@ -47,4 +55,11 @@ export class MailService { } await this.emailQueue.add(QueueJob.SEND_EMAIL, message); } + + private isRecipientBlocked(to: string): boolean { + const blocked = this.environmentService.getMailBlockedRecipientDomains(); + if (blocked.length === 0) return false; + const domain = to?.split('@')[1]?.toLowerCase(); + return !!domain && blocked.includes(domain); + } }