-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
6658 workflows add a first twenty piece email sender #6965
Changes from 11 commits
6339a4e
e484c60
4528242
cb33021
2e44781
4978270
52ac80d
d030c01
5b50565
77e78a8
2f4a6de
fa03414
8fca876
4bcb226
f99d805
98fdcfb
3e74a5d
5726382
611b4ca
c8b0b4a
b3bde02
6ecd69d
d0d9376
7afe454
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { BaseEmail } from 'src/components/BaseEmail'; | ||
import { Title } from 'src/components/Title'; | ||
import { CallToAction } from 'src/components/CallToAction'; | ||
|
||
type WorkflowActionEmailProps = { | ||
dangerousHTML?: string; | ||
title?: string; | ||
callToAction?: { | ||
value: string; | ||
href: string; | ||
}; | ||
}; | ||
export const WorkflowActionEmail = ({ | ||
dangerousHTML, | ||
title, | ||
callToAction, | ||
}: WorkflowActionEmailProps) => { | ||
return ( | ||
<BaseEmail> | ||
{title && <Title value={title} />} | ||
{dangerousHTML && ( | ||
<div dangerouslySetInnerHTML={{ __html: dangerousHTML }} /> | ||
)} | ||
{callToAction && ( | ||
<CallToAction value={callToAction.value} href={callToAction.href} /> | ||
)} | ||
</BaseEmail> | ||
); | ||
}; |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import { Injectable, Logger } from '@nestjs/common'; | ||
|
||
import { z } from 'zod'; | ||
import Handlebars from 'handlebars'; | ||
import { JSDOM } from 'jsdom'; | ||
import DOMPurify from 'dompurify'; | ||
import { WorkflowActionEmail } from 'twenty-emails'; | ||
import { render } from '@react-email/components'; | ||
|
||
import { WorkflowStepResult } from 'src/modules/workflow/common/types/workflow-step-result.type'; | ||
import { WorkflowSendEmailStep } from 'src/modules/workflow/common/types/workflow-step.type'; | ||
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service'; | ||
import { EmailService } from 'src/engine/integrations/email/email.service'; | ||
|
||
@Injectable() | ||
export class SendEmailActionExecutorFactory { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is not a factory to me : a factory is providing an implementation of an abstract service. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, I felt the same, but it is done like that everywhere else in the project, so I did the same There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
private readonly logger = new Logger(SendEmailActionExecutorFactory.name); | ||
constructor( | ||
private readonly environmentService: EnvironmentService, | ||
private readonly emailService: EmailService, | ||
) {} | ||
|
||
async execute({ | ||
step, | ||
payload, | ||
}: { | ||
step: WorkflowSendEmailStep; | ||
payload: { | ||
email: string; | ||
[key: string]: string; | ||
}; | ||
}): Promise<WorkflowStepResult> { | ||
try { | ||
const emailSchema = z.string().trim().email('Invalid email'); | ||
|
||
const result = emailSchema.safeParse(payload.email); | ||
|
||
if (!result.success) { | ||
this.logger.warn(`Email '${payload.email}' invalid`); | ||
|
||
return { data: { success: false } }; | ||
} | ||
|
||
const mainText = Handlebars.compile(step.settings.template)(payload); | ||
|
||
const window = new JSDOM('').window; | ||
const purify = DOMPurify(window); | ||
const safeHTML = purify.sanitize(mainText || ''); | ||
|
||
const email = WorkflowActionEmail({ | ||
dangerousHTML: safeHTML, | ||
title: step.settings.title, | ||
callToAction: step.settings.callToAction, | ||
}); | ||
const html = render(email, { | ||
pretty: true, | ||
}); | ||
const text = render(email, { | ||
plainText: true, | ||
}); | ||
|
||
await this.emailService.send({ | ||
from: `${this.environmentService.get( | ||
'EMAIL_FROM_NAME', | ||
)} <${this.environmentService.get('EMAIL_FROM_ADDRESS')}>`, | ||
to: payload.email, | ||
subject: step.settings.subject || '', | ||
text, | ||
html, | ||
}); | ||
|
||
return { data: { success: true } }; | ||
} catch (error) { | ||
return { error }; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we need this library? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To resolve the data injection in the html template we provide to the payload
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
got it!