@@ -107,20 +107,33 @@ async function tokenMetaHandler(
107
107
req : UpdateReqType < typeof schemas . examEnvironmentTokenMeta > ,
108
108
reply : FastifyReply
109
109
) {
110
+ const logger = this . log . child ( { req } ) ;
110
111
const { 'exam-environment-authorization-token' : encodedToken } = req . headers ;
112
+ logger . info ( { encodedToken } ) ;
111
113
112
114
let payload : JwtPayload ;
113
115
try {
114
116
payload = jwt . verify ( encodedToken , JWT_SECRET ) as JwtPayload ;
115
117
} catch ( e ) {
116
118
// Server refuses to brew (verify) coffee (jwts) with a teapot (random strings)
119
+ logger . warn (
120
+ { examEnvironmentAuthorizationTokenError : e } ,
121
+ 'Invalid token provided.'
122
+ ) ;
117
123
void reply . code ( 418 ) ;
118
124
return reply . send (
119
125
ERRORS . FCC_EINVAL_EXAM_ENVIRONMENT_AUTHORIZATION_TOKEN ( JSON . stringify ( e ) )
120
126
) ;
121
127
}
122
128
123
129
if ( ! isObjectID ( payload . examEnvironmentAuthorizationToken ) ) {
130
+ logger . warn (
131
+ {
132
+ examEnvironmentAuthorizationToken :
133
+ payload . examEnvironmentAuthorizationToken
134
+ } ,
135
+ 'Token is not an object id.'
136
+ ) ;
124
137
void reply . code ( 418 ) ;
125
138
return reply . send (
126
139
ERRORS . FCC_EINVAL_EXAM_ENVIRONMENT_AUTHORIZATION_TOKEN (
@@ -137,6 +150,7 @@ async function tokenMetaHandler(
137
150
138
151
if ( ! token ) {
139
152
// Endpoint is valid, but resource does not exists
153
+ logger . warn ( 'Token does not appear to exist.' ) ;
140
154
void reply . code ( 404 ) ;
141
155
return reply . send (
142
156
ERRORS . FCC_EINVAL_EXAM_ENVIRONMENT_AUTHORIZATION_TOKEN (
@@ -161,6 +175,8 @@ async function postExamGeneratedExamHandler(
161
175
req : UpdateReqType < typeof schemas . examEnvironmentPostExamGeneratedExam > ,
162
176
reply : FastifyReply
163
177
) {
178
+ const logger = this . log . child ( { req } ) ;
179
+ logger . info ( { userId : req . user ?. id } ) ;
164
180
// Get exam from DB
165
181
const examId = req . body . examId ;
166
182
const maybeExam = await mapErr (
@@ -172,10 +188,12 @@ async function postExamGeneratedExamHandler(
172
188
) ;
173
189
if ( maybeExam . hasError ) {
174
190
if ( maybeExam . error instanceof PrismaClientValidationError ) {
191
+ logger . warn ( { examError : maybeExam . error } , 'Invalid exam id given.' ) ;
175
192
void reply . code ( 400 ) ;
176
193
return reply . send ( ERRORS . FCC_EINVAL_EXAM_ID ( maybeExam . error . message ) ) ;
177
194
}
178
195
196
+ logger . error ( { examError : maybeExam . error } ) ;
179
197
void reply . code ( 500 ) ;
180
198
return reply . send (
181
199
ERRORS . FCC_ERR_EXAM_ENVIRONMENT ( JSON . stringify ( maybeExam . error ) )
@@ -185,6 +203,7 @@ async function postExamGeneratedExamHandler(
185
203
const exam = maybeExam . data ;
186
204
187
205
if ( ! exam ) {
206
+ logger . warn ( { examId } , 'No exam with given id.' ) ;
188
207
void reply . code ( 404 ) ;
189
208
return reply . send (
190
209
ERRORS . FCC_ENOENT_EXAM_ENVIRONMENT_MISSING_EXAM ( 'Invalid exam id given.' )
@@ -196,6 +215,10 @@ async function postExamGeneratedExamHandler(
196
215
const isExamPrerequisitesMet = checkPrerequisites ( user , exam . prerequisites ) ;
197
216
198
217
if ( ! isExamPrerequisitesMet ) {
218
+ logger . warn (
219
+ { examId : exam . id } ,
220
+ 'User has not completed prerequisites to take exam.'
221
+ ) ;
199
222
void reply . code ( 403 ) ;
200
223
// TODO: Consider sending unmet prerequisites
201
224
return reply . send (
@@ -216,6 +239,10 @@ async function postExamGeneratedExamHandler(
216
239
) ;
217
240
218
241
if ( maybeExamAttempts . hasError ) {
242
+ logger . error (
243
+ { examAttemptsError : maybeExamAttempts . error } ,
244
+ 'Unable to query exam attempts.'
245
+ ) ;
219
246
void reply . code ( 500 ) ;
220
247
return reply . send (
221
248
ERRORS . FCC_ERR_EXAM_ENVIRONMENT ( JSON . stringify ( maybeExamAttempts . error ) )
@@ -238,6 +265,10 @@ async function postExamGeneratedExamHandler(
238
265
examExpirationTime + exam . config . retakeTimeInMS < Date . now ( ) ;
239
266
240
267
if ( ! retakeAllowed ) {
268
+ logger . warn (
269
+ { examExpirationTime } ,
270
+ 'User has completed exam too recently to retake.'
271
+ ) ;
241
272
void reply . code ( 429 ) ;
242
273
// TODO: Consider sending last completed time
243
274
return reply . send (
@@ -259,13 +290,21 @@ async function postExamGeneratedExamHandler(
259
290
) ;
260
291
261
292
if ( generated . hasError ) {
293
+ logger . error (
294
+ { generatedError : generated . error } ,
295
+ 'Unable to query generated exam.'
296
+ ) ;
262
297
void reply . code ( 500 ) ;
263
298
return reply . send (
264
299
ERRORS . FCC_ERR_EXAM_ENVIRONMENT ( JSON . stringify ( generated . error ) )
265
300
) ;
266
301
}
267
302
268
303
if ( generated . data === null ) {
304
+ logger . error (
305
+ { generatedExamId : lastAttempt . generatedExamId } ,
306
+ 'Generated exam not found.'
307
+ ) ;
269
308
void reply . code ( 500 ) ;
270
309
return reply . send (
271
310
ERRORS . FCC_ERR_EXAM_ENVIRONMENT (
@@ -301,6 +340,7 @@ async function postExamGeneratedExamHandler(
301
340
) ;
302
341
303
342
if ( maybeGeneratedExams . hasError ) {
343
+ logger . error ( { generatedExamsError : maybeGeneratedExams . error } ) ;
304
344
void reply . code ( 500 ) ;
305
345
return reply . send (
306
346
ERRORS . FCC_ERR_EXAM_ENVIRONMENT ( maybeGeneratedExams . error )
@@ -310,12 +350,10 @@ async function postExamGeneratedExamHandler(
310
350
const generatedExams = maybeGeneratedExams . data ;
311
351
312
352
if ( generatedExams . length === 0 ) {
353
+ const errMessage = `Unable to provide a generated exam. Either all generated exams have been exhausted, or all generated exams are deprecated.` ;
354
+ logger . error ( { examId : exam . id } , errMessage ) ;
313
355
void reply . code ( 500 ) ;
314
- return reply . send (
315
- ERRORS . FCC_ERR_EXAM_ENVIRONMENT (
316
- `Unable to provide a generated exam. Either all generated exams have been exhausted, or all generated exams are deprecated.`
317
- )
318
- ) ;
356
+ return reply . send ( ERRORS . FCC_ERR_EXAM_ENVIRONMENT ( errMessage ) ) ;
319
357
}
320
358
321
359
const randomGeneratedExam =
@@ -330,6 +368,7 @@ async function postExamGeneratedExamHandler(
330
368
) ;
331
369
332
370
if ( maybeGeneratedExam . hasError ) {
371
+ logger . error ( { generatedExamError : maybeGeneratedExam . error } ) ;
333
372
void reply . code ( 500 ) ;
334
373
return reply . send (
335
374
// TODO: Consider more specific code
@@ -343,6 +382,10 @@ async function postExamGeneratedExamHandler(
343
382
const generatedExam = maybeGeneratedExam . data ;
344
383
345
384
if ( generatedExam === null ) {
385
+ logger . error (
386
+ { generatedExamId : randomGeneratedExam . id } ,
387
+ 'Generated exam not found.'
388
+ ) ;
346
389
void reply . code ( 500 ) ;
347
390
return reply . send (
348
391
ERRORS . FCC_ERR_EXAM_ENVIRONMENT ( `Unable to locate generated exam.` )
@@ -364,6 +407,7 @@ async function postExamGeneratedExamHandler(
364
407
) ;
365
408
366
409
if ( attempt . hasError ) {
410
+ logger . error ( { attemptError : attempt . error } ) ;
367
411
void reply . code ( 500 ) ;
368
412
return reply . send (
369
413
ERRORS . FCC_ERR_EXAM_ENVIRONMENT_CREATE_EXAM_ATTEMPT (
@@ -378,6 +422,7 @@ async function postExamGeneratedExamHandler(
378
422
) ;
379
423
380
424
if ( maybeUserExam . hasError ) {
425
+ logger . error ( { userExamError : maybeUserExam . error } ) ;
381
426
await this . prisma . envExamAttempt . delete ( {
382
427
where : {
383
428
id : attempt . data . id
@@ -413,6 +458,8 @@ async function postExamAttemptHandler(
413
458
req : UpdateReqType < typeof schemas . examEnvironmentPostExamAttempt > ,
414
459
reply : FastifyReply
415
460
) {
461
+ const logger = this . log . child ( { req } ) ;
462
+ logger . info ( { userId : req . user ?. id } ) ;
416
463
const { attempt } = req . body ;
417
464
418
465
const user = req . user ! ;
@@ -427,6 +474,10 @@ async function postExamAttemptHandler(
427
474
) ;
428
475
429
476
if ( maybeAttempts . hasError ) {
477
+ logger . error (
478
+ { error : maybeAttempts . error } ,
479
+ 'User attempt cannot be linked to an exam attempt.'
480
+ ) ;
430
481
void reply . code ( 500 ) ;
431
482
return reply . send (
432
483
ERRORS . FCC_ERR_EXAM_ENVIRONMENT ( JSON . stringify ( maybeAttempts . error ) )
@@ -436,6 +487,7 @@ async function postExamAttemptHandler(
436
487
const attempts = maybeAttempts . data ;
437
488
438
489
if ( attempts . length === 0 ) {
490
+ logger . warn ( { examId : attempt . examId } , 'No attempts found for user.' ) ;
439
491
void reply . code ( 404 ) ;
440
492
return reply . send (
441
493
ERRORS . FCC_ERR_EXAM_ENVIRONMENT_EXAM_ATTEMPT (
@@ -457,6 +509,7 @@ async function postExamAttemptHandler(
457
509
) ;
458
510
459
511
if ( maybeExam . hasError ) {
512
+ logger . error ( { examError : maybeExam . error } ) ;
460
513
void reply . code ( 500 ) ;
461
514
return reply . send (
462
515
ERRORS . FCC_ERR_EXAM_ENVIRONMENT ( JSON . stringify ( maybeExam . error ) )
@@ -466,6 +519,7 @@ async function postExamAttemptHandler(
466
519
const exam = maybeExam . data ;
467
520
468
521
if ( exam === null ) {
522
+ logger . warn ( { examId : attempt . examId } , 'Invalid exam id given.' ) ;
469
523
void reply . code ( 404 ) ;
470
524
return reply . send (
471
525
ERRORS . FCC_ENOENT_EXAM_ENVIRONMENT_MISSING_EXAM ( 'Invalid exam id given.' )
@@ -476,6 +530,10 @@ async function postExamAttemptHandler(
476
530
latestAttempt . startTimeInMS + exam . config . totalTimeInMS < Date . now ( ) ;
477
531
478
532
if ( isAttemptExpired ) {
533
+ logger . warn (
534
+ { examAttemptId : latestAttempt . id } ,
535
+ 'Attempt has exceeded submission time.'
536
+ ) ;
479
537
void reply . code ( 403 ) ;
480
538
return reply . send (
481
539
ERRORS . FCC_EINVAL_EXAM_ENVIRONMENT_EXAM_ATTEMPT (
@@ -494,6 +552,7 @@ async function postExamAttemptHandler(
494
552
) ;
495
553
496
554
if ( maybeGeneratedExam . hasError ) {
555
+ logger . error ( { generatedExamError : maybeGeneratedExam . error } ) ;
497
556
void reply . code ( 500 ) ;
498
557
return reply . send (
499
558
ERRORS . FCC_ERR_EXAM_ENVIRONMENT ( JSON . stringify ( maybeGeneratedExam . error ) )
@@ -503,6 +562,10 @@ async function postExamAttemptHandler(
503
562
const generatedExam = maybeGeneratedExam . data ;
504
563
505
564
if ( generatedExam === null ) {
565
+ logger . warn (
566
+ { generatedExamId : latestAttempt . generatedExamId } ,
567
+ 'Generated exam not found.'
568
+ ) ;
506
569
void reply . code ( 404 ) ;
507
570
return reply . send (
508
571
ERRORS . FCC_ENOENT_EXAM_ENVIRONMENT_GENERATED_EXAM (
@@ -536,6 +599,10 @@ async function postExamAttemptHandler(
536
599
) ;
537
600
538
601
if ( maybeValidExamAttempt . hasError ) {
602
+ logger . warn (
603
+ { validExamAttemptError : maybeValidExamAttempt . error } ,
604
+ 'Invalid exam attempt.'
605
+ ) ;
539
606
void reply . code ( 400 ) ;
540
607
const message =
541
608
maybeValidExamAttempt . error instanceof Error
@@ -545,6 +612,7 @@ async function postExamAttemptHandler(
545
612
}
546
613
547
614
if ( maybeUpdatedAttempt . hasError ) {
615
+ logger . error ( { updatedAttemptError : maybeUpdatedAttempt . error } ) ;
548
616
void reply . code ( 500 ) ;
549
617
return reply . send (
550
618
ERRORS . FCC_ERR_EXAM_ENVIRONMENT ( JSON . stringify ( maybeUpdatedAttempt . error ) )
@@ -564,9 +632,12 @@ async function postScreenshotHandler(
564
632
req : UpdateReqType < typeof schemas . examEnvironmentPostScreenshot > ,
565
633
reply : FastifyReply
566
634
) {
635
+ const logger = this . log . child ( { req } ) ;
636
+ logger . info ( { userId : req . user ?. id } ) ;
567
637
const isMultipart = req . isMultipart ( ) ;
568
638
569
639
if ( ! isMultipart ) {
640
+ logger . warn ( 'Request is not multipart form data.' ) ;
570
641
void reply . code ( 400 ) ;
571
642
return reply . send (
572
643
ERRORS . FCC_EINVAL_EXAM_ENVIRONMENT_SCREENSHOT (
@@ -579,6 +650,7 @@ async function postScreenshotHandler(
579
650
const imgData = await req . file ( ) ;
580
651
581
652
if ( ! imgData ) {
653
+ logger . warn ( 'No image provided.' ) ;
582
654
void reply . code ( 400 ) ;
583
655
return reply . send (
584
656
ERRORS . FCC_EINVAL_EXAM_ENVIRONMENT_SCREENSHOT ( 'No image provided.' )
@@ -594,6 +666,10 @@ async function postScreenshotHandler(
594
666
) ;
595
667
596
668
if ( maybeAttempt . hasError ) {
669
+ logger . error (
670
+ { error : maybeAttempt . error } ,
671
+ 'User screenshot cannot be linked to an exam attempt.'
672
+ ) ;
597
673
void reply . code ( 500 ) ;
598
674
return reply . send (
599
675
ERRORS . FCC_ERR_EXAM_ENVIRONMENT ( JSON . stringify ( maybeAttempt . error ) )
@@ -603,6 +679,7 @@ async function postScreenshotHandler(
603
679
const attempt = maybeAttempt . data ;
604
680
605
681
if ( attempt . length === 0 ) {
682
+ logger . warn ( 'No exam attempts found for user.' ) ;
606
683
void reply . code ( 404 ) ;
607
684
return reply . send (
608
685
ERRORS . FCC_ERR_EXAM_ENVIRONMENT_EXAM_ATTEMPT (
@@ -615,6 +692,7 @@ async function postScreenshotHandler(
615
692
616
693
// Verify image is JPG using magic number
617
694
if ( imgBinary [ 0 ] !== 0xff || imgBinary [ 1 ] !== 0xd8 || imgBinary [ 2 ] !== 0xff ) {
695
+ logger . warn ( 'Invalid image format' ) ;
618
696
void reply . code ( 400 ) ;
619
697
return reply . send (
620
698
ERRORS . FCC_EINVAL_EXAM_ENVIRONMENT_SCREENSHOT ( 'Invalid image format.' )
@@ -642,6 +720,9 @@ async function getExams(
642
720
req : UpdateReqType < typeof schemas . examEnvironmentExams > ,
643
721
reply : FastifyReply
644
722
) {
723
+ const logger = this . log . child ( { req } ) ;
724
+ logger . info ( { user : req . user } ) ;
725
+
645
726
const user = req . user ! ;
646
727
const exams = await this . prisma . envExam . findMany ( {
647
728
where : {
0 commit comments