@hyperbytes/wappler-imap-manager
Version:
IMAP eMail Management for Wappler
185 lines (162 loc) • 7.11 kB
JavaScript
const Imap = require('imap-simple');
const nodemailer = require('nodemailer');
const mailcomposer = require('mailcomposer');
const { simpleParser } = require('mailparser'); // Used for debugging email content
exports.imapsaveasdraft = async function (options) {
console.log('Starting IMAP draft save process...');
// Parse IMAP configuration
const IMAP_HOST = this.parseOptional(options.imap_host, '*', process.env.IMAP_HOST);
const IMAP_PASSWORD = this.parseOptional(options.imap_password, '*', process.env.IMAP_PASSWORD);
const IMAP_USER = this.parseOptional(options.imap_user, '*', process.env.IMAP_USER);
const IMAP_PORT = this.parseOptional(options.imap_port, '*', process.env.IMAP_PORT);
const imap_tlsstring = this.parseOptional(options.imap_tls, '*', process.env.IMAP_TLS).toLowerCase();
const IMAP_TLS = (imap_tlsstring === 'true');
// Validate essential IMAP configuration
if (!IMAP_HOST || !IMAP_USER || !IMAP_PASSWORD) {
console.error('IMAP host, user, or password not configured.');
return {
status: 401,
message: 'IMAP configuration incomplete.',
error: 'IMAP host, user, or password not configured.',
uid: '',
folder: this.parseOptional(options.mailbox, '*', 'Drafts')
};
}
// Parse email details
const to = this.parseOptional(options.to, "*", "");
console.log('To: ' + to);
const mailfrom = this.parseOptional(options.from, "*", "");
console.log('From: ' + mailfrom);
const cc = this.parseOptional(options.cc, "*", "");
console.log('CC: ' + cc);
const bcc = this.parseOptional(options.bcc, "*", "");
console.log('BCC: ' + bcc);
const subject = this.parseOptional(options.mailsubject, "*", "default subject");
console.log('Subject: ' + subject);
const body = this.parseOptional(options.body, "*", ""); // Assuming this is HTML body
console.log('Body: ' + body);
const attachments = this.parseOptional(options.attachments, '*', []);
console.log('Attachments: ' + attachments);
let folder = this.parseOptional(options.mailbox, '*', 'Drafts'); // Default to 'Drafts'
console.log('Folder: ' + folder);
// Check if a UID is provided – meaning the draft is to be updated
const providedUid = this.parseOptional(options.uid, "*", "");
console.log('UID: ' + providedUid);
console.log(`Attempting to save draft to folder: ${folder}`);
const imapConfig = {
imap: {
user: IMAP_USER,
password: IMAP_PASSWORD,
host: IMAP_HOST,
port: IMAP_PORT,
tls: IMAP_TLS,
tlsOptions: { rejectUnauthorized: false },
authTimeout: 5000
}
};
let connection;
try {
// 1. Construct the email message using mailcomposer
const mailOptions = {
from: mailfrom,
to: to,
cc: cc,
bcc: bcc,
subject: subject,
html: body, // Use 'text' if it's plain text
attachments: attachments
};
const mail = mailcomposer(mailOptions);
const rawEmail = await new Promise((resolve, reject) => {
mail.build((err, message) => {
if (err) {
console.error('Error building email:', err);
return {
status: 400,
message: 'Error building email.',
uid: '',
folder: folder
};
}
resolve(message);
});
});
console.log('Raw email constructed.');
// Debug: Parse the raw email to verify its content
const parsedEmail = await simpleParser(rawEmail);
console.log('Parsed Email Content:', {
subject: parsedEmail.subject,
text: parsedEmail.text,
html: parsedEmail.html,
headers: parsedEmail.headers
});
// 2. Connect to IMAP server
console.log(`Connecting to IMAP server ${IMAP_HOST}:${IMAP_PORT}...`);
connection = await Imap.connect(imapConfig);
console.log('Successfully connected to IMAP server.');
// 3. Open the target mailbox
await connection.openBox(folder);
console.log(`Mailbox "${folder}" opened.`);
// Retrieve UID validity of the destination mailbox (if available)
// 4. If a UID is provided, update the existing draft; otherwise, append a new one.
if (providedUid) {
console.log(`UID provided (${providedUid}). Updating existing draft...`);
// Attempt to delete the existing draft for that UID
try {
await connection.deleteMessage(providedUid);
console.log(`Existing draft with UID ${providedUid} deleted.`);
} catch (delErr) {
console.warn(`Could not delete existing draft with UID ${providedUid}: ${delErr}`);
// You might decide to abort here if deletion is mandatory
}
// Append the new draft (note: the server will normally assign a new UID,
// but we are returning the provided UID per the requirement).
console.log(`Appending updated draft to folder: ${folder}...`);
const updateAppendResult = await connection.append(rawEmail, {
mailbox: folder,
flags: ['\\Draft']
});
console.log('Updated draft appended.', updateAppendResult);
return {
status: 200,
message: 'Draft updated successfully.',
uid: providedUid,
folder: folder
};
} else {
console.log(`Appending email as a draft to folder: ${folder}...`);
const appendResult = await connection.append(rawEmail, {
mailbox: folder,
flags: ['\\Draft']
});
console.log('Email appended to drafts folder.', appendResult);
// Retrieve UID of the saved message
const searchResult = await connection.search(['ALL'], { bodies: ['HEADER.FIELDS (MESSAGE-ID)'] });
const uid = searchResult.length > 0 ? searchResult[searchResult.length - 1].attributes.uid : '';
return {
status: 200,
message: 'Draft saved successfully.',
uid: uid,
folder: folder
};
}
} catch (err) {
console.error('IMAP draft save failed:', err);
return {
status: 400,
message: 'Failed to save draft.',
error: err.toString(),
uid: '',
folder: folder
};
} finally {
if (connection) {
try {
await connection.end();
console.log('IMAP connection closed.');
} catch (endErr) {
console.error('Error ending IMAP connection:', endErr);
}
}
}
};