diff --git a/src/arguments.js b/src/arguments.js index e153cba9..fed030fc 100644 --- a/src/arguments.js +++ b/src/arguments.js @@ -7,7 +7,8 @@ import { program, Option } from 'commander'; import { exit } from 'process'; import ora from 'ora'; -import { AUTH, CLI_COMMAND_NAME, DEFAULT_AUTH, DEFAULT_FILENAME, DEFAULT_FORMAT, DEFAULT_MIN_HEIGHT, DEFAULT_TENANT, DEFAULT_WIDTH, ENV_VAR, FORMAT, TRANSPORT_TYPE, DEFAULT_EMAIL_SUBJECT } from './constants.js'; +import fs from 'fs'; +import { AUTH, CLI_COMMAND_NAME, DEFAULT_AUTH, DEFAULT_FILENAME, DEFAULT_FORMAT, DEFAULT_MIN_HEIGHT, DEFAULT_TENANT, DEFAULT_WIDTH, ENV_VAR, FORMAT, TRANSPORT_TYPE, DEFAULT_EMAIL_SUBJECT, DEFAULT_EMAIL_NOTE } from './constants.js'; import dotenv from "dotenv"; dotenv.config(); @@ -56,9 +57,12 @@ export async function getCommandArguments() { .env(ENV_VAR.SMTP_USERNAME)) .addOption(new Option('--smtppassword ', 'smtp password') .env(ENV_VAR.SMTP_PASSWORD)) - .addOption(new Option('--subject ', 'email Subject') + .addOption(new Option('--subject ', 'email subject') .default(DEFAULT_EMAIL_SUBJECT) .env(ENV_VAR.EMAIL_SUBJECT)) + .addOption(new Option('--note ', 'email body (string or path to text file)') + .default(DEFAULT_EMAIL_NOTE) + .env(ENV_VAR.EMAIL_NOTE)) program.addHelpText('after', ` Note: The tenant in the url has the higher priority than tenant value provided as command option.`); @@ -90,6 +94,7 @@ function getOptions(options) { smtppassword: null, subject: null, time: null, + note: null } // Set url. @@ -145,8 +150,7 @@ function getOptions(options) { commandOptions.filename = options.filename || process.env[ENV_VAR.FILENAME]; commandOptions.filename = options.filename === DEFAULT_FILENAME ? `${commandOptions.filename}-${commandOptions.time.toISOString()}.${commandOptions.format}` - : `${commandOptions.filename}.${commandOptions.format}` - + : `${commandOptions.filename}.${commandOptions.format}`; // Set width and height of the window commandOptions.width = Number(options.width); @@ -169,6 +173,25 @@ function getOptions(options) { // Set email subject. commandOptions.subject = options.subject || process.env[ENV_VAR.EMAIL_SUBJECT]; + // Set email note. + commandOptions.note = options.note || process.env[ENV_VAR.EMAIL_NOTE]; + if (commandOptions.note !== DEFAULT_EMAIL_NOTE && fs.existsSync(commandOptions.note)) { + commandOptions.note = fs.readFileSync(commandOptions.note, "utf8"); + } + commandOptions.note = getHtml(commandOptions.note); + spinner.succeed('Fetched argument values') return commandOptions; +} + +// Convert text to html +function getHtml(text) { + text = (text || ""); + return text + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/\t/g, " ") + .replace(/ /g, "​ ​") + .replace(/\r\n|\r|\n/g, "
"); } \ No newline at end of file diff --git a/src/constants.js b/src/constants.js index eb2ddeba..47046544 100644 --- a/src/constants.js +++ b/src/constants.js @@ -11,6 +11,7 @@ export const DEFAULT_WIDTH = '1680'; export const DEFAULT_MIN_HEIGHT = '600'; export const DEFAULT_FILENAME = 'opensearch-report'; export const DEFAULT_EMAIL_SUBJECT = 'This is an email containing your opensearch dashboard report'; +export const DEFAULT_EMAIL_NOTE = 'Hi,\nHere is the latest report!'; export const REPORT_TYPE = { DASHBOARD: 'Dashboard', @@ -60,7 +61,8 @@ export const ENV_VAR = { SMTP_SECURE: 'OPENSEARCH_SMTP_SECURE', SMTP_USERNAME: 'OPENSEARCH_SMTP_USERNAME', SMTP_PASSWORD: 'OPENSEARCH_SMTP_PASSWORD', - EMAIL_SUBJECT: 'OPENSEARCH_EMAIL_SUBJECT' + EMAIL_SUBJECT: 'OPENSEARCH_EMAIL_SUBJECT', + EMAIL_NOTE: 'OPENSEARCH_EMAIL_NOTE', } export const TRANSPORT_TYPE = { diff --git a/src/download-helpers.js b/src/download-helpers.js index d3fc4501..aa596a99 100644 --- a/src/download-helpers.js +++ b/src/download-helpers.js @@ -11,7 +11,7 @@ import ora from 'ora'; const spinner = ora(); -export async function downloadReport(url, format, width, height, filename, authType, username, password, tenant, time) { +export async function downloadReport(url, format, width, height, filename, authType, username, password, tenant, time, transport) { spinner.start('Connecting to url ' + url); try { const browser = await puppeteer.launch({ @@ -93,7 +93,7 @@ export async function downloadReport(url, format, width, height, filename, authT } // force wait for any resize to load after the above DOM modification. - await new Promise(resolve => setTimeout(resolve, 1000)); + await new Promise(resolve => setTimeout(resolve, 2000)); await waitForDynamicContent(page); let buffer; spinner.text = `Downloading Report...`; @@ -132,12 +132,19 @@ export async function downloadReport(url, format, width, height, filename, authT } } - await browser.close(); - const timeCreated = time.valueOf(); const data = { timeCreated, dataUrl: buffer.toString('base64'), }; - await readStreamToFile(data.dataUrl, filename, format); + + if (transport !== undefined) { + const emailTemplateImageBuffer = await page.screenshot({ + fullPage: true, + }); + const data = { timeCreated, dataUrl: emailTemplateImageBuffer.toString('base64'), }; + await readStreamToFile(data.dataUrl, 'email_body.png', FORMAT.PNG); + } + + await browser.close(); spinner.succeed('The report is downloaded'); } catch (e) { spinner.fail('Downloading report failed. ' + e); diff --git a/src/email-helpers.js b/src/email-helpers.js index 7fedf2a3..f2e0b85f 100644 --- a/src/email-helpers.js +++ b/src/email-helpers.js @@ -6,7 +6,7 @@ import nodemailer from "nodemailer"; import hbs from "nodemailer-express-handlebars"; import ora from 'ora'; -import { FORMAT } from './constants.js'; +import fs from 'fs'; import AWS from "aws-sdk"; import path from 'path'; import { fileURLToPath } from 'url'; @@ -22,11 +22,12 @@ try { // Do not set AWS_SDK_LOAD_CONFIG if aws config file is missing. } -export async function sendEmail(filename, format, sender, recipient, transport, smtphost, smtpport, smtpsecure, smtpusername, smtppassword, subject) { +export async function sendEmail(filename, url, sender, recipient, transport, smtphost, smtpport, smtpsecure, smtpusername, smtppassword, subject, note) { if (transport !== undefined && (transport === 'smtp' || ses !== undefined) && sender !== undefined && recipient !== undefined) { spinner.start('Sending email...'); } else { if (transport === undefined && sender === undefined && recipient === undefined) { + deleteTemporaryImage(); return; } else if (transport === undefined) { spinner.warn('Transport value is missing'); @@ -36,10 +37,11 @@ export async function sendEmail(filename, format, sender, recipient, transport, spinner.warn('Sender/Recipient value is missing'); } spinner.fail('Skipped sending email'); + deleteTemporaryImage(); return; } - let mailOptions = getmailOptions(format, sender, recipient, filename, subject); + let mailOptions = getmailOptions(url, sender, recipient, filename, subject, note); let transporter = getTransporter(transport, smtphost, smtpport, smtpsecure, smtpusername, smtppassword); @@ -59,6 +61,7 @@ export async function sendEmail(filename, format, sender, recipient, transport, } else { spinner.succeed('Email sent successfully'); } + deleteTemporaryImage(); }); } @@ -81,33 +84,39 @@ const getTransporter = (transport, smtphost, smtpport, smtpsecure, smtpusername, return transporter; } -const getmailOptions = (format, sender, recipient, file, emailSubject, mailOptions = {}) => { - if (format === FORMAT.PNG) { - mailOptions = { - from: sender, - subject: emailSubject, - to: recipient, - attachments: [ - { - filename: file, - path: file, - cid: 'report' - }], - template: 'index' - }; - } else { - mailOptions = { - from: sender, - subject: emailSubject, - to: recipient, - attachments: [ - { - filename: file, - path: file, - contentType: 'application/pdf' - }], - template: 'index' - }; - } +const getmailOptions = (url, sender, recipient, file, emailSubject, note, mailOptions = {}) => { + mailOptions = { + from: sender, + subject: emailSubject, + to: recipient, + attachments: [ + { + filename: 'email_body.png', + path: 'email_body.png', + cid: 'email_body' + }, + { + filename: 'opensearch_logo_darkmode.png', + path: path.join(__dirname, './views/opensearch_logo_darkmode.png'), + cid: 'opensearch_logo_darkmode' + }, + { + filename: file, + path: file + }], + template: 'index', + context: { + REPORT_TITLE: file, + DASHBOARD_URL: url, + NOTE: note + } + }; return mailOptions; +} + +// Delete temporary image created for email body +function deleteTemporaryImage() { + if (fs.existsSync('email_body.png')) { + fs.unlinkSync('email_body.png'); + } } \ No newline at end of file diff --git a/src/index.js b/src/index.js index 3b5c38d8..bca4abbe 100755 --- a/src/index.js +++ b/src/index.js @@ -22,12 +22,13 @@ await downloadReport( options.username, options.password, options.tenant, - options.time + options.time, + options.transport ); await sendEmail( options.filename, - options.format, + options.url, options.sender, options.recipient, options.transport, @@ -37,4 +38,5 @@ await sendEmail( options.smtpusername, options.smtppassword, options.subject, + options.note ); diff --git a/src/views/index.hbs b/src/views/index.hbs index 09227bec..9909d079 100644 --- a/src/views/index.hbs +++ b/src/views/index.hbs @@ -1,11 +1,435 @@ - - - - - - - - - - - \ No newline at end of file + + + + + + OpenSearch Dashboards Report: {{{REPORT_TITLE}}} + + + + + A new OpenSearch report is available + + + + + + + + + + + + \ No newline at end of file diff --git a/src/views/opensearch_logo_darkmode.png b/src/views/opensearch_logo_darkmode.png new file mode 100644 index 00000000..ffd60e07 Binary files /dev/null and b/src/views/opensearch_logo_darkmode.png differ diff --git a/test/help.test.js b/test/help.test.js index 710cd13d..54c920b4 100644 --- a/test/help.test.js +++ b/test/help.test.js @@ -30,7 +30,8 @@ Options: --smtpsecure use TLS when connecting to server (env: OPENSEARCH_SMTP_SECURE) --smtpusername smtp username (env: OPENSEARCH_SMTP_USERNAME) --smtppassword smtp password (env: OPENSEARCH_SMTP_PASSWORD) - --subject email Subject (default: "This is an email containing your opensearch dashboard report", env: OPENSEARCH_EMAIL_SUBJECT) + --subject email subject (default: "This is an email containing your opensearch dashboard report", env: OPENSEARCH_EMAIL_SUBJECT) + --note email body (string or path to text file) (default: "Hi,\\nHere is the latest report!", env: OPENSEARCH_EMAIL_NOTE) -h, --help display help for command Note: The tenant in the url has the higher priority than tenant value provided as command option.