Repository navigation
Expand file tree
/
Copy pathserver.js
More file actions
1034 lines (840 loc) · 30.4 KB
/
Copy pathserver.js
File metadata and controls
1034 lines (840 loc) · 30.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
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
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//Test comment for auto deploy
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const nodemailer = require("nodemailer"); //for email verification
const app = express();
const { ObjectId } = require("mongodb"); // If you want to use MongoDB's ObjectId for _id generation
const crypto = require("crypto-js");
require("dotenv").config();
const url = process.env.DATABASE_URL;
const MongoClient = require("mongodb").MongoClient;
const client = new MongoClient(url, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
(async function () {
try {
await client.connect();
console.log("Connected to MongoDB");
} catch (e) {
console.error(e);
}
})();
app.use(cors());
app.use(bodyParser.json());
// Setup Nodemailer
const transporter = nodemailer.createTransport({
service: "Gmail", // or your preferred email provider
auth: {
user: process.env.EMAIL_USER, // your email
pass: process.env.EMAIL_PASS, // your email password or app password
},
});
//LOGIN API
app.post("/api/login", async (req, res) => {
const { login, password } = req.body;
let error = "";
let user = null;
try {
const db = client.db("COP4331");
const usersCollection = db.collection("Users");
const results = await usersCollection.findOne({
$or: [
{ login: login },
{ email: login },
],
password: password,
});
if (results) {
user = {
id: results._id,
login: results.login,
firstName: results.firstName,
lastName: results.lastName,
email: results.email,
role: results.role,
emailVerified: results.emailVerified,
};
} else {
error = "Username or Password is incorrect";
}
} catch (e) {
error = e.toString();
}
const ret = { user: user, error: error };
res.status(200).json(ret);
});
app.post("/api/getUsersByIds", async (req, res) => {
const { userIds } = req.body; // Expecting userIds to be an array of ObjectId strings
let error = "";
let users = [];
try {
const db = client.db("COP4331");
const usersCollection = db.collection("Users");
// Convert user IDs to ObjectId format
const objectIds = userIds.map(id => new ObjectId(id));
// Fetch users by their _id from the Users collection
users = await usersCollection.find(
{ _id: { $in: objectIds } },
{ projection: { firstName: 1, lastName: 1 } } // Only retrieve firstName and lastName
).toArray();
} catch (e) {
error = e.toString();
}
const ret = { users: users, error: error };
res.status(200).json(ret);
});
// GET CLASSES API
app.post("/api/classes", async (req, res) => {
const { login } = req.body;
let error = "";
let classes = [];
try {
const db = client.db("COP4331");
const usersCollection = db.collection("Users");
const classesCollection = db.collection("Classes");
// Find user by login and get class IDs
const user = await usersCollection.findOne({ login: login });
if (user) {
const classIds = user.classes || [];
// Fetch class documents based on class IDs
classes = await classesCollection
.find({ _id: { $in: classIds } })
.toArray();
} else {
error = "User not found";
}
} catch (e) {
error = e.toString();
}
res.status(200).json({ classes: classes, error: error });
});
//CREATE A CLASS
app.post("/api/createClass", async (req, res) => {
const { className, joinCode, teacherID } = req.body;
let error = "";
let newClass = null;
try {
const db = client.db("COP4331");
const classesCollection = db.collection("Classes");
const usersCollection = db.collection("Users");
// Check if a class with the same joinCode already exists
const existingClass = await classesCollection.findOne({
joinCode: joinCode,
});
if (existingClass) {
error = "A class with this join code already exists.";
} else {
newClass = {
className: className,
joinCode: joinCode,
teacherID: new ObjectId(teacherID),
students: [],
sessions: [],
interval: 15
};
const result = await classesCollection.insertOne(newClass);
newClass._id = result.insertedId;
//adding class to teachers classes array
await usersCollection.updateOne(
{ _id: new ObjectId(teacherID) },
{ $push: { classes: newClass._id } }
);
}
} catch (e) {
error = e.toString();
}
const ret = { newClass: newClass, error: error };
res.status(200).json(ret);
});
// JOIN CLASS API
app.post("/api/joinClass", async (req, res) => {
const { studentObjectId, joinCode } = req.body;
let error = "";
let success = false;
console.log("Received studentObjectId:", studentObjectId);
console.log("Wrapped as a new object:", new ObjectId(studentObjectId));
try {
const db = client.db("COP4331");
const classesCollection = db.collection("Classes");
const usersCollection = db.collection("Users");
// Find the class with the given joinCode
const classToJoin = await classesCollection.findOne({ joinCode: joinCode });
if (!classToJoin) {
error = "Class with this join code does not exist.";
} else {
// Check if the student is already in the class
if (
classToJoin.students &&
classToJoin.students.includes(new ObjectId(studentObjectId))
) {
error = "Student is already enrolled in this class.";
} else {
// Add the student's _id to the students array in the class
await classesCollection.updateOne(
{ joinCode: joinCode },
{ $push: { students: new ObjectId(studentObjectId) } }
);
// Add the className to the classes array in the user's document
await usersCollection.updateOne(
{ _id: new ObjectId(studentObjectId) },
{ $push: { classes: classToJoin._id } }
);
success = true;
}
}
} catch (e) {
error = e.toString();
}
const ret = { success: success, error: error };
res.status(200).json(ret);
});
//LEAVE CLASS
app.post("/api/leaveClass", async (req, res) => {
const { studentObjectId, classObjectId } = req.body;
let error = "";
let success = false;
try {
const db = client.db("COP4331");
const classesCollection = db.collection("Classes");
const usersCollection = db.collection("Users");
// Find the class with the given classId
const classToLeave = await classesCollection.findOne({
_id: new ObjectId(classObjectId),
});
if (!classToLeave) {
error = "Class with this _id does not exist";
} else {
console.log("Students in class:", classToLeave.students);
console.log("Checking for student:", new ObjectId(studentObjectId));
// Check if the student is enrolled in the class
//This WAS the condition thats failing
//Convert ObjectId values to strings for comparison
//classToLeave.students contains ObjectId instances from database. studentObjectID is a new instance.
//In JavaScript, two distinct object instances are not considered equal, even if their internal values are the same.
const studentsStrArray = classToLeave.students.map((id) => id.toString()); // Convert all to strings
const studentIdStr = studentObjectId.toString(); // Convert the student ID to string
if (classToLeave.students && studentsStrArray.includes(studentIdStr)) {
// Remove the student's _id from the students array in the class
await classesCollection.updateOne(
{ _id: new ObjectId(classObjectId) },
{ $pull: { students: new ObjectId(studentObjectId) } }
);
// Remove the className from the classes array in the user's document
await usersCollection.updateOne(
{ _id: new ObjectId(studentObjectId) },
{ $pull: { classes: new ObjectId(classToLeave._id) } }
);
success = true;
} else {
error = "Student is not enrolled in this class.";
}
}
} catch (e) {
error = e.toString();
}
const ret = { success: success, error: error };
res.status(200).json(ret);
});
app.post("/api/deleteClass", async (req, res) => {
const { classObjectId } = req.body;
let error = "";
let success = false;
try {
const db = client.db("COP4331");
const classesCollection = db.collection("Classes");
const usersCollection = db.collection("Users");
// Find the class by _id to ensure it exists
const classToDelete = await classesCollection.findOne({
_id: new ObjectId(classObjectId),
});
if (!classToDelete) {
error = "Class with this _id does not exist.";
} else {
// Remove the class from the Classes collection
await classesCollection.deleteOne({ _id: new ObjectId(classObjectId) });
// remove class from Users collection
await usersCollection.updateMany(
{ classes: new ObjectId(classObjectId) },
{ $pull: { classes: new ObjectId(classObjectId) } }
);
success = true;
}
} catch (e) {
error = e.toString();
}
const ret = { success: success, error: error };
res.status(200).json(ret);
});
// CLASS INFO TEACHER API
app.post("/api/classInfoTeacher", async (req, res) => {
console.log("classInfoTeacher endpoint called");
const { _id } = req.body;
let error = "";
let classInfo = null;
try {
const db = client.db("COP4331");
const classesCollection = db.collection("Classes");
const sessionsCollection = db.collection("Sessions");
// Find the class by _id
const result = await classesCollection.findOne({ _id: new ObjectId(_id) });
if (result) {
// Log the class data to ensure it contains `students` and `sessions`
console.log("Class Data Retrieved:", result);
// Convert each session ID to an ObjectId and store in an array
const sessionIds = result.sessions.map(id => new ObjectId(id));
// Retrieve only session documents matching these specific ObjectIds
const sessionDetails = await sessionsCollection
.find({ _id: { $in: sessionIds } })
.toArray();
// Build the class info object with sessions and students
classInfo = {
interval: result.interval,
joinCode: result.joinCode,
className: result.className,
sessions: sessionDetails.map(session => ({
_id: session._id,
...session
})),
//Dina added this for student details page
students: result.students, // Include the array of ObjectIds for students directly
};
} else {
error = "Class not found";
}
} catch (e) {
console.error("Error retrieving class info:", e);
error = "Internal server error";
}
const ret = { classInfo: classInfo, error: error };
res.status(200).json(ret);
});
//CLASS INFO STUDENT API
app.post("/api/classInfoStudent", async (req, res) => {
const { classId, userId } = req.body;
let error = "";
try {
//Connect to DB and retrieve classes and sessions
const db = client.db("COP4331");
const classesCollection = db.collection("Classes");
const sessionsCollection = db.collection("Sessions");
const usersCollection = db.collection("Users")
//use class id to find document
const classDocument = await classesCollection.findOne({ _id: new ObjectId(classId) });
if (!classDocument) {
return res.status(404).json({ error: "Class not found" });
}
//get teachers name
const teacherID = classDocument.teacherID;
const teacherUserDocument = await usersCollection.findOne({ _id: new ObjectId(teacherID) });
if (!teacherUserDocument) {
return res.status(404).json({ error: "Teacher not found" });
}
const teacherLastName = teacherUserDocument.lastName;
//get all sessionIds
const sessionIds = classDocument.sessions;
const className = classDocument.className;
//array for attendance data
const attendanceData = [];
for (const sessionId of sessionIds) {
//find each session document
const sessionDocument = await sessionsCollection.findOne({ _id: new ObjectId(sessionId) });
// Debugging step: Check if the session document was found
//console.log("Checking session ID:", sessionId, "Found:", !!sessionDocument);
if (sessionDocument) {
const { isRunning, startTime, endTime, students} = sessionDocument;
//check inside students array
// Now assign default values if student is not in the session
if(Array.isArray(students)) {
//find the student
const studentSearch = students.find(s => new ObjectId(s.userId).equals(new ObjectId(userId)));
// If student is not in the session, these are default values
let attendanceGrade = false;
let attendanceNumber = 0;
// If student is in the session, use real values
if (studentSearch) {
attendanceGrade = studentSearch.attendanceGrade;
attendanceNumber = studentSearch.attendanceNumber;
}
attendanceData.push({
sessionId: sessionId, // Track session ID for reference
isRunning,
startTime,
endTime,
attendanceGrade: attendanceGrade,
attendanceNumber: attendanceNumber
});
}
}
}
// Label sessions in chronological order
attendanceData.forEach((session, index) => {
session.sessionNumber = `Session ${index + 1}`;
});
// Get the latest session's isRunning status (from the last session ID in the array)
let latestSessionIsRunning = false;
if (sessionIds.length > 0) {
const latestSessionId = sessionIds[sessionIds.length - 1];
const latestSessionDocument = await sessionsCollection.findOne({ _id: new ObjectId(latestSessionId) });
latestSessionIsRunning = latestSessionDocument ? latestSessionDocument.isRunning : false;
}
// Respond with the teacher's name and the attendance data for each session
res.json({
className,
teacherLastName,
latestSessionIsRunning,
attendanceData
});
} catch (error) {
console.error("Error fetching class info for student:", error);
res.status(500).json({ error: "Internal server error" });
}
});
//REGISTER API
app.post("/api/register", async (req, res) => {
const { login, password, firstName, lastName, email, role } = req.body;
let error = "";
let success = false;
try {
const db = client.db("COP4331");
//find the correct table
const usersCollection = db.collection("Users");
// Check if a user with the same email already exists
const existingEmailUser = await usersCollection.findOne({ email: email });
if (existingEmailUser) {
if (existingEmailUser.emailVerified) {
// If email is registered and verified, send an error message
return res.status(200).json({
success: false,
error:
"This email is already registered and verified. Please log in.",
});
} else {
// If email is registered but not verified, prompt to check email
return res.status(200).json({
success: false,
error:
"This email is already registered but not verified. Please check your email for the verification link.",
});
}
}
const existingUser = await usersCollection.findOne({ login: login });
if (existingUser) {
error = "User already exists";
} else {
// Generate a custom or default _id
const userId = new ObjectId();
//NEWLY INSERTED INFO
const result = await usersCollection.insertOne({
_id: userId,
login: login,
password: password,
firstName: firstName,
lastName: lastName,
email: email,
role: role,
classes: [],
emailVerified: false,
});
success = result.acknowledged;
// Generate a temporary token in the URL (no need to store it)
const verificationLink = `http://cop4331.xyz/api/verify-email?email=${encodeURIComponent(
email
)}`;
const mailOptions = {
from: process.env.EMAIL_USER,
to: email,
subject: "Verify your email",
text: `Hello ${firstName}, please verify your email by clicking the following link: ${verificationLink}`,
html: `<p>Hello ${firstName},</p><p>Please verify your email by clicking the link below:</p><a href="${verificationLink}">Verify Email</a>`,
};
transporter.sendMail(mailOptions, (err, info) => {
if (err) {
console.error("Error sending email:", err);
} else {
console.log("Verification email sent:", info.response);
}
});
}
} catch (e) {
error = e.toString();
}
const ret = { success: success, error: error };
res.status(200).json(ret);
});
//CREATE SESSION
app.post("/api/createSession", async (req, res) => {
//Update: pass in students array from the class document into the session document
const { uuid, startTime, endTime=null, signals = 0, isRunning = true, classId } = req.body;
let error = "";
let newSession = null;
try {
const db = client.db("COP4331");
const sessionsCollection = db.collection("Sessions");
const classesCollection = db.collection("Classes");
//Grab class document and pass in user ids and set default values
const classDocument = await classesCollection.findOne({_id: new ObjectId(classId)});
if (!classDocument) {
return res.status(404).json({error: "Class not found"});
}
const studentArray = classDocument.students.map(userId => ( {
userId: userId,
attendanceGrade: false,
attendanceNumber: 0
}));
// Step 1: Create the session in the Sessions collection
const sessionData = {
uuid, // Unique session identifier
startTime: new Date(startTime), // Convert to Date object
endTime, // Convert to Date object
signals, // Number of signals
isRunning, // Boolean indicating if session is running
students: studentArray, // Convert student IDs to ObjectIds
};
const result = await sessionsCollection.insertOne(sessionData);
if (result.acknowledged) {
newSession = { ...sessionData, _id: result.insertedId };
// Step 2: Update the specified class document to include the new session's _id
const updateResult = await classesCollection.updateOne(
{ _id: new ObjectId(classId) }, // Filter to find the correct class
{ $push: { sessions: result.insertedId } } // Add the session _id to the sessions array
);
if (!updateResult.matchedCount) {
error = "Class not found or could not be updated";
}
} else {
error = "Failed to create session";
}
} catch (e) {
error = e.toString();
}
// Return the created session data or an error
res.status(error ? 500 : 201).json({ newSession, error });
});
//End Session
app.post('/api/endSession', async (req, res) => {
// Destructure input values from the request body
const { sessionId, endTime } = req.body;
// Check for required fields
if (!sessionId || !endTime) {
return res.status(400).json({ error: "sessionId and endTime is missing." });
}
try {
// Connect to the database and set up the Sessions collection
const db = client.db("COP4331");
const sessionsCollection = db.collection("Sessions");
// Update the session in the Sessions collection
const result = await sessionsCollection.updateOne(
{ _id: new ObjectId(sessionId) },
{ $set: { endTime: new Date(endTime), isRunning: false } }
);
// Check if any document was modified
if (result.matchedCount === 0) {
return res.status(404).json({ error: "Session not found" });
}
// Send a success response
res.json({ message: "Session ended successfully" });
} catch (error) {
console.error("Error ending session:", error);
res.status(500).json({ error: "Internal server error" });
}
});
//Increment teacher signals
app.post("/api/incrementTeacherSignals", async (req, res) => {
const { sessionId } = req.body;
try {
//Connect to Database and retrieve Sessions collection
const db = client.db("COP4331");
const sessionsCollection = db.collection("Sessions");
//Increment the "signals" field by 1 for the specified session.
//$inc increments by 1
const result = await sessionsCollection.updateOne(
{ _id: new ObjectId(sessionId) },
{ $inc: { signals: 1 } }
);
// Check if the session was found and updated
if (result.matchedCount === 0) {
return res.status(404).json({ error: "Session not found" });
}
res.json({ message: "Signals incremented successfully" });
} catch (error) {
console.error("Error incrementing signals:", error);
res.status(500).json({ error: "Internal server error" });
}
});
//student scan
app.post("/api/studentScan", async (req, res) => {
const { userId, sessionId, isPresent } = req.body;
let error = "";
try {
//Connect to Database and retrieve Sessions collection
const db = client.db("COP4331");
const sessionsCollection = db.collection("Sessions");
//Find the session document
const session = await sessionsCollection.findOne({ _id: new ObjectId(sessionId) });
if (!session) {
return res.status(404).json({ error: "Session not found" });
}
//Check if the session is currently running
const { isRunning } = session;
const { signals } = session;
//if isPresent is true
//increment attendanceNumber
//return isRunning
//message
//Find the student in the session's student array and update attendance if isPresent is true
const updatedStudentArray = session.students.map(students => {
if (students.userId.equals(new ObjectId(userId))) {
//if isPresent update attendance number
if (isPresent) {
students.attendanceNumber += 1; // Increment attendance if the student is marked as present
//check if the increment hits the grade target
if (students.attendanceNumber >= (signals - 1) ) {
students.attendanceGrade = true; // Update the presence status
}
else {
students.attendanceGrade = false;
}
}
}
return students; //Return updated student record
});
// Update the session document with the modified student array
await sessionsCollection.updateOne(
{ _id: new ObjectId(sessionId) },
{ $set: { students: updatedStudentArray } }
);
// Respond with a success message and the current session's isRunning status
res.json({
message: "Student attendance updated successfully",
isRunning: isRunning
});
} catch (error) {
console.error("Error updating student attendance:", error);
res.status(500).json({ error: "Internal server error" });
}
});
//Get Session info
app.post("/api/getSessionInfo", async (req, res) => {
const { sessionId } = req.body;
let error = "";
try {
//Connect to Database and retrieve Sessions collection
const db = client.db("COP4331");
const sessionsCollection = db.collection("Sessions");
//convert sessionId to objectId because a string is passed in
const session = await sessionsCollection.findOne({ _id: new ObjectId(sessionId) });
if (!session) {
return res.status(404).json({ error: "Session not found" });
}
const { signals } = session; // Get the signals value from the session document
//Look through each student in the session's student array to update attendance. Map returns a new array of modified student objects.
const updatedStudents = session.students.map(students => {
if (students.attendanceNumber >= signals - 1) {
students.attendanceGrade = true;
}
else {
students.attendanceGrade = false;
}
return students;
});
//Update databse
await sessionsCollection.updateOne(
{ _id: new ObjectId(sessionId) },
{ $set: { students: updatedStudents } }
);
//Respond with the modified session object
res.json({
...session,
students: updatedStudents
});
} catch (error) {
console.error("Error fetching session info:", error);
res.status(500).json({ error: "Internal server error" });
}
});
//Get Session info
app.post("/api/getMobileSession", async (req, res) => {
const { sessionId } = req.body;
let error = "";
try {
//Connect to Database and retrieve Sessions collection
const db = client.db("COP4331");
const sessionsCollection = db.collection("Sessions");
//convert sessionId to objectId because a string is passed in
const session = await sessionsCollection.findOne({ _id: new ObjectId(sessionId) });
if (!session) {
return res.status(404).json({ error: "Session not found" });
}
//Respond with session object
res.json(session);
} catch (error) {
console.error("Error fetching session info:", error);
res.status(500).json({ error: "Internal server error" });
}
});
app.delete("/api/deleteUser", async (req, res) => {
const { login } = req.body; // Assuming the unique identifier is the 'login' field in the request body
let error = "";
let success = false;
try {
const db = client.db("COP4331");
const usersCollection = db.collection("Users");
// Delete user by login
const result = await usersCollection.deleteOne({ login: login });
if (result.deletedCount === 1) {
success = true;
} else {
error = "User not found or could not be deleted";
}
} catch (e) {
error = e.toString();
}
const ret = { success: success, error: error };
res.status(200).json(ret);
});
// EMAIL VERIFICATION API
app.get("/api/verify-email", async (req, res) => {
const { email } = req.query;
let error = "";
let success = false;
try {
const db = client.db("COP4331");
const usersCollection = db.collection("Users");
console.log("Attempting to verify email:", email);
// Find and update the user document
const result = await usersCollection.findOneAndUpdate(
{ email: email, emailVerified: false },
{ $set: { emailVerified: true } },
{ returnDocument: "after" }
);
console.log("findOneAndUpdate result:", result);
// Set success to true if a document was updated, regardless of result
console.log(result.emailVerified);
if (result.emailVerified === true) {
success = true;
res.send("Email verified successfully. You can now log in.");
} else {
error = "Invalid or expired verification link, or user already verified.";
res.send(error);
}
} catch (e) {
error = "An error occurred: " + e.toString();
console.error("Error in verification:", error);
res.status(500).send(error);
}
console.log("Verification result:", { success, error });
});
//Forgot Password
app.post("/api/forgotPassword", async (req, res) => {
const { email } = req.body;
let error = "";
let success = false;
try {
const db = client.db("COP4331");
const usersCollection = db.collection("Users");
const result = await usersCollection.findOne({ email: email });
if (result) {
const resetToken = crypto.lib.WordArray.random(16).toString(); //random Token generator
const resetTokenExpiration = Date.now() + 3600000; // 1 hour from now
//adds two variables to the database for password reset verification
await usersCollection.updateOne(
{ email: email },
{
$set: {
resetToken: resetToken,
resetTokenExpiration: resetTokenExpiration,
}
}
);
success = result.acknowledged;
//sends email out
const mailOptions = {
from: process.env.EMAIL_USER,
to: email,
subject: 'QuickMark Password Reset',
text: `
You are receiving this because you (or someone else) have requested the reset of the password for your account.
Please click on the following link, or paste this into your browser to complete the process:
http://cop4331.xyz/resetpassword/${resetToken}
If you did not request this, please ignore this email and your password will remain unchanged.
`
}
transporter.sendMail(mailOptions, (err, info) => {
if (err) {
console.error("Error sending email:", err);
error = "Error sending email";
} else {
success = true;
console.log("Password reset email sent:", info.response);
}
});
} else {
error = "Email not found"
}
} catch (e) {
error = e.toString();
}
const ret = { success: success, error: error };
res.status(200).json(ret);
});
//Reset Password
app.post('/api/resetPassword/:token', async (req, res) => {
const token = req.params.token;
const { password } = req.body;
let error = "";
let success = false;
try {
const db = client.db("COP4331");
const usersCollection = db.collection("Users");
const result = await usersCollection.findOne({
resetToken: token,
resetTokenExpiration: { $gt: Date.now() }, //ensure token is not expired
});
if(result) {
await usersCollection.updateOne(
{ resetToken: token },
{
$set: {
password: password,
},
$unset: {
resetToken: "", resetTokenExpiration: "",
}
}
);
} else {
error = "Invalid or Expired token"
}
} catch (e) {
error = e.toString();
}