Tracking emails and storing tracking data

So far, we have discussed the architecture of our email sender and how we can build a scalable system which can send emails in large volumes.

Now, it's time to track those emails. Let's build and code a program that can track the email open rate, location, IP, device, that is, computer, laptop or phone, and agent, that is, browser.

We are also going to insert that data in the MongoDB database or Cosmos DB.

In the last section, we added the tracking code, which was an image tag using our API call as its source, into the email. Our API call is going to return the image.

Let's develop a sample code which will do the same thing.

Initiate a new Node.js project and install the following dependency:

npm i --S express 

Here is the sample code of the API:

const express = require('express'); 
const app = express(); 
 
app.get('/api/track',function(req,res) { 
   console.log('tracking code'+JSON.stringify(req.query)); 
   // sending the image back. 
   var buf = new Buffer([ 
      0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00, 
      0x80, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x2c, 
      0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 
      0x02, 0x44, 0x01, 0x00, 0x3b 
  ]); 
  res.set('Content-Type', 'image/png'); 
  res.end(buf,'binary'); 
}); 
 
app.listen(3000); 
console.log("Started at 3000"); 

As you can see in the preceding code, we have exposed the GET API on the same path, that is, /api/track, and have returned the image.

Don't be afraid of the buffer: it is just a 1 x 1 image binary code. Basically, we need to send the image back to the browser, so that browser doesn't give a 404 error for the image. This image should be transparent and as small as possible, so as not to conflict with the actual email.

Now, let's try to extract the tracking information from the IP and HTTP request.

First, we need the IP.

You can get the IP from the request header. It can be in the following keys, depending upon the network, so we are adding all of them to the exception. See the following code:

let ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress || req.socket.remoteAddress ||req.connection.socket.remoteAddress; 

Next, we need the device type, location, and agent. We need to install an extra dependency for that. Run the following command to install those dependencies:

npm i --S geoip-lite express-device useragent 

Let's declare them in the code:

const geoip = require('geoip-lite'); 
const device = require('express-device'); 
const useragent = require('useragent')(true); 

Add the user agent middleware in the express, so that it captures every request:

app.use(device.capture({parseUserAgent: true})); 

We can extract the information from the request header, using the following code:

var agent = useragent.parse(req.headers['user-agent']); 
var ua = useragent.is(req.headers['user-agent']) 
var geoLocation = geoip.lookup(ip); 

We can extract the device from the request object using the req.device key. Refer to the complete code, as follows:

const express = require('express'); 
const geoip = require('geoip-lite'); 
const device = require('express-device'); 
var useragent = require('useragent')(true); 
const app = express(); 
 
app.use(device.capture({parseUserAgent: true})); 
app.enable('trust proxy'); 
 
app.get('/api/track',function(req,res) { 
   console.log('tracking code'+JSON.stringify(req.query)); 
   let ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress || req.socket.remoteAddress ||req.connection.socket.remoteAddress; 
   var agent = useragent.parse(req.headers['user-agent']); 
   var ua = useragent.is(req.headers['user-agent']) 
   let geoLocation = geoip.lookup(ip); 
   var buf = new Buffer([ 
      0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00, 
      0x80, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x2c, 
      0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 
      0x02, 0x44, 0x01, 0x00, 0x3b 
  ]); 
  res.set('Content-Type', 'image/png'); 
  res.end(buf,'binary'); 
  let trackData = { 
      'trackingCode': JSON.stringify(req.query), 
      'ip': ip, 
      'deviceType': req.device.type, 
      'deviceName': req.device.name, 
      'device': req.device, 
      'agent': agent, 
      'location': geoLocation 
  }; 
  console.log(trackData); 
}); 
 
app.listen(3000); 
console.log("Started at 3000"); 

Now, whenever someone opens up the email, this API will be triggered and you will receive all the information required for tracking.

Let's see if it really works. Run the code and try to hit this API from the browser. If you are running it from your machine, then it will return localhost:3000/api/track?code=1234456 and in terminal, something like this will be printed:

As you can see, we receive the information about device type, browser, and IP. Since I was running it in my local machine, the location retrieval was disabled. It works well on the server.

