mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Improve comment docs about newly accessible email hook. I believe this will fix the issue with docs generation.
163 lines
5.5 KiB
JavaScript
163 lines
5.5 KiB
JavaScript
var Future = Npm.require('fibers/future');
|
|
var urlModule = Npm.require('url');
|
|
var nodemailer = Npm.require('nodemailer');
|
|
|
|
Email = {};
|
|
EmailTest = {};
|
|
|
|
EmailInternals = {
|
|
NpmModules: {
|
|
mailcomposer: {
|
|
version: Npm.require('nodemailer/package.json').version,
|
|
module: Npm.require('nodemailer/lib/mail-composer')
|
|
},
|
|
nodemailer: {
|
|
version: Npm.require('nodemailer/package.json').version,
|
|
module: Npm.require('nodemailer')
|
|
}
|
|
}
|
|
};
|
|
|
|
var MailComposer = EmailInternals.NpmModules.mailcomposer.module;
|
|
|
|
var makeTransport = function (mailUrlString) {
|
|
var mailUrl = urlModule.parse(mailUrlString, true);
|
|
|
|
if (mailUrl.protocol !== 'smtp:' && mailUrl.protocol !== 'smtps:') {
|
|
throw new Error("Email protocol in $MAIL_URL (" +
|
|
mailUrlString + ") must be 'smtp' or 'smtps'");
|
|
}
|
|
|
|
if (mailUrl.protocol === 'smtp:' && mailUrl.port === '465') {
|
|
Meteor._debug("The $MAIL_URL is 'smtp://...:465'. " +
|
|
"You probably want 'smtps://' (The 's' enables TLS/SSL) " +
|
|
"since '465' is typically a secure port.");
|
|
}
|
|
|
|
// Allow overriding pool setting, but default to true.
|
|
if (!mailUrl.query) {
|
|
mailUrl.query = {};
|
|
}
|
|
|
|
if (!mailUrl.query.pool) {
|
|
mailUrl.query.pool = 'true';
|
|
}
|
|
|
|
var transport = nodemailer.createTransport(
|
|
urlModule.format(mailUrl));
|
|
|
|
transport._syncSendMail = Meteor.wrapAsync(transport.sendMail, transport);
|
|
return transport;
|
|
};
|
|
|
|
var getTransport = function() {
|
|
// We delay this check until the first call to Email.send, in case someone
|
|
// set process.env.MAIL_URL in startup code. Then we store in a cache until
|
|
// process.env.MAIL_URL changes.
|
|
var url = process.env.MAIL_URL;
|
|
if (this.cacheKey === undefined || this.cacheKey !== url) {
|
|
this.cacheKey = url;
|
|
this.cache = url ? makeTransport(url) : null;
|
|
}
|
|
return this.cache;
|
|
}
|
|
|
|
var nextDevModeMailId = 0;
|
|
var output_stream = process.stdout;
|
|
|
|
// Testing hooks
|
|
EmailTest.overrideOutputStream = function (stream) {
|
|
nextDevModeMailId = 0;
|
|
output_stream = stream;
|
|
};
|
|
|
|
EmailTest.restoreOutputStream = function () {
|
|
output_stream = process.stdout;
|
|
};
|
|
|
|
var devModeSend = function (mail) {
|
|
var devModeMailId = nextDevModeMailId++;
|
|
|
|
var stream = output_stream;
|
|
|
|
// This approach does not prevent other writers to stdout from interleaving.
|
|
stream.write("====== BEGIN MAIL #" + devModeMailId + " ======\n");
|
|
stream.write("(Mail not sent; to enable sending, set the MAIL_URL " +
|
|
"environment variable.)\n");
|
|
var readStream = new MailComposer(mail).compile().createReadStream();
|
|
readStream.pipe(stream, {end: false});
|
|
var future = new Future;
|
|
readStream.on('end', function () {
|
|
stream.write("====== END MAIL #" + devModeMailId + " ======\n");
|
|
future.return();
|
|
});
|
|
future.wait();
|
|
};
|
|
|
|
var smtpSend = function (transport, mail) {
|
|
transport._syncSendMail(mail);
|
|
};
|
|
|
|
var sendHooks = [];
|
|
|
|
/**
|
|
* @summary Hook that runs before email is sent.
|
|
* @locus Server
|
|
*
|
|
* @param f {function} receives the arguments to Email.send and should return true to go
|
|
* ahead and send the email (or at least, try subsequent hooks), or
|
|
* false to skip sending.
|
|
*/
|
|
Email.hookSend = function (f) {
|
|
sendHooks.push(f);
|
|
};
|
|
|
|
/**
|
|
* @summary Send an email. Throws an `Error` on failure to contact mail server
|
|
* or if mail server returns an error. All fields should match
|
|
* [RFC5322](http://tools.ietf.org/html/rfc5322) specification.
|
|
*
|
|
* If the `MAIL_URL` environment variable is set, actually sends the email.
|
|
* Otherwise, prints the contents of the email to standard out.
|
|
*
|
|
* Note that this package is based on **nodemailer**, so make sure to refer to
|
|
* [the documentation](http://nodemailer.com/)
|
|
* when using the `attachments` or `mailComposer` options.
|
|
*
|
|
* @locus Server
|
|
* @param {Object} options
|
|
* @param {String} [options.from] "From:" address (required)
|
|
* @param {String|String[]} options.to,cc,bcc,replyTo
|
|
* "To:", "Cc:", "Bcc:", and "Reply-To:" addresses
|
|
* @param {String} [options.inReplyTo] Message-ID this message is replying to
|
|
* @param {String|String[]} [options.references] Array (or space-separated string) of Message-IDs to refer to
|
|
* @param {String} [options.messageId] Message-ID for this message; otherwise, will be set to a random value
|
|
* @param {String} [options.subject] "Subject:" line
|
|
* @param {String} [options.text|html] Mail body (in plain text and/or HTML)
|
|
* @param {String} [options.watchHtml] Mail body in HTML specific for Apple Watch
|
|
* @param {String} [options.icalEvent] iCalendar event attachment
|
|
* @param {Object} [options.headers] Dictionary of custom headers - e.g. `{ "header name": "header value" }`. To set an object under a header name, use `JSON.stringify` - e.g. `{ "header name": JSON.stringify({ tracking: { level: 'full' } }) }`.
|
|
* @param {Object[]} [options.attachments] Array of attachment objects, as
|
|
* described in the [nodemailer documentation](https://nodemailer.com/message/attachments/).
|
|
* @param {MailComposer} [options.mailComposer] A [MailComposer](https://nodemailer.com/extras/mailcomposer/#e-mail-message-fields)
|
|
* object representing the message to be sent. Overrides all other options.
|
|
* You can create a `MailComposer` object via
|
|
* `new EmailInternals.NpmModules.mailcomposer.module`.
|
|
*/
|
|
Email.send = function (options) {
|
|
for (var i = 0; i < sendHooks.length; i++)
|
|
if (! sendHooks[i](options))
|
|
return;
|
|
|
|
if (options.mailComposer) {
|
|
options = options.mailComposer.mail;
|
|
}
|
|
|
|
var transport = getTransport();
|
|
if (transport) {
|
|
smtpSend(transport, options);
|
|
} else {
|
|
devModeSend(options);
|
|
}
|
|
};
|