Skip to content

Commit cd42b02

Browse files
committed
✉️ Update serverless email microservice
Closes #5
1 parent ec2b886 commit cd42b02

File tree

3 files changed

+122
-72
lines changed

3 files changed

+122
-72
lines changed

Diff for: README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ This is a simple microservice that will trigger an email via Amazon SES to the u
88

99
A microservice that sends verification mail to users who create a new account using the REST API for [https://prod.sydrawat.me](https://prod.sydrawat.me).
1010

11-
Amazon SES does not trigger multiple verification e-mails to the user even if the user hits the endpoint `/v1/account/` multiple times. The verification mail is sent only once per unique user email-id.
11+
Amazon SES does not trigger multiple verification e-mails to the user even if the user hits the endpoint `/v2/account/` multiple times. The verification mail is sent only once per unique user email-id.
1212

1313
The user has a 5 minute window to verify their account by clicking on the verification link sent in the email. Once verified, the user can continue using the REST API, otherwise the user should not be able to consume the REST API.
1414

Diff for: index.js

+120-70
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,137 @@
1-
const aws = require('aws-sdk')
1+
const AWS = require('aws-sdk')
22

3-
aws.config.update({ region: 'us-east-1' })
4-
const ses = new aws.SES({ region: 'us-east-1' })
5-
const docClient = new aws.DynamoDB.DocumentClient()
6-
7-
const handler = async (event) => {
8-
const message = event.Records[0].Sns.Message
9-
const json = JSON.parse(message)
10-
const email = json.username
11-
const { token } = json
12-
const seconds = 5 * 60
13-
const secondsInEpoch = Math.round(Date.now() / 1000)
14-
const expirationTime = secondsInEpoch + seconds
3+
const checkSentEmail = async (
4+
dynamoDBClient,
5+
trackUserEmailDynamoDBTable,
6+
email
7+
) => {
8+
const params = {
9+
TableName: trackUserEmailDynamoDBTable,
10+
Key: {
11+
Email: email,
12+
},
13+
}
14+
const data = await dynamoDBClient.get(params).promise()
15+
if (data.Item) return true
16+
return false
17+
}
1518

16-
const table = {
17-
TableName: 'csye-6225',
19+
const logSentEmail = async (
20+
dynamoDBClient,
21+
trackUserEmailDynamoDBTable,
22+
email
23+
) => {
24+
const params = {
25+
TableName: trackUserEmailDynamoDBTable,
1826
Item: {
1927
Email: email,
20-
Token: token,
21-
TimeToLive: expirationTime,
2228
},
2329
}
30+
const data = await dynamoDBClient.put(params).promise()
31+
console.log('User data added', data)
32+
}
2433

25-
// Putting an item to DynamoDB Table
26-
docClient.put(table, (err, data) => {
27-
if (err) {
28-
console.error(
29-
'Unable to add item. Error JSON:',
30-
JSON.stringify(err, null, 2)
31-
)
32-
}
34+
const handler = async (event, context, callback) => {
35+
console.log(`Received event ${JSON.stringify(event, null, 4)}`)
36+
const trackUserEmailDynamoDBTable = process.env.TrackUserEmailDynamoDBTable
37+
const trackUserEmailDynamoDBRegion =
38+
process.env.TrackUserEmailDynamoDBRegion || 'us-west-1'
39+
const domainEnvironment = process.env.DomainEnvironment || 'prod'
40+
41+
console.log('Setting AWS region to:', trackUserEmailDynamoDBRegion)
42+
AWS.config.update({ region: emailTrackingDynamoDBRegion })
43+
const dynamoDBClient = new AWS.DynamoDB.DocumentClient({
44+
region: trackUserEmailDynamoDBRegion,
3345
})
3446

35-
const mailbody = `
36-
<h3>Dear CSYE User,</h3>
37-
<p>
38-
Thank you for registering with Network Structures and Cloud Computing coursework for this semester!
39-
While we have you registered to use our REST API service, we need to verify your account.
40-
</br>
41-
The below link will redirect you to verify your email and allow your to consume our REST API.
42-
<b>Link below link will be valid only for 5 minutes:</b>
43-
</br>
44-
</p>
45-
<p> Verify your link:
46-
<a href=https://prod.sydrawat.me/v2/verifyUserEmail?token=${token}&email=${email} >
47-
https://prod.sydrawat.me/v2/verifyUserEmail?token=${token}&email=${email}</a>
48-
</p>
49-
<br/>
50-
<p>
51-
If you did not create a account with us, it is possible that someone else is trying to access our service
52-
by using your account ${email}. <b>Do not forward or give this link to anyone.</b>
53-
</p>
54-
<br/>
55-
<p>
56-
You received this email because you require access to use our REST API services. If you think this is
57-
incorrect, please contact our support team.
58-
</p>
59-
<br/>
60-
Sincerely,
61-
<br/>
62-
The CSYE team
63-
<br/>`
47+
const message = event.Records[0].Sns.Message
48+
const parsedMessage = JSON.parse(message)
49+
console.log('Parsed message:', parsedMessage)
50+
const email = parsedMessage.username
51+
const { first_name, last_name, token } = parsedMessage
6452

65-
const params = {
66-
Destination: {
67-
ToAddresses: [email],
68-
},
69-
Message: {
70-
Body: {
71-
Html: {
53+
const emailAlreadySent = checkSentEmail(
54+
dynamoDBClient,
55+
trackUserEmailDynamoDBTable,
56+
email
57+
)
58+
59+
if (!emailAlreadySent) {
60+
console.log(`Sending email to ${email}`)
61+
const ses = new AWS.SES()
62+
const mailbody = `
63+
<h3>Hello ${first_name}} ${last_name},</h3>
64+
<p>
65+
Thank you for registering with Network Structures and Cloud Computing coursework for this semester!
66+
</p>
67+
<p>
68+
While we have you registered to use our REST API service, we need to verify your account.
69+
</p>
70+
<br>
71+
<p>
72+
The below link will redirect you to verify your email and allow your to consume our REST API.
73+
<b>Link below link will be valid only for 5 minutes:</b>
74+
<br>
75+
</p>
76+
<p> Verify your account:
77+
<br>
78+
<a href=https://${domainEnvironment}.sydrawat.me/v2/verifyUserEmail?token=${token}&email=${email} >
79+
https://${domainEnvironment}.sydrawat.me/v2/verifyUserEmail?token=${token}&email=${email}</a>
80+
</p>
81+
<br>
82+
<p>
83+
If you did not create a account with us, it is possible that someone else is trying to access our service
84+
by using your account ${email}. <b>Do not forward or give this link to anyone.</b>
85+
</p>
86+
<br>
87+
<p>
88+
You received this email because you require access to use our REST API services. If you think this is
89+
incorrect, please contact our support team.
90+
</p>
91+
<br>
92+
Sincerely,
93+
<br/>
94+
The ${domainEnvironment} CSYE team
95+
<br>`
96+
97+
const params = {
98+
Destination: {
99+
ToAddresses: [email],
100+
},
101+
Message: {
102+
Body: {
103+
Html: {
104+
Charset: 'UTF-8',
105+
Data: mailbody,
106+
},
107+
},
108+
Subject: {
72109
Charset: 'UTF-8',
73-
Data: mailbody,
110+
Data: `Account verification for ${domainEnvironment} CSYE6225 REST API: Action required`,
74111
},
75112
},
76-
Subject: {
77-
Charset: 'UTF-8',
78-
Data: 'Account verification for CSYE6225: Action required',
79-
},
80-
},
81-
Source: '[email protected]',
113+
Source: `noreply@${domainEnvironment}.sydrawat.me`,
114+
}
115+
try {
116+
const data = await ses.sendEmail(params).promise()
117+
console.log(data)
118+
console.log('Email successfully sent to:', { email })
119+
} catch (err) {
120+
console.log(`Could not send the email: `, err)
121+
}
122+
// Log this email to track sent emails to prevent duplicate verification email-link generation
123+
try {
124+
await logSentEmail(dynamoDBClient, trackUserEmailDynamoDBTable, email)
125+
console.log('Email successfully logged to trackUserEmailDynamoDBTable')
126+
} catch (err) {
127+
console.log(
128+
`Could not store the email to trackUserEmailDynamoDBTable: `,
129+
err
130+
)
131+
}
132+
} else {
133+
console.log(`Email already sent to user: ${email}. No need to send again`)
82134
}
83-
console.log('Email successfully sent to:', { email })
84-
return ses.sendEmail(params).promise()
85135
}
86136

87137
module.exports = {

Diff for: package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"version": "1.0.0",
44
"description": "Serverless for AWS SES and Lambda Functions",
55
"main": "index.js",
6-
"repository": "https://github.com/ArtemisIX/serverless",
6+
"repository": "https://github.com/VoskhodXIV/microservices",
77
"author": "Siddharth Rawat <[email protected]>",
88
"license": "MIT",
99
"private": true,

0 commit comments

Comments
 (0)