2023-01-27 03:22:44 -08:00
import type {
IDataObject ,
IExecuteFunctions ,
INodeExecutionData ,
INodeProperties ,
2023-05-19 05:31:02 -07:00
JsonObject ,
2023-01-27 03:22:44 -08:00
} from 'n8n-workflow' ;
2023-05-19 05:31:02 -07:00
import { NodeApiError } from 'n8n-workflow' ;
2023-01-24 02:32:31 -08:00
import { createTransport } from 'nodemailer' ;
2023-01-27 03:22:44 -08:00
import type SMTPTransport from 'nodemailer/lib/smtp-transport' ;
2023-01-24 02:32:31 -08:00
2023-06-22 07:47:28 -07:00
import { updateDisplayOptions } from '@utils/utilities' ;
2023-01-24 02:32:31 -08:00
const properties : INodeProperties [ ] = [
// TODO: Add choice for text as text or html (maybe also from name)
{
displayName : 'From Email' ,
name : 'fromEmail' ,
type : 'string' ,
default : '' ,
required : true ,
placeholder : 'admin@example.com' ,
description :
'Email address of the sender. You can also specify a name: Nathan Doe <nate@n8n.io>.' ,
} ,
{
displayName : 'To Email' ,
name : 'toEmail' ,
type : 'string' ,
default : '' ,
required : true ,
placeholder : 'info@example.com' ,
description :
'Email address of the recipient. You can also specify a name: Nathan Doe <nate@n8n.io>.' ,
} ,
{
displayName : 'Subject' ,
name : 'subject' ,
type : 'string' ,
default : '' ,
placeholder : 'My subject line' ,
description : 'Subject line of the email' ,
} ,
{
displayName : 'Email Format' ,
name : 'emailFormat' ,
type : 'options' ,
options : [
{
name : 'Text' ,
value : 'text' ,
} ,
{
name : 'HTML' ,
value : 'html' ,
} ,
] ,
default : 'text' ,
} ,
{
displayName : 'Text' ,
name : 'text' ,
type : 'string' ,
typeOptions : {
rows : 5 ,
} ,
default : '' ,
description : 'Plain text message of email' ,
displayOptions : {
show : {
emailFormat : [ 'text' ] ,
} ,
} ,
} ,
{
displayName : 'HTML' ,
name : 'html' ,
type : 'string' ,
typeOptions : {
rows : 5 ,
} ,
default : '' ,
description : 'HTML text message of email' ,
displayOptions : {
show : {
emailFormat : [ 'html' ] ,
} ,
} ,
} ,
{
displayName : 'Options' ,
name : 'options' ,
type : 'collection' ,
placeholder : 'Add Option' ,
default : { } ,
options : [
{
displayName : 'Attachments' ,
name : 'attachments' ,
type : 'string' ,
default : '' ,
description :
2023-05-19 05:31:02 -07:00
'Name of the binary properties that contain data to add to email as attachment. Multiple ones can be comma-separated. Reference embedded images or other content within the body of an email message, e.g. <img src="cid:image_1">' ,
2023-01-24 02:32:31 -08:00
} ,
{
displayName : 'CC Email' ,
name : 'ccEmail' ,
type : 'string' ,
default : '' ,
placeholder : 'cc@example.com' ,
description : 'Email address of CC recipient' ,
} ,
{
displayName : 'BCC Email' ,
name : 'bccEmail' ,
type : 'string' ,
default : '' ,
placeholder : 'bcc@example.com' ,
description : 'Email address of BCC recipient' ,
} ,
{
displayName : 'Ignore SSL Issues' ,
name : 'allowUnauthorizedCerts' ,
type : 'boolean' ,
default : false ,
description : 'Whether to connect even if SSL certificate validation is not possible' ,
} ,
{
displayName : 'Reply To' ,
name : 'replyTo' ,
type : 'string' ,
default : '' ,
placeholder : 'info@example.com' ,
description : 'The email address to send the reply to' ,
} ,
] ,
} ,
] ;
const displayOptions = {
show : {
resource : [ 'email' ] ,
operation : [ 'send' ] ,
} ,
} ;
export const description = updateDisplayOptions ( displayOptions , properties ) ;
type EmailSendOptions = {
allowUnauthorizedCerts? : boolean ;
attachments? : string ;
ccEmail? : string ;
bccEmail? : string ;
replyTo? : string ;
} ;
function configureTransport ( credentials : IDataObject , options : EmailSendOptions ) {
const connectionOptions : SMTPTransport.Options = {
host : credentials.host as string ,
port : credentials.port as number ,
secure : credentials.secure as boolean ,
} ;
if ( credentials . user || credentials . password ) {
connectionOptions . auth = {
user : credentials.user as string ,
pass : credentials.password as string ,
} ;
}
if ( options . allowUnauthorizedCerts === true ) {
connectionOptions . tls = {
rejectUnauthorized : false ,
} ;
}
return createTransport ( connectionOptions ) ;
}
export async function execute ( this : IExecuteFunctions ) : Promise < INodeExecutionData [ ] [ ] > {
const items = this . getInputData ( ) ;
const returnData : INodeExecutionData [ ] = [ ] ;
let item : INodeExecutionData ;
for ( let itemIndex = 0 ; itemIndex < items . length ; itemIndex ++ ) {
try {
item = items [ itemIndex ] ;
const fromEmail = this . getNodeParameter ( 'fromEmail' , itemIndex ) as string ;
const toEmail = this . getNodeParameter ( 'toEmail' , itemIndex ) as string ;
const subject = this . getNodeParameter ( 'subject' , itemIndex ) as string ;
const emailFormat = this . getNodeParameter ( 'emailFormat' , itemIndex ) as string ;
const options = this . getNodeParameter ( 'options' , itemIndex , { } ) as EmailSendOptions ;
const credentials = await this . getCredentials ( 'smtp' ) ;
const transporter = configureTransport ( credentials , options ) ;
const mailOptions : IDataObject = {
from : fromEmail ,
to : toEmail ,
cc : options.ccEmail ,
bcc : options.bccEmail ,
subject ,
replyTo : options.replyTo ,
} ;
if ( emailFormat === 'text' ) {
mailOptions . text = this . getNodeParameter ( 'text' , itemIndex , '' ) ;
}
if ( emailFormat === 'html' ) {
mailOptions . html = this . getNodeParameter ( 'html' , itemIndex , '' ) ;
}
if ( options . attachments && item . binary ) {
const attachments = [ ] ;
const attachmentProperties : string [ ] = options . attachments
. split ( ',' )
. map ( ( propertyName ) = > {
return propertyName . trim ( ) ;
} ) ;
for ( const propertyName of attachmentProperties ) {
2023-03-06 08:33:32 -08:00
const binaryData = this . helpers . assertBinaryData ( itemIndex , propertyName ) ;
2023-01-24 02:32:31 -08:00
attachments . push ( {
2023-03-06 08:33:32 -08:00
filename : binaryData.fileName || 'unknown' ,
2023-01-24 02:32:31 -08:00
content : await this . helpers . getBinaryDataBuffer ( itemIndex , propertyName ) ,
2023-05-19 05:31:02 -07:00
cid : propertyName ,
2023-01-24 02:32:31 -08:00
} ) ;
}
if ( attachments . length ) {
mailOptions . attachments = attachments ;
}
}
const info = await transporter . sendMail ( mailOptions ) ;
returnData . push ( {
json : info as unknown as IDataObject ,
pairedItem : {
item : itemIndex ,
} ,
} ) ;
} catch ( error ) {
if ( this . continueOnFail ( ) ) {
returnData . push ( {
json : {
error : error.message ,
} ,
pairedItem : {
item : itemIndex ,
} ,
} ) ;
continue ;
}
2023-05-19 05:31:02 -07:00
delete error . cert ;
throw new NodeApiError ( this . getNode ( ) , error as JsonObject ) ;
2023-01-24 02:32:31 -08:00
}
}
return this . prepareOutputData ( returnData ) ;
}