diff --git a/.vscode/launch.json b/.vscode/launch.json index 68a41b8e..1b520992 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,10 +10,10 @@ "name": "Launch Program", "program": "${workspaceFolder}/src/butler.js", "cwd": "${workspaceFolder}/src", - // "args": [ - // "--configfile", - // "./config/production.yaml", - // ], + "args": [ + // "--configfile", + // "./config/production.yaml", + ], "env": { "NODE_CONFIG_DIR": "${workspaceFolder}/src/config", "NODE_ENV": "production" diff --git a/src/app.js b/src/app.js index b7ceb94d..c62718eb 100644 --- a/src/app.js +++ b/src/app.js @@ -17,6 +17,7 @@ const scheduler = require('./lib/scheduler'); const serviceUptime = require('./lib/service_uptime'); const telemetry = require('./lib/telemetry'); const configUtil = require('./lib/config_util'); +const { sendTestEmail } = require('./lib/testemail'); async function build(opts = {}) { const restServer = Fastify({ logger: true }); @@ -86,6 +87,11 @@ async function build(opts = {}) { globals.logger.info(`Client cert key : ${keyFile}`); globals.logger.info(`Client cert CA : ${caFile}`); + // Is there a email address specified on the command line? Send test email to it if so. + if (globals.options.testEmailAddress && globals.options.testEmailAddress.length > 0) { + sendTestEmail(globals.options.testEmailAddress); + } + // Set up anon telemetry reports, if enabled if ( globals.config.has('Butler.anonTelemetry') === false || diff --git a/src/globals.js b/src/globals.js index 38c3fa57..283954b1 100644 --- a/src/globals.js +++ b/src/globals.js @@ -39,7 +39,8 @@ program .option('-c, --configfile ', 'path to config file') .addOption(new Option('-l, --loglevel ', 'log level').choices(['error', 'warn', 'info', 'verbose', 'debug', 'silly'])) .option('--new-relic-api-key ', 'insert API key to use with New Relic') - .option('--new-relic-account-id ', 'New Relic account ID'); + .option('--new-relic-account-id ', 'New Relic account ID') + .option('--test-email-address
', 'send test email to this address. Used to verify email settings in the config file.'); // Parse command line params program.parse(process.argv); @@ -512,4 +513,5 @@ module.exports = { hostInfo, isPkg, checkFileExistsSync, + options, }; diff --git a/src/lib/smtp.js b/src/lib/smtp.js index bb1531a1..a53e950e 100644 --- a/src/lib/smtp.js +++ b/src/lib/smtp.js @@ -246,6 +246,53 @@ async function sendEmail(from, recipientsEmail, emailPriority, subjectHandlebars } } +async function sendEmailBasic(from, recipientsEmail, emailPriority, subject, body) { + try { + // First make sure email sending is enabled in the config file and that we have all required SMTP settings + if (isSmtpConfigOk() === false) { + return 1; + } + + const smtpOptions = getSmtpOptions(); + const transporter = nodemailer.createTransport(smtpOptions); + + // Loop over all email recipients + // eslint-disable-next-line no-restricted-syntax + for (const recipientEmail of recipientsEmail) { + // Verify that email address is valid + if (emailValidator.validate(recipientEmail) === true) { + // Recipient email address has valid format + // Set up mail object + const message = { + priority: emailPriority, + from, + to: recipientEmail, + subject, + text: body, + }; + + // Verify SMTP configuration + // eslint-disable-next-line no-await-in-loop + const smtpStatus = await transporter.verify(); + globals.logger.debug(`SMTP BASIC: SMTP status: ${smtpStatus}`); + globals.logger.debug(`SMTP BASIC: Message=${JSON.stringify(message, null, 2)}`); + + if (smtpStatus) { + // eslint-disable-next-line no-await-in-loop + const result = await transporter.sendMail(message); + globals.logger.debug(`SMTP BASIC: Sending email result: ${JSON.stringify(result, null, 2)}`); + } else { + globals.logger.warn('SMTP BASIC: SMTP transporter not ready'); + } + } else { + globals.logger.warn(`SMTP BASIC: Recipient email adress not valid: ${recipientEmail}`); + } + } + } catch (err) { + globals.logger.error(`SMTP BASIC: ${err}`); + } +} + async function sendReloadTaskFailureNotificationEmail(reloadParams) { // Determine if an alert should be sent or not // 1. If config setting Butler.emailNotification.reloadTaskFailure.alertEnableByCustomProperty.enable is true @@ -691,4 +738,5 @@ async function sendReloadTaskAbortedNotificationEmail(reloadParams) { module.exports = { sendReloadTaskFailureNotificationEmail, sendReloadTaskAbortedNotificationEmail, + sendEmailBasic, }; diff --git a/src/lib/testemail.js b/src/lib/testemail.js new file mode 100644 index 00000000..35627b93 --- /dev/null +++ b/src/lib/testemail.js @@ -0,0 +1,20 @@ +const globals = require('../globals'); +const { sendEmailBasic } = require('./smtp'); + +function sendTestEmail(emailAddress) { + try { + sendEmailBasic( + 'noreply', + [emailAddress], + 'normal', + 'Test email from Butler for Qlik Sense', + "This is a test email sent from your friendly Butler for Qlik Sense Enterprise on Windows.\n\nIf you get this email Butler's email configuration is correct and working." + ); + } catch (err) { + globals.logger.error(`TEST EMAIL: ${err}`); + } +} + +module.exports = { + sendTestEmail, +};