-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.js
More file actions
307 lines (252 loc) · 10.4 KB
/
app.js
File metadata and controls
307 lines (252 loc) · 10.4 KB
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
// Import and create a new instance of express
const express = require("express");
const app = express();
// Import and configure express session middleware
const session = require("express-session");
app.use(session({
name: "sid", // session name
resave: false, // do not store sessions that are not modified during the request
saveUninitialized: false, // do not store new unmodified sessions (with no data)
secret: "secret_key", // string used to sign sid cookie
cookie: {
maxAge: 7200000, // maximum age of a cookie in milliseconds (set to two hours)
sameSite: true // browser will accept cookies only from the same domain
}
}));
// Import bodyparser and use json and urlencoded parser with extented parsing mode as middleware for the application
const bodyParser = require("body-parser");
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(bodyParser.json());
// Import bcrypt
const bcrypt = require("bcrypt");
// Set up EJS as template engine
app.set("view engine", "ejs");
// Import mysql2 module, pass in a config object and create a connection to the database
const mysql = require("mysql2");
const connection = mysql.createConnection({
host: "localhost",
user: "root",
password: "root",
database: "G00438839"
});
connection.connect((err) => {
if (err) {
console.log("Error connecting to the database: ", err);
} else {
console.log("Connected to the database!");
}
});
// Middleware for serving static files
app.use(express.static("home"));
// Import authentication module
const auth = require("./utils/auth.js")
// Custom middleware that checks if userID exists in a session and stores a string ('Log Out' or 'Log In') into a res.locals object
app.use((req, res, next) => {
const { userID } = req.session;
res.locals.loginStatus = "Log In";
if (userID) {
res.locals.loginStatus = "Log Out";
}
next();
});
// Route to serve the landing page
app.get("/", (req, res) => {
// Extract loginStatus from res.locals object
const { loginStatus } = res.locals;
// Render home page with activePage status to highlight navbar link and login status to display 'Log In'/'Log out' accordingly
// Include customer name from the current session
return res.status(200).render("index", {activePage: "home", isLoggedIn: loginStatus, customerName: req.session.userName});
});
// Route for 'About us' page
app.get("/about", (req, res) => {
// Extract loginStatus from res.locals object
const { loginStatus } = res.locals;
// Render about us page with all relevant information
return res.status(200).render("about", {activePage: "about", isLoggedIn: loginStatus, customerName: req.session.userName});
});
// Route that serves 'Products' page
app.get("/products", (req, res) => {
// Extract loginStatus from res.locals object
const { loginStatus } = res.locals;
// Query database for products information and render products template with relevant data
connection.query("SELECT * FROM products", (error, data) => {
if (error) {
console.log("Error querying database: " + error);
return res.status(500).send("Error retrieving data from database!");
} else {
return res.status(200).render("products", {
activePage: "products",
products: data,
isLoggedIn: loginStatus,
customerName: req.session.userName,
productsList: req.session.cart
});
}
});
});
// Route that handles adding products to the cart (session)
app.post("/addToCart", (req, res) => {
// If no productID in request, respond with cart session (every time products page is loaded - cart.js)
if (!req.body.productID) {
return res.status(200).json(req.session.cart);
}
// Extract data from the request body
const prodID = req.body.productID;
const prodQty = req.body.quantity;
// Initialize cart in the session if it doesn't exist yet
if (!req.session.cart) {
req.session.cart = [];
}
// Query database for name and price of a particular product
connection.query("SELECT name, price FROM products WHERE productID = ?", [prodID], (error, data) => {
if (error) {
console.error("Error retrieving data from database: ", error);
return res.status(500).send("Error retrieving data from database!");
} else {
// Add all data to a cart session
req.session.cart.push( {
productID: prodID,
productName: data[0].name,
productQty: prodQty,
productPrice: data[0].price
});
// Send JSON as response containing cart session data
return res.status(200).json(req.session.cart);
}
});
});
// Route to handle product removal from the session (cart)
app.post("/removeFromCart", (req, res) => {
// Get the productID from the request body
const prodID = req.body.productID;
// Session does not exists for some reason
if (!req.session.cart) {
return res.status(400).send("Cart is already empty");
}
// If this is the last item, remove cart session
if (req.session.cart.length <= 1) {
delete req.session.cart;
return res.status(200).send("Last item removed from the cart");
}
// Iterate over req.session.cart array and remove object with particular property value of productID
for (let i = 0; i < req.session.cart.length; i++) {
if (prodID == req.session.cart[i].productID) {
req.session.cart.splice(i, 1);
break;
}
}
// All ok
return res.status(200).send("Product removed from the cart");
});
// Checkout route
app.post("/checkout", (req, res) => {
// If no session (cart empty)
if (!req.session.cart) {
return res.status(400).redirect("/products");
}
// Calculate subtotal and total number of items in the cart
let sum = 0;
let quantity = 0;
const cart = req.session.cart;
for (let i = 0; i < cart.length; i++) {
sum += cart[i].productQty * cart[i].productPrice;
quantity += parseInt(cart[i].productQty);
}
// Render chouckout template with subtotal
return res.status(200).render("checkout", {subtotal: sum, totalQty: quantity});
});
// Route to handle transaction (storing purchased products into a database)
app.post("/transaction", (req, res) => {
// Get customer id from the session
const {userID} = req.session;
// Iterate over the cart session and insert values into the corresponding columns of the transactions table
req.session.cart.forEach(item => {
let {productID} = item;
let {productQty} = item;
connection.query("INSERT INTO transactions VALUES (?, ?, ?)", [userID, productID, productQty], (error) => {
if (error) {
console.error("Error storing data to the database: ", error);
return res.status(500).send("Error storing data to the database!");
}
});
});
// All ok
return res.status(201).redirect("/payment")
});
// Payment route
app.get("/payment", (req, res) => {
// Remove cart session
if (req.session.cart) {
delete req.session.cart;
}
// Render payment template
return res.status(200).render("payment");
});
// Login route
app.post("/login", (req, res) => {
// Store request object in loginData constant
const loginData = req.body;
// Query the database for data of all customers
connection.query("SELECT * FROM customers", async function(error, data) {
if (error) {
console.error("Error retrieving data from database: ", error);
return res.status(500).send("Error retrieving data from database!");
} else {
// Pass in request object properties and array of objects (data) from the database into auth module
const authenticated = await auth(loginData.username, loginData.password, data);
// If user is authenticated, store customerId and name in a session object and respond with status 200
if (authenticated) {
req.session.userID = authenticated.customerID;
req.session.userName = authenticated.name;
return res.status(200).send("Authenticated");
}
// If not authenticated, set response status to 401 and send response body as JSON
return res.status(401).json({ error: "Invalid username/password" });
}
});
});
// Logout route
app.get("/logout", (req, res) => {
// Delete userID and userName from session
if (req.session.userID) {
delete req.session.userID;
delete req.session.userName;
return res.status(302).redirect("/");
}
});
// Account route
app.get("/account", (req, res) => {
return res.status(200).render("register", {regMessage: ""});
});
// Route that handles registering of a new user
app.post("/register", async (req, res) => {
// Get all data from the request body
const { name, username, password, confPassword} = req.body;
// Simple input validation
if (name.trim().length < 1 || username.trim().length < 1 || password.trim().length < 1) {
return res.status(400).render("register", {regMessage: "All fields must be completed!"});
}
if (password.trim().length < 6) {
return res.status(400).render("register", {regMessage: "Password must be at least 6 characters long!"});
}
if (password.trim() !== confPassword.trim()) {
return res.status(400).render("register", {regMessage: "Passwords do not match!"});
}
// Hash the password
const hash = await bcrypt.hash(password.trim(), 10);
// Store credentials to the database
connection.query("INSERT INTO customers (username, password, name) VALUES (?, ?, ?)", [username.trim(), hash, name.trim()], (error) => {
if (error) {
console.error("Error storing data to the database: ", error);
return res.status(500).send("Error storing data to the database!");
} else {
return res.status(201).render("register", {regMessage: `Thank you for registering ${name}!<br>You can now go back and log in!`});
}
});
});
// Start a server
app.listen(3000, () => {
console.log("Server started on port 3000");
});