Vianney Lecroart - acemtp Playground

Note: dear readers from the future!: at the time of writing the released meteor version is 0.5.0; as Meteor is a rapidly changing framework, it is likely this post is out of date!

There’s a doc on Meteor website about email but I encounter some little problems when I tried to setup email. So I created this step by step tutorial to help other not having the same problems.

I use MailGun in this example because it’s what Meteor already uses, because it’s cool (old YCombinator company), and also because you can send up to 200 email per day for Free! But it should work with any other SMTP servers.

Let say your project name is meteorize.

First create an account on MailGun. Set ‘meteorize’ as domain name then follow the instruction and validate the mail MailGun sent to you.

Setup the MAIL_URL in the server startup. If you don’t setup it, the mail will be displayed in text in the stdout of the server, it’s quite useful for debugging.

Meteor.startup(function () {
  process.env.MAIL_URL = 'smtp://postmaster%40meteorize.mailgun.org:YOURPASSWORD@smtp.mailgun.org:587';
});

Notice that MailGun puts a @ character in the login. You need to replace it with a %40.

You have to change YOURPASSWORD. You’ll find it in the MailGun admin interface, in the Domain tab. Select your domain and on the right of the interface, you’ll find the password. It’s not the password you use to create your account.

You should use the port 587, because some ISP (like mine) block the port 25 so you’ll have a ECONNREFUSED in meteor when it tries to send the email:

Exception while invoking method 'invite' Error: connect ECONNREFUSED
    at Object.Future.wait (/usr/local/meteor/lib/node_modules/fibers/future.js:322:15)
    at smtpSend (app/packages/email/email.js:66:43)
    at Object.Email.send (app/packages/email/email.js:108:7)
   ...

Add this function that try to find an email in the user object:

var contactEmail = function (user) {
  if (user.emails && user.emails.length)
    return user.emails[0].address;
  if (user.services && user.services.facebook && user.services.facebook.email)
    return user.services.facebook.email;
  return null;
};

It would be nice if Meteor could provide in the email package a function like this one that look in all services (this function only checks on Facebook, you have to extend the function to look at other available services).

And now here is an example of a function that can be used if a user wants to send a mail to another user (this examples was taken from the Meteor parties sample):

var sendMessage = function (fromId, toId, msg) {
  var from = Meteor.users.findOne(fromId);
  var to = Meteor.users.findOne(toId);
  var fromEmail = contactEmail(from);
  var toEmail = contactEmail(to);
  Email.send({
    from: fromEmail,
    to: toEmail,
    replyTo: fromEmail || undefined,
    subject: "Meteorize: "+from.username+" sent you this email !",
    text: "Hello "+to.username+",\n\n"+msg+
    "Thank you to use Meteorize!\n\n"+
    "The Meteorize Team.\n"+
    Meteor.absoluteUrl()+"\n";
  });
}

The function is really straightforward. It’s not mandatory but you should put this function in a file inside the server/ directory to be sure that the client cannot read it.

Now, we add a function “sendMessage” in Meteor.methods() so the client can call the function directly (RPC):

Meteor.methods({
  'sendMessage': function (toId, msg) {
    if (Meteor.isServer)
  sendMessage(this.userId, toId, msg);
 }
});

Note that the ‘from’ is ‘this.userId’ so we are sure the client cannot hack the ‘from’.

Imagine that the client displays a dialog box with a textarea to enter the message and a send button, you can add a click event to send the message this way:

Template.sendMessageDialog.events({
  'click .sendMessage' : function () {
    var msg = document.getElementById("message").value;
    Meteor.call('sendMessage', Session.get("toId"), msg);
  }
});

It just takes the message from the textarea, takes the recipient from the Session (I could use a hidden input too) and calls the sendMessage function.

Voila, it works locally and deployed on meteor.com.

The full code:

if (Meteor.isServer) {
  Meteor.startup(function () {
    process.env.MAIL_URL = 'smtp://postmaster%40meteorize.mailgun.org:YOURPASSWORD@smtp.mailgun.org:587';
  });
}
if (Meteor.isClient) {
  Template.sendMessageDialog.events({
    'click .sendMessage' : function () {
      var msg = document.getElementById("message").value;
      Meteor.call('sendMessage', Session.get("toId"), msg);
    }
  });
}
var contactEmail = function (user) {
  if (user.emails && user.emails.length)
    return user.emails[0].address;
  if (user.services && user.services.facebook && user.services.facebook.email)
    return user.services.facebook.email;
  return null;
};
var sendMessage = function (fromId, toId, msg) {
  var from = Meteor.users.findOne(fromId);
  var to = Meteor.users.findOne(toId);
  var fromEmail = contactEmail(from);
  var toEmail = contactEmail(to);
  Email.send({
    from: fromEmail,
    to: toEmail,
    replyTo: fromEmail || undefined,
    subject: "Meteorize: "+from.username+" sent you this email !",
    text: "Hello "+to.username+",\n\n"+msg+
    "Thank you to use Meteorize!\n\n"+
    "The Meteorize Team.\n"+
    Meteor.absoluteUrl()+"\n";
  });
}
Meteor.methods({
  'sendMessage': function (toId, msg) {
    if (Meteor.isServer)
      sendMessage(this.userId, toId, msg);
  }
});
  1. acemtp a publié ce billet
Les commentaires de ce blog sont propulsés par Disqus