In the browser, you should see something similar to this:

That's the 1 x 1 image we returned from our code.

So, the tracking code works; what we need now is to add it to the database. As discussed, we are going to use the MongoDB API of Cosmos DB.

So, in order to use that, we first need to create the database. Go to the Azure portal and choose Cosmos DB from the left corner and create a new MongoDB API database and a new collection.

Once it is created, copy the access keys from this screen:

It's time to add the code!

First, install the dependency. Run the following command in the project directory:

npm i --S mongoose 

Create the configuration file and add this piece of code:

var config = {}; 
config.db = {}; 
config.db.uri = 'mongodb://packt-mongodb-2:lTsYGMpczByW0VB7kxCoAPZdSAKdDsJ4GVqWlvLWuVBDlX9XrPAXBwaHOLQWk9HWM5VtRDKOubqE6WN2U3b2ww==@packt-mongodb-2.documents.azure.com:10255/?ssl=true&replicaSet=globaldb'; 
config.db.host = 'packt-mongodb-2.documents.azure.com'; 
config.db.name = tracking; 
module.exports = config; 

Make sure you change the values to match your access keys.

Let's make the connection. Create a file, name it connection.js, and add the following code:

const mongoose = require('mongoose'); 
const config = require('./config.js'); 
mongoose.connect(config.db.uri); 
module.exports = mongoose.connection; 

Now, let us create the model code. To do so, create a new file, name it trackmodel.js, and place this code in it:

var mongoose = require('mongoose'); 
var connection = require('./connection'); 
var Schema = mongoose.Schema; 
 
connection.once('open',function() { 
  console.log("Connected...."); 
}); 
 
// create a schema for our links 
var trackSchema = new Schema({ 
  data: Array, 
  created_at: Date 
}); 
 
module.exports = mongoose.model('track', trackSchema); 

Let's add the model code to our application code and start saving the information in our collection.

Replace the collection name with the one you created at your end.

Add the following code to the dependency declaration:

var model = require('./trackModel'); 

Now, in the router, add this code at the end of the existing file:

var trackInfo = model({data: trackData}) 
trackInfo.save(function(err) { 
      if (err) { 
          console.log(err); 
      } 
}) 

Here is the complete code, for your reference:

const express = require('express'); 
const geoip = require('geoip-lite'); 
const device = require('express-device'); 
var useragent = require('useragent'); 
useragent(true); 
var model = require('./trackModel'); 
const app = express(); 
 
app.use(device.capture({parseUserAgent: true})); 
app.enable('trust proxy'); 
 
app.get('/api/track',function(req,res) { 
   console.log('tracking code'+JSON.stringify(req.query)); 
   let ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress || req.socket.remoteAddress ||req.connection.socket.remoteAddress; 
   var agent = useragent.parse(req.headers['user-agent']); 
   var ua = useragent.is(req.headers['user-agent']) 
   let geoLocation = geoip.lookup(ip); 
   var buf = new Buffer([ 
      0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00, 
      0x80, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x2c, 
      0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 
      0x02, 0x44, 0x01, 0x00, 0x3b 
  ]); 
  res.set('Content-Type', 'image/png'); 
  res.end(buf,'binary'); 
  let trackData = { 
    'trackingCode': JSON.stringify(req.query), 
    'ip': ip, 
    'deviceType': req.device.type, 
    'deviceName': req.device.name, 
    'device': req.device, 
    'agent': agent, 
    'location': geoLocation 
  }; 
  var trackInfo = model({data: trackData}) 
  trackInfo.save(function(err) { 
      if (err){ 
        console.log(err); 
      } 
  }) 
  console.log(trackData); 
}); 
 
app.listen(3000); 
console.log("Started at 3000"); 

Now, run the code and hit the same API, that is, http://localhost:3000/api/track?code=1234456. You should now see the data in your Azure collection.

Congratulations! Now you can send any number of emails, add that tracking code to them, track the data, and store it using the code that we have developed.

I hope you have gained some good insights about the email tracking use case in this chapter.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.228.19