0% found this document useful (0 votes)
8 views3 pages

Email Deliverability Backend Tool

The document outlines the backend code for an Email Deliverability Tool, including configurations in package.json, environment variables, and server setup in server.js. It features routes for managing inboxes and tests, as well as models for storing test results in MongoDB. The tool integrates with Gmail and Outlook APIs to check email deliverability and sends reports via email upon completion.

Uploaded by

hareethshree
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
8 views3 pages

Email Deliverability Backend Tool

The document outlines the backend code for an Email Deliverability Tool, including configurations in package.json, environment variables, and server setup in server.js. It features routes for managing inboxes and tests, as well as models for storing test results in MongoDB. The tool integrates with Gmail and Outlook APIs to check email deliverability and sends reports via email upon completion.

Uploaded by

hareethshree
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Backend Code - Email Deliverability Tool

Generated: 2025-10-16T16:47:55.627574 UTC

--- [Link] ---


{ "name": "email-deliverability-backend", "version": "1.0.0", "main": "[Link]", "scripts": {
"start": "node [Link]", "dev": "nodemon [Link]" }, "dependencies": { "axios": "^1.4.0", "body-
parser": "^1.20.2", "express": "^4.18.2", "mongoose": "^7.0.0", "nodemailer": "^6.9.3", "uuid": "^9.0.0",
"dotenv": "^16.0.0", "googleapis": "^126.0.0", "@azure/msal-node": "^2.0.0", "node-fetch": "^2.6.7" },
"devDependencies": { "nodemon": "^2.0.0" } }

--- .[Link] ---


PORT=4000 BASE_URL=[Link]
MONGODB_URI=mongodb+srv://<user>:<pw>@[Link]/emailtool?retryWrites=true&w=majority
GMAIL_CLIENT_ID=your-google-client-id GMAIL_CLIENT_SECRET=your-google-client-secret
GMAIL_REFRESH_TOKEN_INBOX1=... GMAIL_REFRESH_TOKEN_INBOX2=... OUTLOOK_CLIENT_ID=your-azure-client-id
OUTLOOK_CLIENT_SECRET=your-azure-client-secret OUTLOOK_REFRESH_TOKEN_INBOX1=... SMTP_HOST=[Link]
SMTP_PORT=587 SMTP_USER=apikey SMTP_PASS=your_sendgrid_api_key FROM_EMAIL=no-reply@[Link]
POLL_INTERVAL_MS=5000 POLL_TIMEOUT_MS=300000

--- [Link] ---


