dce-reactkit 3.1.20 → 3.2.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vscode/settings.json +6 -0
- package/dist/cjs/index.js +517 -21
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/constants/LOG_ROUTE_PATH.d.ts +6 -0
- package/dist/cjs/types/constants/ROUTE_PATH_PREFIX.d.ts +6 -0
- package/dist/cjs/types/helpers/genRouteHandler.d.ts +2 -0
- package/dist/cjs/types/helpers/logClientEvent.d.ts +7 -0
- package/dist/cjs/types/helpers/parseUserAgent.d.ts +17 -0
- package/dist/cjs/types/index.d.ts +8 -1
- package/dist/cjs/types/server/initLogCollection.d.ts +8 -0
- package/dist/cjs/types/server/initServer.d.ts +13 -0
- package/dist/cjs/types/types/Log/LogMainInfo.d.ts +37 -0
- package/dist/cjs/types/types/Log/LogSourceSpecificInfo.d.ts +13 -0
- package/dist/cjs/types/types/Log/LogTypeSpecificInfo.d.ts +17 -0
- package/dist/cjs/types/types/Log/index.d.ts +10 -0
- package/dist/cjs/types/types/LogAction.d.ts +23 -0
- package/dist/cjs/types/types/LogBuiltInMetadata.d.ts +15 -0
- package/dist/cjs/types/types/LogFunction.d.ts +24 -0
- package/dist/cjs/types/types/LogSource.d.ts +9 -0
- package/dist/cjs/types/types/LogType.d.ts +9 -0
- package/dist/esm/index.js +512 -22
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/constants/LOG_ROUTE_PATH.d.ts +6 -0
- package/dist/esm/types/constants/ROUTE_PATH_PREFIX.d.ts +6 -0
- package/dist/esm/types/helpers/genRouteHandler.d.ts +2 -0
- package/dist/esm/types/helpers/logClientEvent.d.ts +7 -0
- package/dist/esm/types/helpers/parseUserAgent.d.ts +17 -0
- package/dist/esm/types/index.d.ts +8 -1
- package/dist/esm/types/server/initLogCollection.d.ts +8 -0
- package/dist/esm/types/server/initServer.d.ts +13 -0
- package/dist/esm/types/types/Log/LogMainInfo.d.ts +37 -0
- package/dist/esm/types/types/Log/LogSourceSpecificInfo.d.ts +13 -0
- package/dist/esm/types/types/Log/LogTypeSpecificInfo.d.ts +17 -0
- package/dist/esm/types/types/Log/index.d.ts +10 -0
- package/dist/esm/types/types/LogAction.d.ts +23 -0
- package/dist/esm/types/types/LogBuiltInMetadata.d.ts +15 -0
- package/dist/esm/types/types/LogFunction.d.ts +24 -0
- package/dist/esm/types/types/LogSource.d.ts +9 -0
- package/dist/esm/types/types/LogType.d.ts +9 -0
- package/dist/index.d.ts +171 -1
- package/package.json +1 -1
- package/src/components/AppWrapper.tsx +1 -1
- package/src/constants/LOG_ROUTE_PATH.ts +9 -0
- package/src/constants/ROUTE_PATH_PREFIX.ts +7 -0
- package/src/helpers/genRouteHandler.ts +233 -2
- package/src/helpers/logClientEvent.tsx +68 -0
- package/src/helpers/parseUserAgent.ts +108 -0
- package/src/index.ts +14 -0
- package/src/server/initLogCollection.ts +22 -0
- package/src/server/initServer.ts +90 -0
- package/src/types/Log/LogMainInfo.ts +64 -0
- package/src/types/Log/LogSourceSpecificInfo.ts +25 -0
- package/src/types/Log/LogTypeSpecificInfo.ts +33 -0
- package/src/types/Log/index.ts +17 -0
- package/src/types/LogAction.ts +40 -0
- package/src/types/LogBuiltInMetadata.ts +18 -0
- package/src/types/LogFunction.ts +43 -0
- package/src/types/LogSource.ts +12 -0
- package/src/types/LogType.ts +12 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in metadata for logs
|
|
3
|
+
* @author Gabe Abrams
|
|
4
|
+
*/
|
|
5
|
+
declare const LogBuiltInMetadata: {
|
|
6
|
+
Context: {
|
|
7
|
+
Uncategorized: string;
|
|
8
|
+
ServerRenderedErrorPage: string;
|
|
9
|
+
ServerEndpointError: string;
|
|
10
|
+
};
|
|
11
|
+
Target: {
|
|
12
|
+
NoSpecificTarget: string;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
export default LogBuiltInMetadata;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import Log from './Log';
|
|
2
|
+
import LogAction from './LogAction';
|
|
3
|
+
/**
|
|
4
|
+
* Type of a log action function
|
|
5
|
+
* @author Gabe Abrams
|
|
6
|
+
*/
|
|
7
|
+
declare type LogFunction = (opts: ({
|
|
8
|
+
context: string | {
|
|
9
|
+
_: string;
|
|
10
|
+
};
|
|
11
|
+
subcontext?: string | {
|
|
12
|
+
_: string;
|
|
13
|
+
};
|
|
14
|
+
tags?: string[];
|
|
15
|
+
metadata?: {
|
|
16
|
+
[k: string]: any;
|
|
17
|
+
};
|
|
18
|
+
} & ({
|
|
19
|
+
error: any;
|
|
20
|
+
} | {
|
|
21
|
+
action: LogAction;
|
|
22
|
+
target?: string;
|
|
23
|
+
}))) => Promise<Log>;
|
|
24
|
+
export default LogFunction;
|
package/dist/index.d.ts
CHANGED
|
@@ -515,6 +515,140 @@ declare enum ParamType {
|
|
|
515
515
|
StringOptional = "string-optional"
|
|
516
516
|
}
|
|
517
517
|
|
|
518
|
+
/**
|
|
519
|
+
* Main information in a log event
|
|
520
|
+
* @author Gabe Abrams
|
|
521
|
+
*/
|
|
522
|
+
declare type LogMainInfo = {
|
|
523
|
+
id: string;
|
|
524
|
+
userFirstName: string;
|
|
525
|
+
userLastName: string;
|
|
526
|
+
userEmail: string;
|
|
527
|
+
userId: number;
|
|
528
|
+
isLearner: boolean;
|
|
529
|
+
isAdmin: boolean;
|
|
530
|
+
isTTM: boolean;
|
|
531
|
+
courseId: number;
|
|
532
|
+
courseName: string;
|
|
533
|
+
browser: {
|
|
534
|
+
name: string;
|
|
535
|
+
version: string;
|
|
536
|
+
};
|
|
537
|
+
device: {
|
|
538
|
+
os: string;
|
|
539
|
+
isMobile: boolean;
|
|
540
|
+
};
|
|
541
|
+
year: number;
|
|
542
|
+
month: number;
|
|
543
|
+
day: number;
|
|
544
|
+
hour: number;
|
|
545
|
+
minute: number;
|
|
546
|
+
timestamp: number;
|
|
547
|
+
context: string;
|
|
548
|
+
subcontext: string;
|
|
549
|
+
tags: string[];
|
|
550
|
+
metadata?: {
|
|
551
|
+
[k: string]: any;
|
|
552
|
+
};
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Source of a log event
|
|
557
|
+
* @author Gabe Abrams
|
|
558
|
+
*/
|
|
559
|
+
declare enum LogSource {
|
|
560
|
+
Client = "client",
|
|
561
|
+
Server = "server"
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Log info that is specific to the type of source
|
|
566
|
+
* @author Gabe Abrams
|
|
567
|
+
*/
|
|
568
|
+
declare type LogSourceSpecificInfo = ({
|
|
569
|
+
source: LogSource.Client;
|
|
570
|
+
} | {
|
|
571
|
+
source: LogSource.Server;
|
|
572
|
+
routePath: string;
|
|
573
|
+
routeTemplate: string;
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* Types of actions
|
|
578
|
+
* @author Gabe Abrams
|
|
579
|
+
*/
|
|
580
|
+
declare enum LogAction {
|
|
581
|
+
Open = "open",
|
|
582
|
+
Close = "close",
|
|
583
|
+
Cancel = "cancel",
|
|
584
|
+
Expand = "expand",
|
|
585
|
+
Collapse = "collapse",
|
|
586
|
+
View = "view",
|
|
587
|
+
Interrupt = "interrupt",
|
|
588
|
+
Create = "create",
|
|
589
|
+
Edit = "edit",
|
|
590
|
+
Delete = "delete",
|
|
591
|
+
Add = "add",
|
|
592
|
+
Remove = "remove",
|
|
593
|
+
Activate = "activate",
|
|
594
|
+
Deactivate = "deactivate",
|
|
595
|
+
Peek = "peek",
|
|
596
|
+
Unknown = "unknown"
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* Type of a log event
|
|
601
|
+
* @author Gabe Abrams
|
|
602
|
+
*/
|
|
603
|
+
declare enum LogType {
|
|
604
|
+
Action = "action",
|
|
605
|
+
Error = "error"
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* Log info that is specific to the type of log
|
|
610
|
+
* @author Gabe Abrams
|
|
611
|
+
*/
|
|
612
|
+
declare type LogTypeSpecificInfo = ({
|
|
613
|
+
type: LogType.Error;
|
|
614
|
+
errorMessage: string;
|
|
615
|
+
errorCode: string;
|
|
616
|
+
errorStack: string;
|
|
617
|
+
} | {
|
|
618
|
+
type: LogType.Action;
|
|
619
|
+
target: string;
|
|
620
|
+
action: LogAction;
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* A single log event corresponding to an action performed by a user or an
|
|
625
|
+
* error encountered by a user
|
|
626
|
+
* @author Gabe Abrams
|
|
627
|
+
*/
|
|
628
|
+
declare type Log = (LogMainInfo & LogSourceSpecificInfo & LogTypeSpecificInfo);
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* Type of a log action function
|
|
632
|
+
* @author Gabe Abrams
|
|
633
|
+
*/
|
|
634
|
+
declare type LogFunction = (opts: ({
|
|
635
|
+
context: string | {
|
|
636
|
+
_: string;
|
|
637
|
+
};
|
|
638
|
+
subcontext?: string | {
|
|
639
|
+
_: string;
|
|
640
|
+
};
|
|
641
|
+
tags?: string[];
|
|
642
|
+
metadata?: {
|
|
643
|
+
[k: string]: any;
|
|
644
|
+
};
|
|
645
|
+
} & ({
|
|
646
|
+
error: any;
|
|
647
|
+
} | {
|
|
648
|
+
action: LogAction;
|
|
649
|
+
target?: string;
|
|
650
|
+
}))) => Promise<Log>;
|
|
651
|
+
|
|
518
652
|
/**
|
|
519
653
|
* Generate an express API route handler
|
|
520
654
|
* @author Gabe Abrams
|
|
@@ -556,6 +690,7 @@ declare const genRouteHandler: (opts: {
|
|
|
556
690
|
pageTitle?: string | undefined;
|
|
557
691
|
status?: number | undefined;
|
|
558
692
|
} | undefined) => void;
|
|
693
|
+
logServerEvent: LogFunction;
|
|
559
694
|
}) => any;
|
|
560
695
|
skipSessionCheck?: boolean | undefined;
|
|
561
696
|
}) => (req: any, res: any, next: () => void) => Promise<undefined>;
|
|
@@ -594,10 +729,16 @@ declare type GetLaunchInfoFunction = (req: any) => {
|
|
|
594
729
|
* Prepare dce-reactkit to run on the server
|
|
595
730
|
* @author Gabe Abrams
|
|
596
731
|
* @param opts object containing all arguments
|
|
732
|
+
* @param opts.app express app from inside of the postprocessor function that
|
|
733
|
+
* we will add routes to
|
|
597
734
|
* @param opts.getLaunchInfo CACCL LTI's get launch info function
|
|
735
|
+
* @param [opts.logCollection] mongo collection from dce-mango to use for
|
|
736
|
+
* storing logs. If none is included, logs are written to the console
|
|
598
737
|
*/
|
|
599
738
|
declare const initServer: (opts: {
|
|
739
|
+
app: any;
|
|
600
740
|
getLaunchInfo: GetLaunchInfoFunction;
|
|
741
|
+
logCollection?: any;
|
|
601
742
|
}) => void;
|
|
602
743
|
|
|
603
744
|
/**
|
|
@@ -696,6 +837,20 @@ declare const onlyKeepLetters: (str: string) => string;
|
|
|
696
837
|
*/
|
|
697
838
|
declare const parallelLimit: (taskFunctions: (() => Promise<any>)[], limit?: number | undefined) => Promise<any[]>;
|
|
698
839
|
|
|
840
|
+
/**
|
|
841
|
+
* Log a user action on the client (cannot be used on the server)
|
|
842
|
+
* @author Gabe Abrams
|
|
843
|
+
*/
|
|
844
|
+
declare const logClientEvent: LogFunction;
|
|
845
|
+
|
|
846
|
+
/**
|
|
847
|
+
* Initialize a log collection given the dce-mango Collection class
|
|
848
|
+
* @author Gabe Abrams
|
|
849
|
+
* @param Collection the Collection class from dce-mango
|
|
850
|
+
* @returns initialized logCollection
|
|
851
|
+
*/
|
|
852
|
+
declare const initLogCollection: (Collection: any) => any;
|
|
853
|
+
|
|
699
854
|
/**
|
|
700
855
|
* List of error codes built into the react kit
|
|
701
856
|
* @author Gabe Abrams
|
|
@@ -727,4 +882,19 @@ declare enum DayOfWeek {
|
|
|
727
882
|
Sunday = "u"
|
|
728
883
|
}
|
|
729
884
|
|
|
730
|
-
|
|
885
|
+
/**
|
|
886
|
+
* Built-in metadata for logs
|
|
887
|
+
* @author Gabe Abrams
|
|
888
|
+
*/
|
|
889
|
+
declare const LogBuiltInMetadata: {
|
|
890
|
+
Context: {
|
|
891
|
+
Uncategorized: string;
|
|
892
|
+
ServerRenderedErrorPage: string;
|
|
893
|
+
ServerEndpointError: string;
|
|
894
|
+
};
|
|
895
|
+
Target: {
|
|
896
|
+
NoSpecificTarget: string;
|
|
897
|
+
};
|
|
898
|
+
};
|
|
899
|
+
|
|
900
|
+
export { AppWrapper, ButtonInputGroup, CheckboxButton, CopiableBox, DAY_IN_MS, DayOfWeek, Drawer, ErrorBox, ErrorWithCode, HOUR_IN_MS, ItemPicker, LoadingSpinner, Log, LogAction, LogBuiltInMetadata, LogSource, LogType, MINUTE_IN_MS, Modal, ModalButtonType, ModalSize, ModalType, ParamType, PickableItem, PopFailureMark, PopPendingMark, PopSuccessMark, RadioButton, ReactKitErrorCode, SimpleDateChooser, TabBox, Variant, abbreviate, alert, avg, ceilToNumDecimals, confirm, floorToNumDecimals, forceNumIntoBounds, genRouteHandler, getHumanReadableDate, getOrdinal, getPartOfDay, getTimeInfoInET, handleError, handleSuccess, initLogCollection, initServer, logClientEvent, onlyKeepLetters, padDecimalZeros, padZerosLeft, parallelLimit, roundToNumDecimals, showFatalError, startMinWait, stringsToHumanReadableList, stubServerEndpoint, sum, visitServerEndpoint, waitMs };
|
package/package.json
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
// Import caccl functions
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
cacclGetLaunchInfo,
|
|
4
|
+
internalGetLogCollection,
|
|
5
|
+
} from '../server/initServer';
|
|
3
6
|
|
|
4
7
|
// Import shared types
|
|
5
8
|
import ReactKitErrorCode from '../types/ReactKitErrorCode';
|
|
@@ -9,6 +12,19 @@ import ParamType from '../types/ParamType';
|
|
|
9
12
|
import handleError from './handleError';
|
|
10
13
|
import handleSuccess from './handleSuccess';
|
|
11
14
|
import genErrorPage from '../html/genErrorPage';
|
|
15
|
+
import parseUserAgent from './parseUserAgent';
|
|
16
|
+
import getTimeInfoInET from './getTimeInfoInET';
|
|
17
|
+
|
|
18
|
+
// Import shared types
|
|
19
|
+
import LogFunction from '../types/LogFunction';
|
|
20
|
+
import Log from '../types/Log';
|
|
21
|
+
import LogType from '../types/LogType';
|
|
22
|
+
import LogSource from '../types/LogSource';
|
|
23
|
+
import LogTypeSpecificInfo from '../types/Log/LogTypeSpecificInfo';
|
|
24
|
+
import LogMainInfo from '../types/Log/LogMainInfo';
|
|
25
|
+
import LogSourceSpecificInfo from '../types/Log/LogSourceSpecificInfo';
|
|
26
|
+
import LogBuiltInMetadata from '../types/LogBuiltInMetadata';
|
|
27
|
+
import LogAction from '../types/LogAction';
|
|
12
28
|
|
|
13
29
|
/**
|
|
14
30
|
* Generate an express API route handler
|
|
@@ -55,6 +71,7 @@ const genRouteHandler = (
|
|
|
55
71
|
status?: number,
|
|
56
72
|
},
|
|
57
73
|
) => void,
|
|
74
|
+
logServerEvent: LogFunction,
|
|
58
75
|
},
|
|
59
76
|
) => any,
|
|
60
77
|
skipSessionCheck?: boolean,
|
|
@@ -439,6 +456,195 @@ const genRouteHandler = (
|
|
|
439
456
|
);
|
|
440
457
|
}
|
|
441
458
|
|
|
459
|
+
/*----------------------------------------*/
|
|
460
|
+
/* Log Handler */
|
|
461
|
+
/*----------------------------------------*/
|
|
462
|
+
|
|
463
|
+
// Create a log handler function
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Log an event on the server
|
|
467
|
+
* @author Gabe Abrams
|
|
468
|
+
*/
|
|
469
|
+
const logServerEvent: LogFunction = async (opts) => {
|
|
470
|
+
// NOTE: internally, we slip through an opts.overrideAsClientEvent boolean
|
|
471
|
+
// that indicates that this is actually a client event, but we don't
|
|
472
|
+
// include that in the LogFunction type because this is internal and
|
|
473
|
+
// hidden from users
|
|
474
|
+
try {
|
|
475
|
+
// Parse user agent
|
|
476
|
+
const {
|
|
477
|
+
browser,
|
|
478
|
+
device,
|
|
479
|
+
} = parseUserAgent(req.headers['user-agent']);
|
|
480
|
+
|
|
481
|
+
// Get time info in ET
|
|
482
|
+
const {
|
|
483
|
+
timestamp,
|
|
484
|
+
year,
|
|
485
|
+
month,
|
|
486
|
+
day,
|
|
487
|
+
hour,
|
|
488
|
+
minute,
|
|
489
|
+
} = getTimeInfoInET();
|
|
490
|
+
|
|
491
|
+
// Main log info
|
|
492
|
+
const mainLogInfo: LogMainInfo = {
|
|
493
|
+
id: `${launchInfo.userId}-${Date.now()}-${Math.floor(Math.random() * 100000)}-${Math.floor(Math.random() * 100000)}`,
|
|
494
|
+
userFirstName: launchInfo.userFirstName,
|
|
495
|
+
userLastName: launchInfo.userLastName,
|
|
496
|
+
userEmail: launchInfo.userEmail,
|
|
497
|
+
userId: launchInfo.userId,
|
|
498
|
+
isLearner: !!launchInfo.isLearner,
|
|
499
|
+
isAdmin: !!launchInfo.isAdmin,
|
|
500
|
+
isTTM: !!launchInfo.isTTM,
|
|
501
|
+
courseId: launchInfo.courseId,
|
|
502
|
+
courseName: launchInfo.courseName,
|
|
503
|
+
browser,
|
|
504
|
+
device,
|
|
505
|
+
year,
|
|
506
|
+
month,
|
|
507
|
+
day,
|
|
508
|
+
hour,
|
|
509
|
+
minute,
|
|
510
|
+
timestamp,
|
|
511
|
+
context: (
|
|
512
|
+
typeof opts.context === 'string'
|
|
513
|
+
? opts.context
|
|
514
|
+
: (
|
|
515
|
+
((opts.context as any) ?? {})._
|
|
516
|
+
?? LogBuiltInMetadata.Context.Uncategorized
|
|
517
|
+
)
|
|
518
|
+
),
|
|
519
|
+
subcontext: (
|
|
520
|
+
typeof opts.context === 'string'
|
|
521
|
+
? opts.subcontext
|
|
522
|
+
: (
|
|
523
|
+
((opts.subcontext as any) ?? {})._
|
|
524
|
+
?? LogBuiltInMetadata.Context.Uncategorized
|
|
525
|
+
)
|
|
526
|
+
),
|
|
527
|
+
tags: opts.tags ?? [],
|
|
528
|
+
metadata: opts.metadata ?? {},
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
// Type-specific info
|
|
532
|
+
const typeSpecificInfo: LogTypeSpecificInfo = (
|
|
533
|
+
('error' in opts && opts.error)
|
|
534
|
+
? {
|
|
535
|
+
type: LogType.Error,
|
|
536
|
+
errorMessage: opts.error.message ?? 'Unknown message',
|
|
537
|
+
errorCode: opts.error.code ?? ReactKitErrorCode.NoCode,
|
|
538
|
+
errorStack: opts.error.stack ?? 'No stack',
|
|
539
|
+
}
|
|
540
|
+
: {
|
|
541
|
+
type: LogType.Action,
|
|
542
|
+
target: (
|
|
543
|
+
(opts as any).target
|
|
544
|
+
?? LogBuiltInMetadata.Target.NoSpecificTarget
|
|
545
|
+
),
|
|
546
|
+
action: (
|
|
547
|
+
(opts as any).action
|
|
548
|
+
?? LogAction.Unknown
|
|
549
|
+
),
|
|
550
|
+
}
|
|
551
|
+
);
|
|
552
|
+
|
|
553
|
+
// Source-specific info
|
|
554
|
+
const sourceSpecificInfo: LogSourceSpecificInfo = (
|
|
555
|
+
(opts as any).overrideAsClientEvent
|
|
556
|
+
? {
|
|
557
|
+
source: LogSource.Client,
|
|
558
|
+
}
|
|
559
|
+
: {
|
|
560
|
+
source: LogSource.Server,
|
|
561
|
+
routePath: req.path,
|
|
562
|
+
routeTemplate: req.route.path,
|
|
563
|
+
}
|
|
564
|
+
);
|
|
565
|
+
|
|
566
|
+
// Build log event
|
|
567
|
+
const log: Log = {
|
|
568
|
+
...mainLogInfo,
|
|
569
|
+
...typeSpecificInfo,
|
|
570
|
+
...sourceSpecificInfo,
|
|
571
|
+
};
|
|
572
|
+
|
|
573
|
+
// Either print to console or save to db
|
|
574
|
+
const logCollection = internalGetLogCollection();
|
|
575
|
+
if (logCollection) {
|
|
576
|
+
// Store to the log collection
|
|
577
|
+
await logCollection.insert(log);
|
|
578
|
+
} else {
|
|
579
|
+
// Print to console
|
|
580
|
+
if (log.type === LogType.Error) {
|
|
581
|
+
console.error('dce-reactkit error log:', log);
|
|
582
|
+
} else {
|
|
583
|
+
console.log('dce-reactkit action log:', log);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// Return log entry
|
|
588
|
+
return log;
|
|
589
|
+
} catch (err) {
|
|
590
|
+
// Print because we cannot store the error
|
|
591
|
+
console.error('Could not log the following:', opts);
|
|
592
|
+
|
|
593
|
+
// Create a dummy log to return
|
|
594
|
+
const dummyMainInfo: LogMainInfo = {
|
|
595
|
+
id: '-1',
|
|
596
|
+
userFirstName: 'Unknown',
|
|
597
|
+
userLastName: 'Unknown',
|
|
598
|
+
userEmail: 'unknown@harvard.edu',
|
|
599
|
+
userId: 1,
|
|
600
|
+
isLearner: false,
|
|
601
|
+
isAdmin: false,
|
|
602
|
+
isTTM: false,
|
|
603
|
+
courseId: 1,
|
|
604
|
+
courseName: 'Unknown',
|
|
605
|
+
browser: {
|
|
606
|
+
name: 'Unknown',
|
|
607
|
+
version: 'Unknown',
|
|
608
|
+
},
|
|
609
|
+
device: {
|
|
610
|
+
isMobile: false,
|
|
611
|
+
os: 'Unknown',
|
|
612
|
+
},
|
|
613
|
+
year: 1,
|
|
614
|
+
month: 1,
|
|
615
|
+
day: 1,
|
|
616
|
+
hour: 1,
|
|
617
|
+
minute: 1,
|
|
618
|
+
timestamp: Date.now(),
|
|
619
|
+
tags: [],
|
|
620
|
+
metadata: {},
|
|
621
|
+
context: LogBuiltInMetadata.Context.Uncategorized,
|
|
622
|
+
subcontext: LogBuiltInMetadata.Context.Uncategorized,
|
|
623
|
+
};
|
|
624
|
+
|
|
625
|
+
const dummyTypeSpecificInfo: LogTypeSpecificInfo = {
|
|
626
|
+
type: LogType.Error,
|
|
627
|
+
errorMessage: 'Unknown',
|
|
628
|
+
errorCode: 'Unknown',
|
|
629
|
+
errorStack: 'No Stack',
|
|
630
|
+
};
|
|
631
|
+
|
|
632
|
+
const dummySourceSpecificInfo: LogSourceSpecificInfo = {
|
|
633
|
+
source: LogSource.Server,
|
|
634
|
+
routePath: req.path,
|
|
635
|
+
routeTemplate: req.route.path,
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
const log: Log = {
|
|
639
|
+
...dummyMainInfo,
|
|
640
|
+
...dummyTypeSpecificInfo,
|
|
641
|
+
...dummySourceSpecificInfo,
|
|
642
|
+
};
|
|
643
|
+
|
|
644
|
+
return log;
|
|
645
|
+
}
|
|
646
|
+
};
|
|
647
|
+
|
|
442
648
|
/*------------------------------------------------------------------------*/
|
|
443
649
|
/* Call handler */
|
|
444
650
|
/*------------------------------------------------------------------------*/
|
|
@@ -490,6 +696,22 @@ const genRouteHandler = (
|
|
|
490
696
|
) => {
|
|
491
697
|
const html = genErrorPage(opts);
|
|
492
698
|
send(html, opts.status ?? 500);
|
|
699
|
+
|
|
700
|
+
// Log
|
|
701
|
+
logServerEvent({
|
|
702
|
+
context: LogBuiltInMetadata.Context.ServerRenderedErrorPage,
|
|
703
|
+
error: {
|
|
704
|
+
message: `${opts.title}: ${opts.description}`,
|
|
705
|
+
code: opts.code,
|
|
706
|
+
},
|
|
707
|
+
metadata: {
|
|
708
|
+
title: opts.title,
|
|
709
|
+
description: opts.description,
|
|
710
|
+
code: opts.code,
|
|
711
|
+
pageTitle: opts.pageTitle,
|
|
712
|
+
status: opts.status ?? 500,
|
|
713
|
+
},
|
|
714
|
+
});
|
|
493
715
|
};
|
|
494
716
|
|
|
495
717
|
// Call the handler
|
|
@@ -504,6 +726,7 @@ const genRouteHandler = (
|
|
|
504
726
|
},
|
|
505
727
|
redirect,
|
|
506
728
|
renderErrorPage,
|
|
729
|
+
logServerEvent,
|
|
507
730
|
});
|
|
508
731
|
|
|
509
732
|
// Send results to client (only if next wasn't called)
|
|
@@ -513,7 +736,15 @@ const genRouteHandler = (
|
|
|
513
736
|
} catch (err) {
|
|
514
737
|
// Send error to client (only if next wasn't called)
|
|
515
738
|
if (!responseSent) {
|
|
516
|
-
|
|
739
|
+
handleError(res, err);
|
|
740
|
+
|
|
741
|
+
// Log server-side error
|
|
742
|
+
logServerEvent({
|
|
743
|
+
context: LogBuiltInMetadata.Context.ServerEndpointError,
|
|
744
|
+
error: err,
|
|
745
|
+
});
|
|
746
|
+
|
|
747
|
+
return;
|
|
517
748
|
}
|
|
518
749
|
|
|
519
750
|
// Log error that was not responded with
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Import shared types
|
|
2
|
+
import LOG_ROUTE_PATH from '../constants/LOG_ROUTE_PATH';
|
|
3
|
+
import LogBuiltInMetadata from '../types/LogBuiltInMetadata';
|
|
4
|
+
import LogFunction from '../types/LogFunction';
|
|
5
|
+
|
|
6
|
+
// Import shared functions
|
|
7
|
+
import visitServerEndpoint from './visitServerEndpoint';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Log a user action on the client (cannot be used on the server)
|
|
11
|
+
* @author Gabe Abrams
|
|
12
|
+
*/
|
|
13
|
+
const logClientEvent: LogFunction = async (opts) => {
|
|
14
|
+
return visitServerEndpoint({
|
|
15
|
+
path: LOG_ROUTE_PATH,
|
|
16
|
+
method: 'POST',
|
|
17
|
+
params: {
|
|
18
|
+
context: (
|
|
19
|
+
typeof opts.context === 'string'
|
|
20
|
+
? opts.context
|
|
21
|
+
: (
|
|
22
|
+
((opts.context as any) ?? {})._
|
|
23
|
+
?? LogBuiltInMetadata.Context.Uncategorized
|
|
24
|
+
)
|
|
25
|
+
),
|
|
26
|
+
subcontext: (
|
|
27
|
+
typeof opts.context === 'string'
|
|
28
|
+
? opts.subcontext
|
|
29
|
+
: (
|
|
30
|
+
((opts.subcontext as any) ?? {})._
|
|
31
|
+
?? LogBuiltInMetadata.Context.Uncategorized
|
|
32
|
+
)
|
|
33
|
+
),
|
|
34
|
+
tags: JSON.stringify(opts.tags ?? []),
|
|
35
|
+
metadata: JSON.stringify(opts.metadata ?? {}),
|
|
36
|
+
errorMessage: (
|
|
37
|
+
(opts as any).error
|
|
38
|
+
? (opts as any).error.message
|
|
39
|
+
: undefined
|
|
40
|
+
),
|
|
41
|
+
errorCode: (
|
|
42
|
+
(opts as any).error
|
|
43
|
+
? (opts as any).error.code
|
|
44
|
+
: undefined
|
|
45
|
+
),
|
|
46
|
+
errorStack: (
|
|
47
|
+
(opts as any).error
|
|
48
|
+
? (opts as any).error.stack
|
|
49
|
+
: undefined
|
|
50
|
+
),
|
|
51
|
+
target: (
|
|
52
|
+
(opts as any).action
|
|
53
|
+
? (
|
|
54
|
+
(opts as any).target
|
|
55
|
+
?? LogBuiltInMetadata.Target.NoSpecificTarget
|
|
56
|
+
)
|
|
57
|
+
: undefined
|
|
58
|
+
),
|
|
59
|
+
action: (
|
|
60
|
+
(opts as any).action
|
|
61
|
+
? (opts as any).action
|
|
62
|
+
: undefined
|
|
63
|
+
),
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export default logClientEvent;
|