-
Notifications
You must be signed in to change notification settings - Fork 1
/
server.js
589 lines (538 loc) · 15.9 KB
/
server.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
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
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
/**
* Environment variables
*/
const TWO_HOURS = 1000 * 60 * 60 * 2
const {
NODE_ENV = 'development',
SESSION_NAME = 'sid',
SESSION_SECRET = 'ssh!quiet,it\'asecret!',
PORT = 3000,
} = process.env
const IN_PROD = NODE_ENV === 'production'
/**
* Setting global constants for the express public
*/
const path = require('path');
const express = require('express'); // Express module
const session = require('express-session'); // Express session module
const bcrypt = require('bcrypt'); // Bcrypt module for hashing passwords
const bodyParser = require('body-parser') // Body parser for reading request messages
const mysql2 = require('mysql2') // Database driver for database connection
const compression = require('compression'); // Caching
const helmet = require('helmet');
const {body, validationResult} = require('express-validator'); // Server-side form validation
/**
* Application Settings
* All the settings for express are defined here
*/
const app = express();
const server = require('http').Server(app);
require('log-timestamp')('#Starting app');
app.set('view engine', 'ejs');
app.use(compression());
app.use(helmet());
app.use(express.static(__dirname + '/public'));
app.use(express.static(__dirname + '/public/assets'));
app.use(express.static(__dirname + 'app.js'));
app.use(session({
name: SESSION_NAME,
resave: false,
saveUninitialized: false,
secret: SESSION_SECRET,
cookie: {
maxAge: TWO_HOURS,
sameSite: true,
secure: IN_PROD,
}
})) // Session settings
app.use(bodyParser.urlencoded({
extended: true
})) // Bodyparser settings
/**
* Global Variables
*/
let broadcaster;
let onlineUsers = [] // Array to store the currently online users
// Expose port 3000
server.listen(PORT, () => {
console.info(`Server is listening on port ${process.env.PORT || 3000}`)
console.log(`App currently in ${NODE_ENV}, Author: Nigel Ritfeld \< info@nigelritfeld.nl \> `)
// console.log(`p`)
// console.log(`Author: Nigel Ritfeld "info@nigelritfeld.nl"`)
require('log-timestamp')('#LOG');
});
/**
* Creating websocket with cross origin for client app
* @type {*}
*/
const io = require('socket.io')(server,
{
// Cors settings
cors: {
// Allow these origins
origin: "https://watch.brainbug.tech",
// origin: "http://localhost:3001",
// Allowing get and post methods for these origins
methods: ["GET", "POST"]
}
});
/**
* Creating a connection pool
* @type {Pool}
*/
const connection = mysql2.createPool(
{
host: 'localhost',
port: '1234',
user: 'username',
password: 'password',
database: 'database_name',
connectionLimit: 10
}
)
/**
* Check if there is a existing session
* @param req
* @param res
* @param next
*/
const redirectLogin = (req, res, next) => {
if (!req.session.userId) {
// Redirecting to the sign up page
res.redirect('/login')
} else {
// Else continue..
next()
}
}
/**
* Checks if there is a existing session
* @param req
* @param res
* @param next
*/
const isLoggedIn = (req, res, next) => {
if (!req.session.userId) {
// Else continue..
next()
} else {
res.redirect('/')
}
}
/**
* All application routes
*/
app.get('/', redirectLogin, (req, res) => {
res.sendFile(path.join(__dirname, 'public/main.html'))
});
/**
* Profile routes
*/
app.get('/profile', redirectLogin, (req, res) => {
// Getting user id from session
const userId = req.session.userId
// Getting index of user in online sessions
const userIndex = onlineUsers.map((user) => {
return user.id;
}).indexOf(userId);
// Getting user from session
const user = onlineUsers[userIndex]
let username = user.username
let name = user.name
let email = user.email
res.render('profile', {
username: username,
name: name,
email: email
})
});
/**
* Profile POST routes
*/
app.post('/profile', (req, res) => {
const {username, name, email, password} = req.body
// Getting user id from session
const userId = req.session.userId
// Getting index of user in online sessions
const userIndex = onlineUsers.map((user) => {
return user.id
}).indexOf(userId);
bcrypt.hash(password, 10, function (err, hash) {
// Creating a user with received input username, name, email, password, id, userIndex, req, res
updateUser(`${username}`, `${name}`, `${email}`, `${hash}`, `${userId}`, userIndex, req, res)
});
});
/**
* Login error route
*/
app.get('/login/:error', redirectLogin, (req, res) => {
let error = req.query.error;
console.log(error)
// Redirecting to the sign up page
res.render('login.ejs', {
error: error
})
})
/**
* Delete user profile
*/
app.post('/deleteUserProfile', (req, res) => {
// Getting user id from session
const userId = req.session.userId
// Getting index of user in online sessions
const userIndex = onlineUsers.map((user) => {
return user.id
}).indexOf(userId);
setOfflineUser(userIndex, userId, req, res)
deleteUser(userId)
});
/**
* Login GET route
*/
app.get('/login/:error', redirectLogin, (req, res) => {
let error = req.query.error;
console.log(error)
// Redirecting to the sign up page
res.render('login.ejs', {
error: error
})
})
/**
* Login GET route
*/
app.get('/login', (req, res) => {
// If there is a GET parameter passed
if (req.query.session) {
// if there is a session GET parameter is given
let errorMessage = 'Your session expired, please log in again '
// Render page with error message
res.render('login', {error: errorMessage})
} else if (req.query.error) {
// Render page with error message
// Create var of given error GET parameter
let errorMessage = req.query.error
if (errorMessage === '1') {
errorMessage = 'Login incorrect'
}
console.log(`Detected error: ${errorMessage}`)
res.render('login', {error: errorMessage})
} else {
// If there is no user ID in the session
if (!req.session.userId) {
res.sendFile(path.join(__dirname, 'public/login.html'))
} else {
// Else return redirect to profile page..
res.redirect('/profile')
}
}
});
/**
* Login POST route
*/
app.post('/login', (req, res) => {
const {login_username, login_password} = req.body
if (login_username && login_password) {
validateUser(login_username.toLowerCase(), login_password, req, res)
}
})
/**
* Register GET route
*/
app.get('/registreren', isLoggedIn, (req, res) => {
res.sendFile(path.join(__dirname, 'public/register.html'))
}
);
/**
* Register GET route
*/
app.get('/registreren/:error', isLoggedIn, (req, res) => {
if (req.query.error === 1) {
let errorMessage = 'Ingevoerde is geen geldige e-mailadres'
res.render('register', {error: errorMessage})
}
if (req.query.error === 2) {
let errorMessage = 'Wachtwoord is niet langer dan 6 karakters'
res.render('register', {error: errorMessage})
}
if (req.query.error === 3) {
let errorMessage = 'Ingevoerde is geen geldige e-mailadres'
res.render('register', {error: errorMessage})
}
res.sendFile(path.join(__dirname, 'public/register.html'))
}
);
/**
* Register POST route
*/
app.post('/registreren', (req, res) => {
try {
// SALT ROUNDS
const saltRounds = 10;
// Received input
const {username, name, email, password} = req.body
// checkInput(username,email, req, res)
// Hasing the password
bcrypt.hash(password, saltRounds, function (err, hash) {
// Creating a user with received input
createUser(`${username.toLowerCase()}`, `${name}`, `${email}`, `${hash}`)
});
// Redirecting
res.redirect('/login')
} catch (e) {
res.redirect('/404')
}
});
/**
* Logout route
*/
app.get('/logout', (req, res) => {
try {
// Remove from online users array
let userId = req.session.userId
let userIndex = onlineUsers.map((user) => {
return user.id
}).indexOf(userId);
setOfflineUser(userIndex, userId, req, res)
} catch (e) {
console.log(e)
res.redirect('/404')
}
});
/**
* Redirect non existing pages to the homepage
*/
app.get('*', (req, res) => {
res.redirect('/')
})
/**
* User constructor
*
* @param id
* @param username
* @param name
* @param password
* @param created_at
* @param updated_at
* @constructor
*/
function User(id, username, name, email, created_at, updated_at) {
this.id = id
this.username = username
this.name = name
this.email = email
this.created_at = created_at
this.updated_at = updated_at
}
/**
* Get user from database
* @param username
*/
function getUser(username, req, res) {
return new Promise((resolve, reject) => {
try {
// Doing a query on the database
connection.execute(`SELECT * FROM \`users\` WHERE \`username\` = ?`, [username], function (err, results, rows) {
if (results[0] === undefined) {
console.log(`Login attempt for user: ${username} `)
res.redirect('/login?error=1')
} else if (results[0].username === username) {
return resolve(results[0]);
}
});
} catch (err) {
console.log(err)
}
})
}
/**
* Checks input
* @param username
* @param email
* @param req
* @param res
*/
function checkInput(username, email, req, res) {
try {
connection.query(`SELECT * FROM \`users\` WHERE username = ? `, [username],
function (err, results, fields) {
console.log(results)
console.log(results[0].username)
if (results[0].username === username) {
res.redirect('/registreren?username=1')
console.log(results); // results contains rows returned by server
}
if (results['email'] === email) {
res.redirect('/registreren?email=1')
}
})
} catch (e) {
console.log(e)
}
}
/**
* Insert new user to database
* @param username
* @param name
* @param email
* @param password
*/
function createUser(username, name, email, password) {
try {
connection.execute(`INSERT INTO \`users\`(\`username\`, \`name\`, \`email\`, \`password\`)
VALUES (?,?,?,?)`, [username, name, email, password],
function (err, results, fields) {
console.log(results); // results contains rows returned by server
}
)
} catch (err) {
console.log(err)
}
}
/**
* Update user in database
* @param username
* @param name
* @param email
* @param password
* @param id
* @param userIndex
* @param req
* @param res
*/
function updateUser(username, name, email, password, id, userIndex, req, res) {
try {
connection.execute(`UPDATE \`users\` SET \`username\`=?,\`name\`=?,\`email\`=?,\`password\`=? WHERE id = ? `, [username, name, email, password, id],
function (err, results, fields) {
console.log(results); // results contains rows returned by server
setOfflineUser(`${userIndex}`, `${id}`, req, res)
}
)
} catch (err) {
console.log(err)
}
}
/**
* Delete user from database
*/
function deleteUser(id) {
connection.execute('DELETE from \`users\` WHERE id = ? ', [id], function (err, results, fields) {
if (!err) {
console.log(`Deleted user #${id}`)
} else {
console.log(err)
}
})
}
/**
* Validates user
* @param username
* @param receivedPassword
* @param req
* @param res
* @returns {Promise<void>}
*/
async function validateUser(username, receivedPassword, req, res) {
return await getUser(`${username}`, req, res)
.then((result) => {
return checkPass(receivedPassword, result, req, res)
}).catch((err) => {
console.error(err);
res.redirect('/login?error=1')
});
}
/**
* Set user in the onlineUsers array
* @param user
*/
function setOnlineUser(user) {
onlineUsers.push(user)
console.log(`[ ${user.username.toUpperCase()}: IS NOW ONLINE ]`)
console.log('[ONLINE USERS:]')
function allOnlineUsers(onlineUsers) {
for (user of onlineUsers) {
console.log(`* ${user.username}`)
}
}
allOnlineUsers(onlineUsers)
}
/**
* Removes user from onlineUsers array and removes session & cookie
* @param userIndex
* @param userID
* @param req
* @param res
*/
function setOfflineUser(userIndex, userID, req, res) {
// Console log
console.log(`[${onlineUsers[userIndex].username} IS NOW OFFLINE]`)
// Remove session
req.session.destroy();
// Remove cookie
res.clearCookie(SESSION_NAME, {path: '/'});
// Remove from registered online user
onlineUsers.splice(userIndex, 1)
// Redirecting
res.redirect('/login?session=expired')
// Checking all online users
if (onlineUsers < 0) {
for (let user of onlineUsers) {
console.log('[ONLINE USER:]')
console.log('[ONLINE USER:]')
console.log(user.username)
}
} else {
console.log('Currently no users online')
}
}
/**
* Validates the password
* @param receivedPassword
* @param user
* @param req
* @param res
*/
function checkPass(receivedPassword, result, req, res) {
// Hashed password
let hash = result['password']
// Load hash from your password DB.
bcrypt.compare(receivedPassword, hash, function (err, verified) {
// result == true
if (verified) {
let user = new User(`${result['id']}`, `${result['username']}`, `${result['name']}`, `${result['email']}`, `${result['created_at']}`, `${result['updated_at']}`)
req.session.userId = user.id
setOnlineUser(user)
res.redirect('/')
} else {
console.log('Login incorrect')
res.redirect('/login?error=1')
}
});
}
/**
* Socket IO
* Creating a peer connection between Broadcaster and the client.
* There are two scripts one for the Broadcaster and the client.
* Via those scripts & the socket connection the peer connection will be established.
*/
io.sockets.on("error", e => console.log(e));
io.sockets.on("connection", socket => {
socket.on("broadcaster", () => {
broadcaster = socket.id;
socket.broadcast.emit("broadcaster");
});
socket.on("watcher", () => {
socket.to(broadcaster).emit("watcher", socket.id);
});
socket.on("offer", (id, message) => {
socket.to(id).emit("offer", socket.id, message);
});
socket.on("answer", (id, message) => {
socket.to(id).emit("answer", socket.id, message);
});
socket.on("candidate", (id, message) => {
socket.to(id).emit("candidate", socket.id, message);
});
socket.on("disconnect", () => {
socket.to(broadcaster).emit("disconnectPeer", socket.id);
});
});