mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Basic email support
This commit is contained in:
@@ -20,10 +20,9 @@
|
||||
// matches exclude, and if it doesn't exist yet, you should watch for
|
||||
// it to appear)
|
||||
//
|
||||
// The application launcher is expected to execute /main.js with node,
|
||||
// setting the PORT and MONGO_URL environment variables. The enclosed
|
||||
// node application is expected to do the rest, including serving
|
||||
// /static.
|
||||
// The application launcher is expected to execute /main.js with node, setting
|
||||
// various environment variables (such as PORT and MONGO_URL). The enclosed node
|
||||
// application is expected to do the rest, including serving /static.
|
||||
|
||||
var files = require('./files.js');
|
||||
var packages = require('./packages.js');
|
||||
@@ -578,6 +577,7 @@ _.extend(Bundle.prototype, {
|
||||
" $ npm install fibers\n" +
|
||||
" $ export MONGO_URL='mongodb://user:password@host:port/databasename'\n" +
|
||||
" $ export ROOT_URL='http://example.com'\n" +
|
||||
" $ export MAIL_URL='smtp://user:password@mailhost:port/'\n" +
|
||||
" $ node main.js\n" +
|
||||
"\n" +
|
||||
"Use the PORT environment variable to set the port where the\n" +
|
||||
|
||||
@@ -66,8 +66,9 @@ var run = function () {
|
||||
|
||||
// check environment
|
||||
var port = process.env.PORT ? parseInt(process.env.PORT) : 80;
|
||||
var mongo_url = process.env.MONGO_URL;
|
||||
if (!mongo_url)
|
||||
|
||||
// check for a valid MongoDB URL right away
|
||||
if (!process.env.MONGO_URL)
|
||||
throw new Error("MONGO_URL must be set in environment");
|
||||
|
||||
// webserver
|
||||
@@ -88,9 +89,6 @@ var run = function () {
|
||||
Fiber(function () {
|
||||
// (put in a fiber to let Meteor.db operations happen during loading)
|
||||
|
||||
// pass in database info
|
||||
__meteor_bootstrap__.mongo_url = mongo_url;
|
||||
|
||||
// load app code
|
||||
_.each(info.load, function (filename) {
|
||||
var code = fs.readFileSync(path.join(bundle_dir, filename));
|
||||
|
||||
@@ -539,6 +539,9 @@ server, we can recommend our friends at [MongoHQ](http://mongohq.com).
|
||||
|
||||
$ PORT=3000 MONGO_URL=mongodb://localhost:27017/myapp node bundle/main.js
|
||||
|
||||
Other packages may require other environment variables (for example, the `email`
|
||||
package requires a `MAIL_URL` environment variable).
|
||||
|
||||
{{#warning}}
|
||||
For now, bundles will only run on the platform that the bundle was
|
||||
created on. To run on a different platform, you'll need to rebuild
|
||||
|
||||
111
packages/email/email.js
Normal file
111
packages/email/email.js
Normal file
@@ -0,0 +1,111 @@
|
||||
Email = {};
|
||||
|
||||
(function () {
|
||||
var Future = __meteor_bootstrap__.require('fibers/future');
|
||||
// js2-mode AST blows up when parsing 'future.return()', so alias.
|
||||
Future.prototype.ret = Future.prototype.return;
|
||||
var urlModule = __meteor_bootstrap__.require('url');
|
||||
var MailComposer = __meteor_bootstrap__.require('mailcomposer').MailComposer;
|
||||
|
||||
var makePool = function (mailUrlString) {
|
||||
var mailUrl = urlModule.parse(mailUrlString);
|
||||
if (mailUrl.protocol !== 'smtp:')
|
||||
throw new Error("Email protocol in $MAIL_URL (" +
|
||||
mailUrlString + ") must be 'smtp'");
|
||||
|
||||
var port = +(mailUrl.port);
|
||||
var auth = false;
|
||||
if (mailUrl.auth) {
|
||||
var parts = mailUrl.auth.split(':', 2);
|
||||
auth = {user: parts[0] && decodeURIComponent(parts[0]),
|
||||
pass: parts[1] && decodeURIComponent(parts[1])};
|
||||
}
|
||||
|
||||
var simplesmtp = __meteor_bootstrap__.require('simplesmtp');
|
||||
return simplesmtp.createClientPool(
|
||||
port, // Defaults to 25
|
||||
mailUrl.hostname, // Defaults to "localhost"
|
||||
{ secureConnection: (port === 465),
|
||||
// XXX allow maxConnections to be configured?
|
||||
auth: auth });
|
||||
};
|
||||
|
||||
var smtpPool = null;
|
||||
if (process.env.MAIL_URL) {
|
||||
smtpPool = makePool(process.env.MAIL_URL);
|
||||
};
|
||||
|
||||
Email._next_devmode_mail_id = 0;
|
||||
|
||||
// Overridden by tests.
|
||||
Email._output_stream = process.stdout;
|
||||
|
||||
var devModeSend = function (mc) {
|
||||
var devmode_mail_id = Email._next_devmode_mail_id++;
|
||||
|
||||
// This approach does not prevent other writers to stdout from interleaving.
|
||||
Email._output_stream.write("====== BEGIN MAIL #" + devmode_mail_id +
|
||||
" ======\n");
|
||||
mc.streamMessage();
|
||||
mc.pipe(Email._output_stream, {end: false});
|
||||
var future = new Future;
|
||||
mc.on('end', function () {
|
||||
Email._output_stream.write("====== END MAIL #" + devmode_mail_id +
|
||||
" ======\n");
|
||||
future.ret();
|
||||
});
|
||||
future.wait();
|
||||
};
|
||||
|
||||
var smtpSend = function (mc) {
|
||||
var future = new Future;
|
||||
smtpPool.sendMail(mc, function (err, responseObj) {
|
||||
future.ret([err, responseObj]);
|
||||
});
|
||||
var errAndResponse = future.wait();
|
||||
// XXX figure out error handling
|
||||
if (errAndResponse[0])
|
||||
throw errAndResponse[0];
|
||||
console.log(errAndResponse[1]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Send an email.
|
||||
*
|
||||
* Connects to the mail server configured via the MAIL_URL environment
|
||||
* variable. If unset, prints formatted message to stdout. May yield.
|
||||
*
|
||||
* @param options
|
||||
* @param options.from {String} RFC5322 "From:" address
|
||||
* @param options.to {String|String[]} RFC5322 "To:" address[es]
|
||||
* @param options.cc {String|String[]} RFC5322 "Cc:" address[es]
|
||||
* @param options.bcc {String|String[]} RFC5322 "Bcc:" address[es]
|
||||
* @param options.replyTo {String|String[]} RFC5322 "Reply-To:" address[es]
|
||||
* @param options.subject {String} RFC5322 "Subject:" line
|
||||
* @param options.text {String} RFC5322 mail body (plain text)
|
||||
*/
|
||||
Email.send = function (options) {
|
||||
var mc = new MailComposer();
|
||||
|
||||
// setup message data
|
||||
// XXX support HTML body
|
||||
// XXX support attachments
|
||||
// XXX support arbitrary headers
|
||||
mc.setMessageOption({
|
||||
from: options.from,
|
||||
to: options.to,
|
||||
cc: options.cc,
|
||||
bcc: options.bcc,
|
||||
replyTo: options.replyTo,
|
||||
subject: options.subject,
|
||||
text: options.text
|
||||
});
|
||||
|
||||
if (smtpPool) {
|
||||
smtpSend(mc);
|
||||
} else {
|
||||
devModeSend(mc);
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
33
packages/email/email_tests.js
Normal file
33
packages/email/email_tests.js
Normal file
@@ -0,0 +1,33 @@
|
||||
streamBuffers = __meteor_bootstrap__.require('stream-buffers');
|
||||
|
||||
Tinytest.add("email - dev mode smoke test", function (test) {
|
||||
var old_stream = Email._output_stream;
|
||||
try {
|
||||
Email._output_stream = new streamBuffers.WritableStreamBuffer;
|
||||
Email._next_devmode_mail_id = 0;
|
||||
Email.send({
|
||||
from: "foo@example.com",
|
||||
to: "bar@example.com",
|
||||
cc: ["friends@example.com", "enemies@example.com"],
|
||||
subject: "This is the subject",
|
||||
text: "This is the body\nof the message\nFrom us."
|
||||
});
|
||||
// XXX brittle if mailcomposer changes header order, etc
|
||||
test.equal(Email._output_stream.getContentsAsString("utf8"),
|
||||
"====== BEGIN MAIL #0 ======\n" +
|
||||
"MIME-Version: 1.0\r\n" +
|
||||
"From: foo@example.com\r\n" +
|
||||
"To: bar@example.com\r\n" +
|
||||
"Cc: friends@example.com, enemies@example.com\r\n" +
|
||||
"Subject: This is the subject\r\n" +
|
||||
"Content-Type: text/plain; charset=utf-8\r\n" +
|
||||
"Content-Transfer-Encoding: quoted-printable\r\n" +
|
||||
"\r\n" +
|
||||
"This is the body\r\n" +
|
||||
"of the message\r\n" +
|
||||
"From us.\r\n" +
|
||||
"====== END MAIL #0 ======\n");
|
||||
} finally {
|
||||
Email._output_stream = old_stream;
|
||||
}
|
||||
});
|
||||
13
packages/email/package.js
Normal file
13
packages/email/package.js
Normal file
@@ -0,0 +1,13 @@
|
||||
Package.describe({
|
||||
summary: "Send email messages"
|
||||
});
|
||||
|
||||
Package.on_use(function (api) {
|
||||
api.add_files('email.js', 'server');
|
||||
});
|
||||
|
||||
Package.on_test(function (api) {
|
||||
api.use('email', 'server');
|
||||
api.use('tinytest');
|
||||
api.add_files('email_tests.js', 'server');
|
||||
});
|
||||
@@ -17,4 +17,4 @@ _.extend(Meteor._RemoteCollectionDriver.prototype, {
|
||||
|
||||
// singleton
|
||||
// XXX kind of hacky
|
||||
Meteor._RemoteCollectionDriver = new Meteor._RemoteCollectionDriver(__meteor_bootstrap__.mongo_url);
|
||||
Meteor._RemoteCollectionDriver = new Meteor._RemoteCollectionDriver(process.env.MONGO_URL);
|
||||
|
||||
Reference in New Issue
Block a user