diff --git a/packages/twenty-server/src/engine/core-modules/emailing-domain/drivers/aws-ses/services/aws-ses-driver.service.ts b/packages/twenty-server/src/engine/core-modules/emailing-domain/drivers/aws-ses/services/aws-ses-driver.service.ts index b8f9154e3a..d512a68acb 100644 --- a/packages/twenty-server/src/engine/core-modules/emailing-domain/drivers/aws-ses/services/aws-ses-driver.service.ts +++ b/packages/twenty-server/src/engine/core-modules/emailing-domain/drivers/aws-ses/services/aws-ses-driver.service.ts @@ -14,14 +14,23 @@ import { PutEmailIdentityDkimAttributesCommand, } from '@aws-sdk/client-sesv2'; +import { isNonEmptyString } from '@sniptt/guards'; + import { type AwsSesDriverConfig } from 'src/engine/core-modules/emailing-domain/drivers/interfaces/driver-config.interface'; import { type EmailingDomainDriverInterface, type EmailingDomainResourceInput, type EmailingDomainVerificationResult, } from 'src/engine/core-modules/emailing-domain/drivers/interfaces/emailing-domain-driver.interface'; -import { type EmailingDomainSendEmailInput } from 'src/engine/core-modules/emailing-domain/drivers/types/emailing-domain-send-email-input.type'; +import { + EmailingDomainDriverException, + EmailingDomainDriverExceptionCode, +} from 'src/engine/core-modules/emailing-domain/drivers/exceptions/emailing-domain-driver.exception'; +import { type EmailingDomainSendEmailRequest } from 'src/engine/core-modules/emailing-domain/drivers/types/emailing-domain-send-email-input.type'; import { type EmailingDomainSendEmailResult } from 'src/engine/core-modules/emailing-domain/drivers/types/emailing-domain-send-email-result.type'; +import { UnsubscribeHostnameStatus } from 'src/engine/core-modules/emailing-domain/drivers/types/unsubscribe-hostname-status.type'; +import { type EmailingDomainEntity } from 'src/engine/core-modules/emailing-domain/emailing-domain.entity'; +import { type UnsubscribeContentService } from 'src/engine/core-modules/emailing-domain/services/unsubscribe-content.service'; import { AWS_SES_RESOURCE_NAME_PREFIX } from 'src/engine/core-modules/emailing-domain/drivers/aws-ses/constants/aws-ses-resource-name-prefix.constant'; import { type AwsSesClientProvider } from 'src/engine/core-modules/emailing-domain/drivers/aws-ses/providers/aws-ses-client.provider'; @@ -40,6 +49,7 @@ export class AwsSesDriver implements EmailingDomainDriverInterface { private readonly awsSesHandleErrorService: AwsSesHandleErrorService, private readonly awsSesRegisterDomainService: AwsSesRegisterDomainService, private readonly awsSesSendEmailService: AwsSesSendEmailService, + private readonly unsubscribeContentService: UnsubscribeContentService, ) {} async verifyDomain( @@ -127,14 +137,35 @@ export class AwsSesDriver implements EmailingDomainDriverInterface { } async sendEmail( - input: EmailingDomainSendEmailInput, + input: EmailingDomainSendEmailRequest, ): Promise { - return this.awsSesSendEmailService.sendEmail(input, { + const unsubscribeBaseUrl = this.getUnsubscribeBaseUrl(input.emailingDomain); + const emailToSend = this.unsubscribeContentService.addTo( + input, + unsubscribeBaseUrl, + ); + + return this.awsSesSendEmailService.sendEmail(emailToSend, { tenantName: this.buildTenantName(input.workspaceId), configurationSetName: this.buildConfigurationSetName(input.workspaceId), }); } + private getUnsubscribeBaseUrl(emailingDomain: EmailingDomainEntity): string { + if ( + emailingDomain.unsubscribeHostnameStatus !== + UnsubscribeHostnameStatus.ACTIVE || + !isNonEmptyString(emailingDomain.unsubscribeHostname) + ) { + throw new EmailingDomainDriverException( + `Cannot send email for ${emailingDomain.domain}: unsubscribe domain is not active (status: ${emailingDomain.unsubscribeHostnameStatus})`, + EmailingDomainDriverExceptionCode.UNSUBSCRIBE_NOT_READY, + ); + } + + return `https://${emailingDomain.unsubscribeHostname}`; + } + async cleanupDomain(input: EmailingDomainResourceInput): Promise { const sesClient = this.awsSesClientProvider.getSESClient(); const tenantName = this.buildTenantName(input.workspaceId); diff --git a/packages/twenty-server/src/engine/core-modules/emailing-domain/drivers/emailing-domain-driver.factory.ts b/packages/twenty-server/src/engine/core-modules/emailing-domain/drivers/emailing-domain-driver.factory.ts index 0e8b7caaa9..63d2ed32fc 100644 --- a/packages/twenty-server/src/engine/core-modules/emailing-domain/drivers/emailing-domain-driver.factory.ts +++ b/packages/twenty-server/src/engine/core-modules/emailing-domain/drivers/emailing-domain-driver.factory.ts @@ -10,6 +10,7 @@ import { AwsSesHandleErrorService } from 'src/engine/core-modules/emailing-domai import { AwsSesSendEmailService } from 'src/engine/core-modules/emailing-domain/drivers/aws-ses/services/aws-ses-send-email.service'; import { LogEmailingDomainDriver } from 'src/engine/core-modules/emailing-domain/drivers/log/services/log-emailing-domain-driver.service'; import { EmailingDomainDriver } from 'src/engine/core-modules/emailing-domain/drivers/types/emailing-domain-driver.type'; +import { UnsubscribeContentService } from 'src/engine/core-modules/emailing-domain/services/unsubscribe-content.service'; import { DriverFactoryBase } from 'src/engine/core-modules/twenty-config/dynamic-factory.base'; import { ConfigVariablesGroup } from 'src/engine/core-modules/twenty-config/enums/config-variables-group.enum'; import { ConfigGroupHashService } from 'src/engine/core-modules/twenty-config/services/config-group-hash.service'; @@ -25,6 +26,7 @@ export class EmailingDomainDriverFactory extends DriverFactoryBase; cleanupDomain(input: EmailingDomainResourceInput): Promise; sendEmail( - input: EmailingDomainSendEmailInput, + input: EmailingDomainSendEmailRequest, ): Promise; } diff --git a/packages/twenty-server/src/engine/core-modules/emailing-domain/drivers/log/services/log-emailing-domain-driver.service.ts b/packages/twenty-server/src/engine/core-modules/emailing-domain/drivers/log/services/log-emailing-domain-driver.service.ts index 5b7ee9e06c..6256acbe97 100644 --- a/packages/twenty-server/src/engine/core-modules/emailing-domain/drivers/log/services/log-emailing-domain-driver.service.ts +++ b/packages/twenty-server/src/engine/core-modules/emailing-domain/drivers/log/services/log-emailing-domain-driver.service.ts @@ -1,20 +1,34 @@ import { Injectable, Logger } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { isNonEmptyString } from '@sniptt/guards'; +import { Repository } from 'typeorm'; import { v4 } from 'uuid'; +import { UNSUBSCRIBE_HOSTNAME_PREFIX } from 'src/engine/core-modules/emailing-domain/constants/unsubscribe-hostname-prefix.constant'; import { type EmailingDomainDriverInterface, type EmailingDomainResourceInput, type EmailingDomainVerificationResult, } from 'src/engine/core-modules/emailing-domain/drivers/interfaces/emailing-domain-driver.interface'; import { EmailingDomainStatus } from 'src/engine/core-modules/emailing-domain/drivers/types/emailing-domain-status.type'; -import { type EmailingDomainSendEmailInput } from 'src/engine/core-modules/emailing-domain/drivers/types/emailing-domain-send-email-input.type'; +import { type EmailingDomainSendEmailRequest } from 'src/engine/core-modules/emailing-domain/drivers/types/emailing-domain-send-email-input.type'; import { type EmailingDomainSendEmailResult } from 'src/engine/core-modules/emailing-domain/drivers/types/emailing-domain-send-email-result.type'; +import { UnsubscribeContentService } from 'src/engine/core-modules/emailing-domain/services/unsubscribe-content.service'; +import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; +import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; @Injectable() export class LogEmailingDomainDriver implements EmailingDomainDriverInterface { private readonly logger = new Logger(LogEmailingDomainDriver.name); + constructor( + private readonly twentyConfigService: TwentyConfigService, + private readonly unsubscribeContentService: UnsubscribeContentService, + @InjectRepository(WorkspaceEntity) + private readonly workspaceRepository: Repository, + ) {} + async provisionWorkspace(workspaceId: string): Promise { this.logger.log(`[log-driver] provisionWorkspace(${workspaceId})`); } @@ -56,21 +70,59 @@ export class LogEmailingDomainDriver implements EmailingDomainDriverInterface { } async sendEmail( - input: EmailingDomainSendEmailInput, + input: EmailingDomainSendEmailRequest, ): Promise { + const unsubscribeBaseUrl = await this.getUnsubscribeBaseUrl( + input.workspaceId, + ); + const emailToSend = this.unsubscribeContentService.addTo( + input, + unsubscribeBaseUrl, + ); + const messageId = `log-${v4()}`; + const listUnsubscribe = emailToSend.headers?.find( + (header) => header.name === 'List-Unsubscribe', + )?.value; + this.logger.log( - `[log-driver] sendEmail from=${input.from} to=${input.to.join(',')} subject="${input.subject}" → fake messageId=${messageId}`, + `[log-driver] sendEmail → fake messageId=${messageId}\n` + + `From: ${emailToSend.from}\n` + + `To: ${emailToSend.to.join(',')}\n` + + `Subject: ${emailToSend.subject}\n` + + `List-Unsubscribe: ${listUnsubscribe ?? '(none)'}\n` + + `Content Text: ${emailToSend.text}\n` + + `Content HTML: ${emailToSend.html ?? '(none)'}`, ); return { messageId, deliveredRecipients: { - to: input.to, - cc: input.cc ?? [], - bcc: input.bcc ?? [], + to: emailToSend.to, + cc: emailToSend.cc ?? [], + bcc: emailToSend.bcc ?? [], }, }; } + + private async getUnsubscribeBaseUrl( + workspaceId: string, + ): Promise { + const workspace = await this.workspaceRepository.findOneBy({ + id: workspaceId, + }); + + if (!isNonEmptyString(workspace?.subdomain)) { + return null; + } + + const baseUrl = new URL(this.twentyConfigService.get('SERVER_URL')); + + baseUrl.hostname = this.twentyConfigService.get('IS_MULTIWORKSPACE_ENABLED') + ? `${UNSUBSCRIBE_HOSTNAME_PREFIX}.${workspace.subdomain}.${baseUrl.hostname}` + : `${UNSUBSCRIBE_HOSTNAME_PREFIX}.${baseUrl.hostname}`; + + return baseUrl.origin; + } } diff --git a/packages/twenty-server/src/engine/core-modules/emailing-domain/drivers/types/emailing-domain-send-email-input.type.ts b/packages/twenty-server/src/engine/core-modules/emailing-domain/drivers/types/emailing-domain-send-email-input.type.ts index a49c288d8d..2aa1579d0d 100644 --- a/packages/twenty-server/src/engine/core-modules/emailing-domain/drivers/types/emailing-domain-send-email-input.type.ts +++ b/packages/twenty-server/src/engine/core-modules/emailing-domain/drivers/types/emailing-domain-send-email-input.type.ts @@ -1,6 +1,11 @@ import { type EmailingDomainEmailContent } from 'src/engine/core-modules/emailing-domain/drivers/types/emailing-domain-email-content.type'; +import { type EmailingDomainEntity } from 'src/engine/core-modules/emailing-domain/emailing-domain.entity'; export type EmailingDomainSendEmailInput = EmailingDomainEmailContent & { workspaceId: string; domain: string; }; + +export type EmailingDomainSendEmailRequest = EmailingDomainSendEmailInput & { + emailingDomain: EmailingDomainEntity; +}; diff --git a/packages/twenty-server/src/engine/core-modules/emailing-domain/emailing-domain.module.ts b/packages/twenty-server/src/engine/core-modules/emailing-domain/emailing-domain.module.ts index 06d071cd08..489d2ff523 100644 --- a/packages/twenty-server/src/engine/core-modules/emailing-domain/emailing-domain.module.ts +++ b/packages/twenty-server/src/engine/core-modules/emailing-domain/emailing-domain.module.ts @@ -1,4 +1,5 @@ import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm'; @@ -16,16 +17,19 @@ import { EmailingDomainResolver } from 'src/engine/core-modules/emailing-domain/ import { EmailingDomainWorkspaceCleanupJob } from 'src/engine/core-modules/emailing-domain/jobs/emailing-domain-workspace-cleanup.job'; import { EmailingDomainTenantStatusService } from 'src/engine/core-modules/emailing-domain/services/emailing-domain-tenant-status.service'; import { EmailingDomainService } from 'src/engine/core-modules/emailing-domain/services/emailing-domain.service'; +import { UnsubscribeContentService } from 'src/engine/core-modules/emailing-domain/services/unsubscribe-content.service'; import { UnsubscribeHostnameService } from 'src/engine/core-modules/emailing-domain/services/unsubscribe-hostname.service'; import { UnsubscribeTokenService } from 'src/engine/core-modules/emailing-domain/services/unsubscribe-token.service'; import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module'; import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module'; import { SecretEncryptionModule } from 'src/engine/core-modules/secret-encryption/secret-encryption.module'; +import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { provideWorkspaceScopedRepository } from 'src/engine/twenty-orm/workspace-scoped-repository/provide-workspace-scoped-repository'; @Module({ imports: [ TypeORMModule, + TypeOrmModule.forFeature([WorkspaceEntity]), NestjsQueryTypeOrmModule.forFeature([EmailingDomainEntity]), FeatureFlagModule, PermissionsModule, @@ -42,6 +46,7 @@ import { provideWorkspaceScopedRepository } from 'src/engine/twenty-orm/workspac EmailingDomainService, EmailingDomainTenantStatusService, UnsubscribeTokenService, + UnsubscribeContentService, UnsubscribeHostnameService, EmailingDomainResolver, EmailingDomainDriverFactory, diff --git a/packages/twenty-server/src/engine/core-modules/emailing-domain/services/unsubscribe-content.service.ts b/packages/twenty-server/src/engine/core-modules/emailing-domain/services/unsubscribe-content.service.ts new file mode 100644 index 0000000000..22c593bbb9 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/emailing-domain/services/unsubscribe-content.service.ts @@ -0,0 +1,52 @@ +import { Injectable } from '@nestjs/common'; + +import { isNonEmptyString } from '@sniptt/guards'; + +import { type EmailingDomainSendEmailInput } from 'src/engine/core-modules/emailing-domain/drivers/types/emailing-domain-send-email-input.type'; +import { UnsubscribeTokenService } from 'src/engine/core-modules/emailing-domain/services/unsubscribe-token.service'; +import { buildUnsubscribeHeaders } from 'src/engine/core-modules/emailing-domain/utils/build-unsubscribe-headers.util'; +import { buildUnsubscribeHtmlFooter } from 'src/engine/core-modules/emailing-domain/utils/build-unsubscribe-html-footer.util'; +import { buildUnsubscribeTextFooter } from 'src/engine/core-modules/emailing-domain/utils/build-unsubscribe-text-footer.util'; +import { buildUnsubscribeUrls } from 'src/engine/core-modules/emailing-domain/utils/build-unsubscribe-urls.util'; + +@Injectable() +export class UnsubscribeContentService { + constructor( + private readonly unsubscribeTokenService: UnsubscribeTokenService, + ) {} + + addTo( + email: EmailingDomainSendEmailInput, + unsubscribeBaseUrl: string | null, + ): EmailingDomainSendEmailInput { + if (!isNonEmptyString(unsubscribeBaseUrl)) { + return email; + } + + const token = this.unsubscribeTokenService.sign({ + workspaceId: email.workspaceId, + emailAddress: email.to[0], + ...(isNonEmptyString(email.unsubscribeTopicId) + ? { unsubscribeTopicId: email.unsubscribeTopicId } + : {}), + }); + + const unsubscribeUrls = buildUnsubscribeUrls({ + unsubscribeBaseUrl, + domain: email.domain, + token, + }); + + return { + ...email, + text: `${email.text}${buildUnsubscribeTextFooter(unsubscribeUrls.webUrl)}`, + html: isNonEmptyString(email.html) + ? `${email.html}${buildUnsubscribeHtmlFooter(unsubscribeUrls.webUrl)}` + : email.html, + headers: [ + ...(email.headers ?? []), + ...buildUnsubscribeHeaders(unsubscribeUrls), + ], + }; + } +} diff --git a/packages/twenty-server/src/engine/core-modules/emailing-domain/types/unsubscribe-urls.type.ts b/packages/twenty-server/src/engine/core-modules/emailing-domain/types/unsubscribe-urls.type.ts index ee9555197c..1c7362cf20 100644 --- a/packages/twenty-server/src/engine/core-modules/emailing-domain/types/unsubscribe-urls.type.ts +++ b/packages/twenty-server/src/engine/core-modules/emailing-domain/types/unsubscribe-urls.type.ts @@ -1,4 +1,4 @@ export type UnsubscribeUrls = { - httpsUrl: string; + webUrl: string; mailtoUrl: string; }; diff --git a/packages/twenty-server/src/engine/core-modules/emailing-domain/utils/build-unsubscribe-headers.util.ts b/packages/twenty-server/src/engine/core-modules/emailing-domain/utils/build-unsubscribe-headers.util.ts index fd5f8c6839..ffd03a4795 100644 --- a/packages/twenty-server/src/engine/core-modules/emailing-domain/utils/build-unsubscribe-headers.util.ts +++ b/packages/twenty-server/src/engine/core-modules/emailing-domain/utils/build-unsubscribe-headers.util.ts @@ -2,9 +2,9 @@ import { type EmailingDomainHeader } from 'src/engine/core-modules/emailing-doma import { type UnsubscribeUrls } from 'src/engine/core-modules/emailing-domain/types/unsubscribe-urls.type'; export const buildUnsubscribeHeaders = ({ - httpsUrl, + webUrl, mailtoUrl, }: UnsubscribeUrls): EmailingDomainHeader[] => [ - { name: 'List-Unsubscribe', value: `<${httpsUrl}>, <${mailtoUrl}>` }, + { name: 'List-Unsubscribe', value: `<${webUrl}>, <${mailtoUrl}>` }, { name: 'List-Unsubscribe-Post', value: 'List-Unsubscribe=One-Click' }, ]; diff --git a/packages/twenty-server/src/engine/core-modules/emailing-domain/utils/build-unsubscribe-html-footer.util.ts b/packages/twenty-server/src/engine/core-modules/emailing-domain/utils/build-unsubscribe-html-footer.util.ts index 9b4cbfb2a6..c679d427a7 100644 --- a/packages/twenty-server/src/engine/core-modules/emailing-domain/utils/build-unsubscribe-html-footer.util.ts +++ b/packages/twenty-server/src/engine/core-modules/emailing-domain/utils/build-unsubscribe-html-footer.util.ts @@ -1,2 +1,2 @@ -export const buildUnsubscribeHtmlFooter = (httpsUrl: string): string => - `

Don't want these emails? Unsubscribe.

`; +export const buildUnsubscribeHtmlFooter = (webUrl: string): string => + `

Don't want these emails? Unsubscribe.

`; diff --git a/packages/twenty-server/src/engine/core-modules/emailing-domain/utils/build-unsubscribe-text-footer.util.ts b/packages/twenty-server/src/engine/core-modules/emailing-domain/utils/build-unsubscribe-text-footer.util.ts index 6bcf23481b..54213e1b83 100644 --- a/packages/twenty-server/src/engine/core-modules/emailing-domain/utils/build-unsubscribe-text-footer.util.ts +++ b/packages/twenty-server/src/engine/core-modules/emailing-domain/utils/build-unsubscribe-text-footer.util.ts @@ -1,2 +1,2 @@ -export const buildUnsubscribeTextFooter = (httpsUrl: string): string => - `\n\n--\nUnsubscribe: ${httpsUrl}`; +export const buildUnsubscribeTextFooter = (webUrl: string): string => + `\n\n--\nUnsubscribe: ${webUrl}`; diff --git a/packages/twenty-server/src/engine/core-modules/emailing-domain/utils/build-unsubscribe-urls.util.ts b/packages/twenty-server/src/engine/core-modules/emailing-domain/utils/build-unsubscribe-urls.util.ts index b8f268c848..ad5b07506c 100644 --- a/packages/twenty-server/src/engine/core-modules/emailing-domain/utils/build-unsubscribe-urls.util.ts +++ b/packages/twenty-server/src/engine/core-modules/emailing-domain/utils/build-unsubscribe-urls.util.ts @@ -2,16 +2,16 @@ import { UNSUBSCRIBE_MAILBOX_LOCAL_PART } from 'src/engine/core-modules/emailing import { type UnsubscribeUrls } from 'src/engine/core-modules/emailing-domain/types/unsubscribe-urls.type'; type BuildUnsubscribeUrlsArgs = { - unsubscribeHostname: string; + unsubscribeBaseUrl: string; domain: string; token: string; }; export const buildUnsubscribeUrls = ({ - unsubscribeHostname, + unsubscribeBaseUrl, domain, token, }: BuildUnsubscribeUrlsArgs): UnsubscribeUrls => ({ - httpsUrl: `https://${unsubscribeHostname}/emailing/unsubscribe?t=${token}`, + webUrl: `${unsubscribeBaseUrl}/emailing/unsubscribe?t=${token}`, mailtoUrl: `mailto:${UNSUBSCRIBE_MAILBOX_LOCAL_PART}@${domain}?subject=${token}`, }); diff --git a/packages/twenty-server/src/modules/emailing/services/emailing-domain-sender.service.ts b/packages/twenty-server/src/modules/emailing/services/emailing-domain-sender.service.ts index 6e00ad7c8b..5fe34048b1 100644 --- a/packages/twenty-server/src/modules/emailing/services/emailing-domain-sender.service.ts +++ b/packages/twenty-server/src/modules/emailing/services/emailing-domain-sender.service.ts @@ -5,7 +5,6 @@ import { isNonEmptyString } from '@sniptt/guards'; import { MessageChannelType } from 'twenty-shared/types'; import { Repository } from 'typeorm'; -import { EMPTY_UNSUBSCRIBE_CONTENT } from 'src/engine/core-modules/emailing-domain/constants/empty-unsubscribe-content.constant'; import { EmailingDomainDriverException, EmailingDomainDriverExceptionCode, @@ -14,21 +13,12 @@ import { EmailingDomainDriverFactory } from 'src/engine/core-modules/emailing-do import { EmailingDomainStatus } from 'src/engine/core-modules/emailing-domain/drivers/types/emailing-domain-status.type'; import { EmailingDomainTenantStatus } from 'src/engine/core-modules/emailing-domain/drivers/types/emailing-domain-tenant-status.type'; import { type EmailingDomainEmailContent } from 'src/engine/core-modules/emailing-domain/drivers/types/emailing-domain-email-content.type'; -import { type EmailingDomainSendEmailInput } from 'src/engine/core-modules/emailing-domain/drivers/types/emailing-domain-send-email-input.type'; +import { type EmailingDomainSendEmailRequest } from 'src/engine/core-modules/emailing-domain/drivers/types/emailing-domain-send-email-input.type'; import { type EmailingDomainSendEmailResult } from 'src/engine/core-modules/emailing-domain/drivers/types/emailing-domain-send-email-result.type'; -import { UnsubscribeHostnameStatus } from 'src/engine/core-modules/emailing-domain/drivers/types/unsubscribe-hostname-status.type'; -import { EmailingDomainDriver } from 'src/engine/core-modules/emailing-domain/drivers/types/emailing-domain-driver.type'; import { EmailingDomainEntity } from 'src/engine/core-modules/emailing-domain/emailing-domain.entity'; import { MessageSuppressionService } from 'src/modules/emailing/services/message-suppression.service'; -import { UnsubscribeTokenService } from 'src/engine/core-modules/emailing-domain/services/unsubscribe-token.service'; import { MessageChannelEntity } from 'src/engine/metadata-modules/message-channel/entities/message-channel.entity'; -import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; import { type DeliverableRecipients } from 'src/engine/core-modules/emailing-domain/types/deliverable-recipients.type'; -import { type UnsubscribeContent } from 'src/engine/core-modules/emailing-domain/types/unsubscribe-content.type'; -import { buildUnsubscribeHeaders } from 'src/engine/core-modules/emailing-domain/utils/build-unsubscribe-headers.util'; -import { buildUnsubscribeHtmlFooter } from 'src/engine/core-modules/emailing-domain/utils/build-unsubscribe-html-footer.util'; -import { buildUnsubscribeTextFooter } from 'src/engine/core-modules/emailing-domain/utils/build-unsubscribe-text-footer.util'; -import { buildUnsubscribeUrls } from 'src/engine/core-modules/emailing-domain/utils/build-unsubscribe-urls.util'; import { getDomainFromEmail } from 'src/utils/get-domain-from-email'; import { InjectWorkspaceScopedRepository } from 'src/engine/twenty-orm/workspace-scoped-repository/inject-workspace-scoped-repository.decorator'; import { WorkspaceScopedRepository } from 'src/engine/twenty-orm/workspace-scoped-repository/workspace-scoped-repository'; @@ -41,8 +31,6 @@ export class EmailingDomainSenderService { private readonly emailingDomainRepository: WorkspaceScopedRepository, private readonly emailingDomainDriverFactory: EmailingDomainDriverFactory, private readonly messageSuppressionService: MessageSuppressionService, - private readonly unsubscribeTokenService: UnsubscribeTokenService, - private readonly twentyConfigService: TwentyConfigService, @InjectRepository(MessageChannelEntity) private readonly messageChannelRepository: Repository, ) {} @@ -65,31 +53,18 @@ export class EmailingDomainSenderService { emailContent, ); - const unsubscribe = this.buildUnsubscribeContent( - workspaceId, - emailingDomain, - recipients.to[0], - emailContent.unsubscribeTopicId, - ); - const replyTo = await this.resolveReplyTo(workspaceId, emailContent); - const emailToSend = { + const emailToSend: EmailingDomainSendEmailRequest = { + ...emailContent, workspaceId, domain: emailingDomain.domain, - from: emailContent.from, + emailingDomain, replyTo, to: recipients.to, cc: recipients.cc, bcc: recipients.bcc, - subject: emailContent.subject, - text: `${emailContent.text}${unsubscribe.textFooter}`, - html: isNonEmptyString(emailContent.html) - ? `${emailContent.html}${unsubscribe.htmlFooter}` - : emailContent.html, - attachments: emailContent.attachments, - headers: [...(emailContent.headers ?? []), ...unsubscribe.headers], - } as EmailingDomainSendEmailInput; + }; return this.emailingDomainDriverFactory .getCurrentDriver() @@ -230,48 +205,4 @@ export class EmailingDomainSenderService { unsubscribeTopicId, ); } - - private buildUnsubscribeContent( - workspaceId: string, - emailingDomain: EmailingDomainEntity, - primaryRecipient: string, - unsubscribeTopicId: string | undefined, - ): UnsubscribeContent { - const isDemoMode = - this.twentyConfigService.get('EMAILING_DOMAIN_DRIVER') === - EmailingDomainDriver.LOG; - - if (isDemoMode) { - return EMPTY_UNSUBSCRIBE_CONTENT; - } - - if ( - emailingDomain.unsubscribeHostnameStatus !== - UnsubscribeHostnameStatus.ACTIVE || - !isNonEmptyString(emailingDomain.unsubscribeHostname) - ) { - throw new EmailingDomainDriverException( - `Cannot send email for ${emailingDomain.domain}: unsubscribe domain is not active (status: ${emailingDomain.unsubscribeHostnameStatus})`, - EmailingDomainDriverExceptionCode.UNSUBSCRIBE_NOT_READY, - ); - } - - const token = this.unsubscribeTokenService.sign({ - workspaceId, - emailAddress: primaryRecipient, - ...(isNonEmptyString(unsubscribeTopicId) ? { unsubscribeTopicId } : {}), - }); - - const unsubscribeUrls = buildUnsubscribeUrls({ - unsubscribeHostname: emailingDomain.unsubscribeHostname, - domain: emailingDomain.domain, - token, - }); - - return { - headers: buildUnsubscribeHeaders(unsubscribeUrls), - textFooter: buildUnsubscribeTextFooter(unsubscribeUrls.httpsUrl), - htmlFooter: buildUnsubscribeHtmlFooter(unsubscribeUrls.httpsUrl), - }; - } }