From ad105bfb214978b08bc1401ccdfa7bd86071e6fe Mon Sep 17 00:00:00 2001 From: Jamie Chapman Date: Wed, 3 Feb 2016 01:31:31 +0000 Subject: [PATCH] Added MailAdapter and MailgunAdapter for sending email --- MailAdapter.js | 73 +++++++++++++++++++++++++++++++++++++++++++++++ MailgunAdapter.js | 57 ++++++++++++++++++++++++++++++++++++ index.js | 10 +++++++ package.json | 1 + 4 files changed, 141 insertions(+) create mode 100644 MailAdapter.js create mode 100644 MailgunAdapter.js diff --git a/MailAdapter.js b/MailAdapter.js new file mode 100644 index 0000000000..a230661873 --- /dev/null +++ b/MailAdapter.js @@ -0,0 +1,73 @@ +// Mail Adapter +// +// Allows you to send email using a third party API such as Mailgun. +// +// To send messages: +// var service = MailAdapter.getMailService(appId); +// if(service !== null) service.sendMail('user@domain.com', 'Hello User!', 'Thanks for signing up!'); +// +// Each adapter requires:- +// * validateConfig(config) -> returns a set of configuration values for each service. Different services have different config requirements +// * sendMail(to, subject, text) -> sends a message using the configured service + +var MailgunAdapter = require('./MailgunAdapter'); + +var adapter = MailgunAdapter; +var mailConfigs = {}; +var mailServices = {}; + +function setMailServiceConfig(appId, mailApiConfig) { + + // Perform a type check on mailApiConfig to ensure it's a dictionary/object + if(typeof mailApiConfig === 'object') { + + // Ensure mailApiConfig has a least a service defined, not not — default to mailgun + if(typeof mailApiConfig.service === 'undefined' || mailApiConfig.service === '') { + console.error('Error: You need to define a `service` when configuring `mailConfig` in ParseServer.'); + mailApiConfig.service = 'mailgun'; // use mailgun as the default adapter + } + + // Set the mail service as configured + if(mailApiConfig.service === '' || mailApiConfig.service === 'mailgun') { + adapter = MailgunAdapter; + mailApiConfig = MailgunAdapter.validateConfig(mailApiConfig); + } else { + // Handle other mail adapters here... (such as mandrill, postmark, etc + } + + } else { + // Unexpected type, should be an object/dictionary. + console.log('Error: Unexpected `mailApiConfig` in MailAdapter.'); + mailApiConfig = MailgunAdapter.validateConfig({}); // Just get some empty values + return false; + } + + mailConfigs[appId] = mailApiConfig; + return true; +} + +function clearMailService(appId) { + delete mailConfigs[appId]; + delete mailServices[appId]; +} + +function getMailService(appId) { + if (mailServices[appId]) { + return mailServices[appId]; + } + + if(mailConfigs[appId] != null) { + mailServices[appId] = new adapter(appId, mailConfigs[appId]); + return mailServices[appId]; + } else { + return null; + } +} + +module.exports = { + mailConfigs: mailConfigs, + mailServices: mailServices, + setMailServiceConfig: setMailServiceConfig, + getMailService: getMailService, + clearMailService: clearMailService +}; diff --git a/MailgunAdapter.js b/MailgunAdapter.js new file mode 100644 index 0000000000..fb38bd9a58 --- /dev/null +++ b/MailgunAdapter.js @@ -0,0 +1,57 @@ +// A mail adapter for the Mailgun API + +// options can contain: +function MailgunAdapter(appId, mailApiConfig) { + this.appId = appId; + this.apiConfig = mailApiConfig; +} + +// Connects to the database. Returns a promise that resolves when the +// connection is successful. +// this.db will be populated with a Mongo "Db" object when the +// promise resolves successfully. +MailgunAdapter.prototype.sendMail = function(to, subject, text) { + + var mailgun = require('mailgun-js')({apiKey: this.apiConfig.apiKey, domain: this.apiConfig.domain}); + + var data = { + from: this.apiConfig.fromAddress, + to: to, + subject: subject, + text: text + }; + + return new Promise((resolve, reject) => { + mailgun.messages().send(data, (err, body) => { + if (typeof err !== 'undefined') { + // console.log("Mailgun Error", err); + return reject(err); + } + // console.log(body); + resolve(body); + }); + }); +}; + +MailgunAdapter.validateConfig = function(config) { + var cfg = {apiKey:'', domain:'', fromAddress:''}; + var helperMessage = "When creating an instance of ParseServer, you should have a mailConfig section like this: mailConfig: { service:'mailgun', apiKey:'MAILGUN_KEY_HERE', domain:'MAILGUN_DOMAIN_HERE', fromAddress:'MAILGUN_FROM_ADDRESS_HERE' }"; + if(typeof config.apiKey === 'undefined' || config.apiKey === '') { + console.error('Error: You need to define a MailGun `apiKey` when configuring ParseServer. ' + helperMessage); + } else { + cfg.apiKey = config.apiKey; + } + if(typeof config.domain === 'undefined' || config.domain === '') { + console.error('Error: You need to define a MailGun `domain` when configuring ParseServer. ' + helperMessage); + } else { + cfg.domain = config.domain; + } + if(typeof config.fromAddress === 'undefined' || config.fromAddress === '') { + console.error('Error: You need to define a MailGun `fromAddress` when configuring ParseServer. ' + helperMessage); + } else { + cfg.fromAddress = config.fromAddress; + } + return cfg; +}; + +module.exports = MailgunAdapter; diff --git a/index.js b/index.js index e884eb6794..9b1b42c9e3 100644 --- a/index.js +++ b/index.js @@ -7,6 +7,7 @@ var batch = require('./batch'), express = require('express'), FilesAdapter = require('./FilesAdapter'), S3Adapter = require('./S3Adapter'), + MailAdapter = require('./MailAdapter'), middlewares = require('./middlewares'), multer = require('multer'), Parse = require('parse/node').Parse, @@ -27,6 +28,11 @@ addParseCloud(); // "cloud": relative location to cloud code to require, or a function // that is given an instance of Parse as a parameter. Use this instance of Parse // to register your cloud code hooks and functions. +// "mailConfig": a dictionary of 3rd party mail service settings (such as API keys etc) +// currently only Mailgun is supported. So service, apiKey, domain and fromAddress +// are all required. Setup like mailgun: { service: 'mailgun', apiKey: 'MG_APIKEY', +// domain:'MG_DOMAIN', fromAddress:'Your App ' }. If you do not +// define mailConfig, no mail service will be setup. // "appId": the application id to host // "masterKey": the master key for requests to this app // "facebookAppIds": an array of valid Facebook Application IDs, required @@ -52,6 +58,10 @@ function ParseServer(args) { if (args.databaseURI) { DatabaseAdapter.setAppDatabaseURI(args.appId, args.databaseURI); } + if(args.mailConfig) { + MailAdapter.setMailServiceConfig(args.appId, args.mailConfig); + } + if (args.cloud) { addParseCloud(); if (typeof args.cloud === 'function') { diff --git a/package.json b/package.json index 62c6aec7e5..8fffac1def 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "deepcopy": "^0.5.0", "express": "~4.2.x", "hat": "~0.0.3", + "mailgun-js": "^0.7.7", "mime": "^1.3.4", "mongodb": "~2.0.33", "multer": "~0.1.8",