mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-25 04:34:06 -08:00
✨ Add Force node reconnect option to IMAP trigger node (#1792)
* Adding reconnect feature to IMAP nodes * Fixing clear interval and persisting last UID in static data
This commit is contained in:
parent
0877b0779d
commit
3e98612881
|
@ -24,6 +24,10 @@ import {
|
|||
|
||||
import * as lodash from 'lodash';
|
||||
|
||||
import {
|
||||
LoggerProxy as Logger
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export class EmailReadImap implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'EmailReadImap',
|
||||
|
@ -158,6 +162,13 @@ export class EmailReadImap implements INodeType {
|
|||
default: false,
|
||||
description: 'Do connect even if SSL certificate validation is not possible.',
|
||||
},
|
||||
{
|
||||
displayName: 'Force reconnect',
|
||||
name: 'forceReconnect',
|
||||
type: 'number',
|
||||
default: 60,
|
||||
description: 'Sets an interval (in minutes) to force a reconnection.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
@ -176,16 +187,8 @@ export class EmailReadImap implements INodeType {
|
|||
const postProcessAction = this.getNodeParameter('postProcessAction') as string;
|
||||
const options = this.getNodeParameter('options', {}) as IDataObject;
|
||||
|
||||
let searchCriteria = [
|
||||
'UNSEEN',
|
||||
];
|
||||
if (options.customEmailConfig !== undefined) {
|
||||
try {
|
||||
searchCriteria = JSON.parse(options.customEmailConfig as string);
|
||||
} catch (error) {
|
||||
throw new NodeOperationError(this.getNode(), `Custom email config is not valid JSON.`);
|
||||
}
|
||||
}
|
||||
const staticData = this.getWorkflowStaticData('node');
|
||||
Logger.debug('Loaded static data for node "EmailReadImap"', {staticData});
|
||||
|
||||
// Returns the email text
|
||||
const getText = async (parts: any[], message: Message, subtype: string) => { // tslint:disable-line:no-any
|
||||
|
@ -237,7 +240,7 @@ export class EmailReadImap implements INodeType {
|
|||
|
||||
|
||||
// Returns all the new unseen messages
|
||||
const getNewEmails = async (connection: ImapSimple, searchCriteria: string[]): Promise<INodeExecutionData[]> => {
|
||||
const getNewEmails = async (connection: ImapSimple, searchCriteria: Array<string | string[]>): Promise<INodeExecutionData[]> => {
|
||||
const format = this.getNodeParameter('format', 0) as string;
|
||||
|
||||
let fetchOptions = {};
|
||||
|
@ -277,6 +280,12 @@ export class EmailReadImap implements INodeType {
|
|||
const dataPropertyAttachmentsPrefixName = this.getNodeParameter('dataPropertyAttachmentsPrefixName') as string;
|
||||
|
||||
for (const message of results) {
|
||||
if (staticData.lastMessageUid !== undefined && message.attributes.uid <= (staticData.lastMessageUid as number)) {
|
||||
continue;
|
||||
}
|
||||
if (staticData.lastMessageUid === undefined || staticData.lastMessageUid as number < message.attributes.uid) {
|
||||
staticData.lastMessageUid = message.attributes.uid;
|
||||
}
|
||||
const part = lodash.find(message.parts, { which: '' });
|
||||
|
||||
if (part === undefined) {
|
||||
|
@ -295,6 +304,12 @@ export class EmailReadImap implements INodeType {
|
|||
}
|
||||
|
||||
for (const message of results) {
|
||||
if (staticData.lastMessageUid !== undefined && message.attributes.uid <= (staticData.lastMessageUid as number)) {
|
||||
continue;
|
||||
}
|
||||
if (staticData.lastMessageUid === undefined || staticData.lastMessageUid as number < message.attributes.uid) {
|
||||
staticData.lastMessageUid = message.attributes.uid;
|
||||
}
|
||||
const parts = getParts(message.attributes.struct!);
|
||||
|
||||
newEmail = {
|
||||
|
@ -335,6 +350,12 @@ export class EmailReadImap implements INodeType {
|
|||
}
|
||||
} else if (format === 'raw') {
|
||||
for (const message of results) {
|
||||
if (staticData.lastMessageUid !== undefined && message.attributes.uid <= (staticData.lastMessageUid as number)) {
|
||||
continue;
|
||||
}
|
||||
if (staticData.lastMessageUid === undefined || staticData.lastMessageUid as number < message.attributes.uid) {
|
||||
staticData.lastMessageUid = message.attributes.uid;
|
||||
}
|
||||
const part = lodash.find(message.parts, { which: 'TEXT' });
|
||||
|
||||
if (part === undefined) {
|
||||
|
@ -366,6 +387,33 @@ export class EmailReadImap implements INodeType {
|
|||
},
|
||||
onmail: async () => {
|
||||
if (connection) {
|
||||
let searchCriteria = [
|
||||
'UNSEEN',
|
||||
] as Array<string | string[]>;
|
||||
if (options.customEmailConfig !== undefined) {
|
||||
try {
|
||||
searchCriteria = JSON.parse(options.customEmailConfig as string);
|
||||
} catch (error) {
|
||||
throw new NodeOperationError(this.getNode(), `Custom email config is not valid JSON.`);
|
||||
}
|
||||
}
|
||||
if (staticData.lastMessageUid !== undefined) {
|
||||
searchCriteria.push(['UID', `${staticData.lastMessageUid as number}:*`]);
|
||||
/**
|
||||
* A short explanation about UIDs and how they work
|
||||
* can be found here: https://dev.to/kehers/imap-new-messages-since-last-check-44gm
|
||||
* TL;DR:
|
||||
* - You cannot filter using ['UID', 'CURRENT ID + 1:*'] because IMAP
|
||||
* won't return correct results if current id + 1 does not yet exist.
|
||||
* - UIDs can change but this is not being treated here.
|
||||
* If the mailbox is recreated (lets say you remove all emails, remove
|
||||
* the mail box and create another with same name, UIDs will change)
|
||||
* - You can check if UIDs changed in the above example
|
||||
* by checking UIDValidity.
|
||||
*/
|
||||
Logger.debug('Querying for new messages on node "EmailReadImap"', {searchCriteria});
|
||||
}
|
||||
|
||||
const returnData = await getNewEmails(connection, searchCriteria);
|
||||
|
||||
if (returnData.length) {
|
||||
|
@ -386,7 +434,9 @@ export class EmailReadImap implements INodeType {
|
|||
return imapConnect(config).then(async conn => {
|
||||
conn.on('error', async err => {
|
||||
if (err.code.toUpperCase() === 'ECONNRESET') {
|
||||
Logger.verbose('IMAP connection was reset - reconnecting.');
|
||||
connection = await establishConnection();
|
||||
await connection.openBox(mailbox);
|
||||
}
|
||||
throw err;
|
||||
});
|
||||
|
@ -398,8 +448,22 @@ export class EmailReadImap implements INodeType {
|
|||
|
||||
await connection.openBox(mailbox);
|
||||
|
||||
let reconnectionInterval: NodeJS.Timeout | undefined;
|
||||
|
||||
if (options.forceReconnect !== undefined) {
|
||||
reconnectionInterval = setInterval(async () => {
|
||||
Logger.verbose('Forcing reconnection of IMAP node.');
|
||||
await connection.end();
|
||||
connection = await establishConnection();
|
||||
await connection.openBox(mailbox);
|
||||
}, options.forceReconnect as number * 1000 * 60);
|
||||
}
|
||||
|
||||
// When workflow and so node gets set to inactive close the connectoin
|
||||
async function closeFunction() {
|
||||
if (reconnectionInterval) {
|
||||
clearInterval(reconnectionInterval);
|
||||
}
|
||||
await connection.end();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue