From 169c82acfc6f833a8822048fcf7cc37a4e1b19dd Mon Sep 17 00:00:00 2001
From: Berkeley Martinez <Berkeley@robotie.com>
Date: Sun, 24 Jan 2016 20:05:35 -0800
Subject: [PATCH] Add emailing on server exceptions

Keymetrics VC's are coming. They no longer let you see reported
errors or notify you on their free tier which renders the service
useless for us.

This will emailing you using mandrill with a stack trace and
context of the error when it receives it
---
 package.json                     |  3 ++-
 pm2Start.js                      | 57 ++++++++++++++++++++++++++++++++++++++++
 server/middlewares/keymetrics.js | 14 +++++++++-
 3 files changed, 72 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index 8ec11c6..07ae1bf 100644
--- a/package.json
+++ b/package.json
@@ -31,11 +31,11 @@
     "babel-core": "^6.3.26",
     "babel-eslint": "^4.1.4",
     "babel-loader": "^6.2.1",
-    "babel-register": "^6.3.0",
     "babel-plugin-add-module-exports": "^0.1.2",
     "babel-preset-es2015": "^6.3.13",
     "babel-preset-react": "^6.3.13",
     "babel-preset-stage-0": "^6.3.13",
+    "babel-register": "^6.3.0",
     "body-parser": "^1.13.2",
     "chai-jquery": "^2.0.0",
     "cheerio": "~0.19.0",
@@ -94,6 +94,7 @@
     "merge-stream": "^1.0.0",
     "method-override": "^2.3.0",
     "moment": "^2.10.2",
+    "moment-timezone": "^0.5.0",
     "mongodb": "^2.0.33",
     "morgan": "^1.6.1",
     "node-uuid": "^1.4.3",
diff --git a/pm2Start.js b/pm2Start.js
index 3b9ec6b..b0755e4 100644
--- a/pm2Start.js
+++ b/pm2Start.js
@@ -1,8 +1,22 @@
 require('dotenv').load();
 var pm2 = require('pm2');
+var nodemailer = require('nodemailer');
+var moment = require('moment-timezone');
+var _ = require('lodash');
+
 var instances = process.env.INSTANCES || 1;
 var serverName = process.env.SERVER_NAME || 'server';
 var maxMemory = process.env.MAX_MEMORY || '390M';
+var transportOptions = {
+  type: 'smtp',
+  service: 'Mandrill',
+  auth: {
+    user: process.env.MANDRILL_USER || false,
+    pass: process.env.MANDRILL_PASSWORD
+  }
+};
+
+var mailReceiver = process.env.MAIL_RECEIVER || false;
 
 pm2.connect(function() {
   pm2.start({
@@ -22,3 +36,46 @@ pm2.connect(function() {
     pm2.disconnect();
   });
 });
+
+
+if (transportOptions.auth.user && mailReceiver) {
+  console.log('setting up mailer');
+  var transporter = nodemailer.createTransport(transportOptions);
+  var compiled = _.template(
+    'An error has occurred on server ' +
+    '<% name %>\n' +
+    'Stack Trace:\n\n\n<%= stack %>\n\n\n' +
+    'Context:\n\n<%= text %>'
+  );
+
+  pm2.launchBus(function(err, bus) {
+    if (err) {
+      return console.error(err);
+    }
+    console.log('event bus connected');
+
+    bus.on('process:exception', function(data) {
+      var text;
+      var stack;
+      var name;
+      try {
+        data.date = moment(data.at || new Date())
+          .tz('America/Los_Angeles')
+          .format('MMMM Do YYYY, h:mm:ss a z');
+
+        text = JSON.stringify(data, null, 2);
+        stack = data.data.stack;
+        name = data.process.name;
+      } catch (e) {
+        return e;
+      }
+
+      transporter.sendMail({
+        to: mailReceiver,
+        from: 'team@freecodecamp.com',
+        subject: 'Server exception',
+        text: compiled({ name: name, text: text, stack: stack })
+      });
+    });
+  });
+}
diff --git a/server/middlewares/keymetrics.js b/server/middlewares/keymetrics.js
index 0902d6c..183f670 100644
--- a/server/middlewares/keymetrics.js
+++ b/server/middlewares/keymetrics.js
@@ -4,5 +4,17 @@ export default function keymetrics() {
   if (process.env.NODE_ENV !== 'production') {
     return (err, req, res, next) => next(err);
   }
-  return pmx.expressErrorHandler();
+  return (err, req, res, next) => {
+    if (res.statusCode < 400) { res.statusCode = 500; }
+
+    err.url = req.url;
+    err.component = req.url;
+    err.action = req.method;
+    err.params = req.body;
+    err.session = req.session;
+    err.username = req.user ? req.user.username : 'anonymous';
+    err.userId = req.user ? req.user.id : 'anonymous';
+
+    return next(pmx.notify(err));
+  };
 }
