2023-01-27 03:22:44 -08:00
import type {
2024-07-22 11:38:26 -07:00
ICredentialsDecrypted ,
ICredentialTestFunctions ,
2023-01-27 03:22:44 -08:00
IDataObject ,
IExecuteFunctions ,
2024-07-22 11:38:26 -07:00
INodeCredentialTestResult ,
2023-01-27 03:22:44 -08:00
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' ,
} ,
2023-10-03 01:18:59 -07:00
{
displayName : 'Email Format' ,
name : 'emailFormat' ,
type : 'options' ,
options : [
{
name : 'Text' ,
value : 'text' ,
2023-11-09 08:09:01 -08:00
description : 'Send email as plain text' ,
2023-10-03 01:18:59 -07:00
} ,
{
name : 'HTML' ,
value : 'html' ,
2023-11-09 08:09:01 -08:00
description : 'Send email as HTML' ,
2023-10-03 01:18:59 -07:00
} ,
{
name : 'Both' ,
value : 'both' ,
2023-11-09 08:09:01 -08:00
description : "Send both formats, recipient's client selects version to display" ,
2023-10-03 01:18:59 -07:00
} ,
] ,
default : 'html' ,
displayOptions : {
hide : {
'@version' : [ 2 ] ,
} ,
} ,
} ,
2023-01-24 02:32:31 -08:00
{
displayName : 'Email Format' ,
name : 'emailFormat' ,
type : 'options' ,
options : [
{
name : 'Text' ,
value : 'text' ,
} ,
{
name : 'HTML' ,
value : 'html' ,
} ,
2023-08-21 02:49:33 -07:00
{
name : 'Both' ,
value : 'both' ,
} ,
2023-01-24 02:32:31 -08:00
] ,
default : 'text' ,
2023-10-03 01:18:59 -07:00
displayOptions : {
show : {
'@version' : [ 2 ] ,
} ,
} ,
2023-01-24 02:32:31 -08:00
} ,
{
displayName : 'Text' ,
name : 'text' ,
type : 'string' ,
typeOptions : {
rows : 5 ,
} ,
default : '' ,
description : 'Plain text message of email' ,
displayOptions : {
show : {
2023-08-21 02:49:33 -07:00
emailFormat : [ 'text' , 'both' ] ,
2023-01-24 02:32:31 -08:00
} ,
} ,
} ,
{
displayName : 'HTML' ,
name : 'html' ,
type : 'string' ,
typeOptions : {
rows : 5 ,
} ,
default : '' ,
description : 'HTML text message of email' ,
displayOptions : {
show : {
2023-08-21 02:49:33 -07:00
emailFormat : [ 'html' , 'both' ] ,
2023-01-24 02:32:31 -08:00
} ,
} ,
} ,
{
displayName : 'Options' ,
name : 'options' ,
type : 'collection' ,
2024-07-29 05:27:23 -07:00
placeholder : 'Add option' ,
2023-01-24 02:32:31 -08:00
default : { } ,
options : [
2023-10-03 01:18:59 -07:00
{
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
displayName : 'Append n8n Attribution' ,
name : 'appendAttribution' ,
type : 'boolean' ,
default : true ,
description :
'Whether to include the phrase “This email was sent automatically with n8n” to the end of the email' ,
} ,
2023-01-24 02:32:31 -08:00
{
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 = {
2023-10-03 01:18:59 -07:00
appendAttribution? : boolean ;
2023-01-24 02:32:31 -08:00
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 ,
} ;
2024-07-22 11:38:26 -07:00
if ( credentials . secure === false ) {
connectionOptions . ignoreTLS = credentials . disableStartTls as boolean ;
}
2024-05-07 08:38:23 -07:00
if ( typeof credentials . hostName === 'string' && credentials . hostName ) {
connectionOptions . name = credentials . hostName ;
}
2023-01-24 02:32:31 -08:00
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 ) ;
}
2024-07-22 11:38:26 -07:00
export async function smtpConnectionTest (
this : ICredentialTestFunctions ,
credential : ICredentialsDecrypted ,
) : Promise < INodeCredentialTestResult > {
const credentials = credential . data ! ;
const transporter = configureTransport ( credentials , { } ) ;
try {
await transporter . verify ( ) ;
return {
status : 'OK' ,
message : 'Connection successful!' ,
} ;
} catch ( error ) {
return {
status : 'Error' ,
message : error.message ,
} ;
} finally {
transporter . close ( ) ;
}
}
2023-01-24 02:32:31 -08:00
export async function execute ( this : IExecuteFunctions ) : Promise < INodeExecutionData [ ] [ ] > {
const items = this . getInputData ( ) ;
2023-10-03 01:18:59 -07:00
const nodeVersion = this . getNode ( ) . typeVersion ;
2023-10-23 04:39:35 -07:00
const instanceId = this . getInstanceId ( ) ;
2023-01-24 02:32:31 -08:00
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 ,
} ;
2023-08-21 02:49:33 -07:00
if ( emailFormat === 'text' || emailFormat === 'both' ) {
2023-01-24 02:32:31 -08:00
mailOptions . text = this . getNodeParameter ( 'text' , itemIndex , '' ) ;
}
2023-08-21 02:49:33 -07:00
if ( emailFormat === 'html' || emailFormat === 'both' ) {
2023-01-24 02:32:31 -08:00
mailOptions . html = this . getNodeParameter ( 'html' , itemIndex , '' ) ;
}
2023-10-03 01:18:59 -07:00
let appendAttribution = options . appendAttribution ;
if ( appendAttribution === undefined ) {
appendAttribution = nodeVersion >= 2.1 ;
}
if ( appendAttribution ) {
const attributionText = 'This email was sent automatically with ' ;
const link = ` https://n8n.io/?utm_source=n8n-internal&utm_medium=powered_by&utm_campaign= ${ encodeURIComponent (
'n8n-nodes-base.emailSend' ,
) } $ { instanceId ? '_' + instanceId : '' } ` ;
if ( emailFormat === 'html' || ( emailFormat === 'both' && mailOptions . html ) ) {
mailOptions . html = `
$ { mailOptions . html }
< br >
< br >
-- -
< br >
< em > $ { attributionText } < a href = "${link}" target = "_blank" > n8n < / a > < / em >
` ;
} else {
2023-11-23 02:55:02 -08:00
mailOptions . text = ` ${ mailOptions . text } \ n \ n--- \ n ${ attributionText } n8n \ n ${ 'https://n8n.io' } ` ;
2023-10-03 01:18:59 -07:00
}
}
2023-01-24 02:32:31 -08:00
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 ) {
2024-06-19 22:45:00 -07:00
if ( this . continueOnFail ( error ) ) {
2023-01-24 02:32:31 -08:00
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
}
}
2023-09-05 03:59:02 -07:00
return [ returnData ] ;
2023-01-24 02:32:31 -08:00
}