-
Notifications
You must be signed in to change notification settings - Fork 5
/
app.js
137 lines (114 loc) · 3.52 KB
/
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
const fs = require('fs');
const path = require('path');
const Cabin = require('cabin');
const Email = require('email-templates');
const bodyParser = require('body-parser');
const cryptoRandomString = require('crypto-random-string');
const dayjs = require('dayjs');
const express = require('express');
const isSANB = require('is-string-and-not-blank');
const requestId = require('express-request-id');
const requestReceived = require('request-received');
const responseTime = require('response-time');
const { Signale } = require('signale');
const { isEmail } = require('validator');
// create persistent email queue file if it doesn't already exist
// note you could swap out this for MongoDB (e.g. Mongoose), SQL, Redis, etc
// as Bree will read and update this file every few seconds to flush teh queue
const queueFile = path.join(__dirname, 'queue.json');
if (!fs.existsSync(queueFile)) fs.writeFileSync(queueFile, JSON.stringify([]));
// initialize email-templates
const email = new Email({
message: {
from: '[email protected]'
},
transport: {
jsonTransport: true
}
});
// initialize cabin
const cabin = new Cabin({
axe: {
logger: new Signale()
}
});
// initialize express
const app = express();
// adds request received hrtime and date symbols to request object
// (which is used by Cabin internally to add `request.timestamp` to logs
app.use(requestReceived);
// adds `X-Response-Time` header to responses
app.use(responseTime());
// adds or re-uses `X-Request-Id` header
app.use(requestId());
// use the cabin middleware (adds request-based logging and helpers)
app.use(cabin.middleware);
// support parsing of url encoded data
app.use(bodyParser.urlencoded({ extended: false }));
// support parsing of application/json data
app.use(bodyParser.json());
// test endpoint
app.get('/test', (req, res) => {
res.sendStatus(200);
});
// send email test endpoint
app.post('/send-email', async (req, res, next) => {
try {
// validate email
if (!isSANB(req.body.email) || !isEmail(req.body.email))
throw new Error('Email was invalid');
// send email
await email.send({
message: {
to: req.body.email,
subject: 'Hello, World!',
html: '<p>Hello, World!</p>'
}
});
// send response
res.sendStatus(200);
} catch (err) {
next(err);
}
});
// ticket booking endpoint
app.post('/book-ticket', async (req, res, next) => {
try {
// validate email
if (!isSANB(req.body.email) || !isEmail(req.body.email))
throw new Error('Email was invalid');
const movieTime = isSANB(req.body.start_time)
? dayjs(req.body.start_time, 'M/D/YY h:mm A')
: false;
if (!movieTime || !movieTime.isValid())
throw new Error('Movie time is invalid (must be M/D/YY h:mm A) format');
// add an email to the queue for this ticket we booked (subtract 10 mins from start time)
let queue = [];
try {
queue = require(queueFile);
} catch (err) {
cabin.debug(err);
}
queue.push({
id: cryptoRandomString({ length: 10 }),
email: req.body.email,
send_at: movieTime.subtract(10, 'minutes').toDate()
});
await fs.promises.writeFile(queueFile, JSON.stringify(queue));
// send email
await email.send({
message: {
to: req.body.email,
subject: 'Booking Confirmed',
html: '<p>Your booking is confirmed!</p>'
}
});
// send response
res.sendStatus(200);
} catch (err) {
next(err);
}
});
app.listen(8080, () => {
cabin.info('App started: http://localhost:8080');
});