require('dotenv').config(); const app = require('./src/app'); const mongoose = require('mongoose'); const PORT
= [Link] || 4000; async function start(){ if(![Link].MONGODB_URI){ [Link]('MONGODB_URI
not set'); [Link](1);} await [Link]([Link].MONGODB_URI); [Link]('Connected to
MongoDB'); [Link](PORT, ()=>[Link](`Server listening on ${PORT}`)); } start();

--- src/[Link] ---


const express = require('express'); const bodyParser = require('body-parser'); const cors = require('cors');
const inboxesRouter = require('./routes/inboxes'); const testsRouter = require('./routes/tests'); const app =
express(); [Link](cors()); [Link]([Link]()); [Link]('/api/inboxes', inboxesRouter);
[Link]('/api/tests', testsRouter); [Link]('/', (req,res)=>[Link]('Email Deliverability Tool API'));
[Link] = app;

--- src/models/[Link] ---


const mongoose = require('mongoose'); const InboxResult = new [Link]({ provider: String, address:
String, received: { type: Boolean, default: false }, folder: { type: String, default: null }, messageId: {
type: String, default: null }, checkedAt: { type: Date, default: null } }); const TestSchema = new
[Link]({ testCode: String, userEmail: String, status: { type: String, enum:
['pending','completed','failed'], default: 'pending' }, createdAt: { type: Date, default: [Link] }, inboxes:
[InboxResult], reportUrl: String, score: Number }); [Link] = [Link]('Test', TestSchema);

--- src/routes/[Link] ---


const express=require('express'); const router=[Link](); const TEST_INBOXES=[
{provider:'gmail',address:'test1+inbox@[Link]'}, {provider:'gmail',address:'test2+inbox@[Link]'},
{provider:'outlook',address:'test3@[Link]'}, {provider:'outlook',address:'test4@[Link]'},
{provider:'custom',address:'test5@[Link]'} ]; [Link]('/',(req,res)=>[Link](TEST_INBOXES));
[Link]=router;

--- src/routes/[Link] ---


const express=require('express'); const router=[Link](); const Test=require('../models/Test'); const
generateCode=require('../utils/generateCode'); const poller=require('../services/poller');
[Link]('/',async(req,res)=>{ try{ const {userEmail}=[Link]; if(!userEmail) return
[Link](400).json({error:'userEmail required'}); const testCode=generateCode(); const inboxes=[
{provider:'gmail',address:'test1+inbox@[Link]'}, {provider:'gmail',address:'test2+inbox@[Link]'},
{provider:'outlook',address:'test3@[Link]'}, {provider:'outlook',address:'test4@[Link]'},
{provider:'custom',address:'test5@[Link]'} ]; const test=new Test({testCode,userEmail,inboxes}); await
[Link](); [Link]({testId:test._id,testCode,inboxes}); }catch(err){ [Link]('Create test
error',err); [Link](500).json({error:'server error'}); }}); [Link]('/:testId/start',async(req,res)=>{
try{ const {testId}=[Link]; const test=await [Link](testId); if(!test) return
[Link](404).json({error:'Test not found'}); [Link](test._id.toString());
[Link]({message:'Polling started'}); }catch(err){ [Link]('Start test error',err);
[Link](500).json({error:'server error'}); }}); [Link]('/:testId',async(req,res)=>{ try{ const
test=await [Link]([Link]); if(!test) return [Link](404).json({error:'Test not found'});
[Link](test); }catch(err){ [Link]('Get test error',err); [Link](500).json({error:'server
error'}); }}); [Link]=router;

--- src/utils/[Link] ---


const {v4:uuidv4}=require('uuid'); [Link]=function generateCode(){ return
uuidv4().split('-')[0].toUpperCase(); }

--- src/utils/[Link] ---


const nodemailer=require('nodemailer'); const transporter=[Link]({
host:[Link].SMTP_HOST, port:parseInt([Link].SMTP_PORT||'587'), auth:{ user:[Link].SMTP_USER,
pass:[Link].SMTP_PASS } }); async function sendReportEmail(to,subject,html){ const info=await
[Link]({ from:[Link].FROM_EMAIL, to, subject, html }); return info; }
[Link]={sendReportEmail};

--- src/services/[Link] ---


const Test=require('../models/Test'); const gmailClient=require('./mailClients/gmailClient'); const
outlookClient=require('./mailClients/outlookClient'); const emailSender=require('../utils/emailSender'); const
POLL_INTERVAL_MS=parseInt([Link].POLL_INTERVAL_MS||'5000'); const
POLL_TIMEOUT_MS=parseInt([Link].POLL_TIMEOUT_MS||'300000'); const activePolls=new Map(); async function
checkInboxForCode(inbox,testCode){ const provider=[Link]; try{ if(provider==='gmail') return await
[Link]([Link],testCode); if(provider==='outlook') return await
[Link]([Link],testCode); return {found:false}; }catch(err){
[Link]('checkInboxForCode error',err); return {found:false}; }} async function pollOnce(testId){ const
testDoc=await [Link](testId); if(!testDoc) return; let changed=false; for(let
i=0;i<[Link];i++){ const inbox=[Link][i]; if([Link]) continue; const
res=await checkInboxForCode(inbox,[Link]); if(res&&[Link]){ [Link]=true;
[Link]=[Link]||'Unknown'; [Link]=[Link]||null; [Link]=new Date(); changed=true; }
} if(changed) await [Link](); const allChecked=[Link](i=>[Link]===true);
if(allChecked){ [Link]='completed';
[Link]=[Link](([Link](i=>[Link]==='Inbox').length/[Link])*100);
[Link]=`${[Link].BASE_URL||'[Link] await
[Link](); const html=`<p>Your deliverability test is complete. <a href="${[Link]}">View
report</a></p>`; try{ await [Link]([Link],'Deliverability Test Report',html);
}catch(err){ [Link]('Error sending report email',err);} return 'done'; } return 'continue'; } async
function startPolling(testId){ if([Link](testId)) return; const startAt=[Link](); const
interval=setInterval(async()=>{ try{ const result=await pollOnce(testId); if(result==='done'){
clearInterval(interval); [Link](testId); } else if([Link]()-startAt>POLL_TIMEOUT_MS){ const
testDoc=await [Link](testId); if(testDoc){ [Link]='failed'; await [Link](); }
clearInterval(interval); [Link](testId); } }catch(err){ [Link]('polling error',err);}
},POLL_INTERVAL_MS); [Link](testId,interval);} [Link]={startPolling};

--- src/services/mailClients/[Link] ---


const {google}=require('googleapis'); const INBOX_TOKEN_MAP={
'test1+inbox@[Link]':[Link].GMAIL_REFRESH_TOKEN_INBOX1,
'test2+inbox@[Link]':[Link].GMAIL_REFRESH_TOKEN_INBOX2 }; function
getOauth2ClientForInbox(inboxAddress){ const refreshToken=INBOX_TOKEN_MAP[inboxAddress]; if(!refreshToken)
throw new Error('No refresh token configured for '+inboxAddress); const oAuth2Client=new
[Link].OAuth2([Link].GMAIL_CLIENT_ID,[Link].GMAIL_CLIENT_SECRET);
[Link]({refresh_token:refreshToken}); return oAuth2Client; } async function
searchMessages(inboxAddress,testCode){ const client=getOauth2ClientForInbox(inboxAddress); const
gmail=[Link]({version:'v1',auth:client}); const q=`"${testCode}"`; try{ const res=await
[Link]({userId:'me',q,maxResults:5}); if(![Link]||[Link]===0)
return {found:false}; const msg=[Link][0]; const msgDetail=await
[Link]({userId:'me',id:[Link],format:'metadata'}); const labels=[Link]||[];
let folder='Inbox'; if([Link]('SPAM')) folder='Spam'; if([Link]('CATEGORY_PROMOTIONS'))
folder='Promotions'; return {found:true,folder,id:[Link]}; }catch(err){ [Link]('gmail search
error',err); return {found:false}; } } [Link]={searchMessages};
--- src/services/mailClients/[Link] ---
const fetch=require('node-fetch'); const TENANT='common'; const
TOKEN_ENDPOINT=`[Link] const INBOX_TOKEN_MAP={
'test3@[Link]':[Link].OUTLOOK_REFRESH_TOKEN_INBOX1 }; async function getAccessToken(refreshToken){
const params=new URLSearchParams(); [Link]('client_id',[Link].OUTLOOK_CLIENT_ID);
[Link]('client_secret',[Link].OUTLOOK_CLIENT_SECRET); [Link]('grant_type','refresh_token');
[Link]('refresh_token',refreshToken); [Link]('scope','[Link]
offline_access openid profile'); const res=await fetch(TOKEN_ENDPOINT,{method:'POST',body:params}); const
data=await [Link](); if(!data.access_token) throw new Error('No access token from outlook token endpoint');
return data.access_token; } async function searchMessages(inboxAddress,testCode){ const
refreshToken=INBOX_TOKEN_MAP[inboxAddress]; if(!refreshToken) return {found:false}; try{ const
accessToken=await getAccessToken(refreshToken); const
url=`[Link] const res=await fetch(url,{
headers:{ Authorization:`Bearer ${accessToken}`, 'Prefer':'[Link]-content-type="text"' } }); const
json=await [Link](); if(![Link]||[Link]===0) return {found:false}; const msg=[Link][0];
let folder=[Link]||'Inbox'; return {found:true,folder,id:[Link]}; }catch(err){
[Link]('outlook search error',err); return {found:false}; } } [Link]={searchMessages};

Common questions

Powered by AI

The polling mechanism functions by routinely checking each associated inbox to verify if the email containing a specific test code has arrived. This process is handled by the `pollOnce` function in `src/services/poller.js`, which checks each inbox using respective client services (`gmailClient` for Gmail and `outlookClient` for Outlook) for the code. If found, it updates the inbox's received status and associated metadata. Completion is determined when all inboxes confirm receipt, transitioning the test status to 'completed', calculating a deliverability score, and sending a report to the user. Failure to complete within the defined timeout period results in a 'failed' status .

The system's scalability is achieved through its use of modular and microservice-based scripts. The backend employs a model-controller architecture, using Express.js for routing, Mongoose for database models, and separate services for polling and email sending, which effectively compartmentalizes logic and tasks. Additionally, `setInterval` function in `poller.js` ensures that polling is non-blocking, allowing multiple tests to proceed simultaneously without halting the system's responsiveness. Environment configurations, such as multiple refresh tokens and separate routes for inboxes and tests, permit handling increased loads as usage scales .

The backend system incorporates several error handling mechanisms. For instance, during test creation in `src/routes/tests.js`, missing user email data returns a 400 error. The server encounters errors saving a test, logging 'Create test error' and responding with a 500 status. Similar handling exists for starting tests and retrieving test data, providing 404 errors for not found instances and logging specific error messages, e.g., 'Start test error' . In the polling mechanism, errors in message checking are logged at the `checkInboxForCode` function, suggesting robustness in tracking and managing issues across different parts of the system operations .

Nodemailer is utilized in the backend system to send emails as part of the email deliverability tests. Specifically, the `emailSender` module uses Nodemailer to set up an SMTP transport using credentials, allowing the service to send email reports upon the completion of tests. Nodemon, on the other hand, is a development dependency that facilitates development by automatically restarting the node application when file changes are detected, thus increasing developer efficiency and providing immediate feedback .

The backend service initializes an email deliverability test by creating a test entry in the database with a unique test code and user email. This is managed by the endpoint described in the `src/routes/tests.js` file, where the test creation includes setting up predefined inboxes and saving the test with a 'pending' status. Polling for email deliverability involves continuously checking the associated inboxes to determine whether the email with the test code has arrived. The polling process, described in `src/services/poller.js`, starts asynchronously; if polling is successful and all emails are confirmed received (reflected in the 'Inbox' folder), the test status is marked as 'completed', and an email report is generated and sent to the user. If the test times out without retrieving all emails, the status changes to 'failed' .

The backend service uses environment variables to handle sensitive API credentials and tokens securely. In `package.json`, relevant credentials like API keys and client secrets are stored in a `.env.example` file. These environment variables are loaded into the application through the `dotenv` package, preventing sensitive data from being hard-coded in the source files. This practice reduces the risk of exposing credentials in version control systems and ensures they are kept out of reach from unauthorized access .

The essential environmental settings include `PORT`, `BASE_URL`, `MONGODB_URI`, API keys and client secrets for Gmail and Outlook (`GMAIL_CLIENT_ID`, `OUTLOOK_CLIENT_ID`, etc.), SMTP configuration details (e.g., `SMTP_HOST`, `SMTP_USER`, `SMTP_PASS`), `FROM_EMAIL`, and polling intervals (`POLL_INTERVAL_MS`, `POLL_TIMEOUT_MS`). These local configurations, outlined in `.env.example`, are significant as they guide the server's listening port, define necessary database and email client connections, and streamline email operations while managing test execution timing effectively .

The backend system adopts a clear versioning strategy, defined by specific version numbers or caret (^) indicating compatibility with versions not below but possibly above the specified number, e.g., `

You might also like