dce-reactkit 3.1.19 → 3.2.0-beta.1
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/dist/cjs/index.js +373 -25
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/components/AppWrapper.d.ts +14 -3
- 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 +5 -1
- 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 +22 -0
- package/dist/cjs/types/types/LogFunction.d.ts +20 -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 +371 -26
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/components/AppWrapper.d.ts +14 -3
- 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 +5 -1
- 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 +22 -0
- package/dist/esm/types/types/LogFunction.d.ts +20 -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 +165 -19
- package/package.json +1 -1
- package/src/components/AppWrapper.tsx +32 -13
- package/src/constants/LOG_ROUTE_PATH.ts +9 -0
- package/src/constants/ROUTE_PATH_PREFIX.ts +7 -0
- package/src/helpers/genRouteHandler.ts +129 -1
- package/src/helpers/logClientEvent.tsx +50 -0
- package/src/helpers/parseUserAgent.ts +108 -0
- package/src/index.ts +8 -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 +38 -0
- package/src/types/LogFunction.ts +41 -0
- package/src/types/LogSource.ts +12 -0
- package/src/types/LogType.ts +12 -0
|
@@ -0,0 +1,20 @@
|
|
|
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
|
+
category: string;
|
|
9
|
+
subcategory?: string;
|
|
10
|
+
tags?: string[];
|
|
11
|
+
metadata?: {
|
|
12
|
+
[k: string]: any;
|
|
13
|
+
};
|
|
14
|
+
} & ({
|
|
15
|
+
error: any;
|
|
16
|
+
} | {
|
|
17
|
+
target: string;
|
|
18
|
+
action: LogAction;
|
|
19
|
+
}))) => Promise<Log>;
|
|
20
|
+
export default LogFunction;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
import React from 'react';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Bootstrap variants
|
|
6
|
+
* @author Gabe Abrams
|
|
7
|
+
*/
|
|
8
|
+
declare enum Variant {
|
|
9
|
+
Primary = "primary",
|
|
10
|
+
Secondary = "secondary",
|
|
11
|
+
Success = "success",
|
|
12
|
+
Warning = "warning",
|
|
13
|
+
Info = "info",
|
|
14
|
+
Danger = "danger",
|
|
15
|
+
Light = "light",
|
|
16
|
+
Dark = "dark"
|
|
17
|
+
}
|
|
18
|
+
|
|
4
19
|
/**
|
|
5
20
|
* A wrapper for the entire React app that adds global functionality like
|
|
6
21
|
* handling for fatal error messages, adds bootstrap support
|
|
@@ -43,11 +58,21 @@ declare const alert: (title: string, text: string) => Promise<undefined>;
|
|
|
43
58
|
* @author Gabe Abrams
|
|
44
59
|
* @param title the title text to display at the top of the alert
|
|
45
60
|
* @param text the text to display in the alert
|
|
46
|
-
* @param [
|
|
47
|
-
* @param [
|
|
61
|
+
* @param [opts={}] additional options for the confirmation dialog
|
|
62
|
+
* @param [opts.confirmButtonText=Okay] the text of the confirm button
|
|
63
|
+
* @param [opts.confirmButtonVariant=Variant.Dark] the variant of the confirm
|
|
64
|
+
* button
|
|
65
|
+
* @param [opts.cancelButtonText=Cancel] the text of the cancel button
|
|
66
|
+
* @param [opts.cancelButtonVariant=Variant.Secondary] the variant of the cancel
|
|
67
|
+
* button
|
|
48
68
|
* @returns true if the user confirmed
|
|
49
69
|
*/
|
|
50
|
-
declare const confirm: (title: string, text: string,
|
|
70
|
+
declare const confirm: (title: string, text: string, opts?: {
|
|
71
|
+
confirmButtonText?: string | undefined;
|
|
72
|
+
confirmButtonVariant?: Variant | undefined;
|
|
73
|
+
cancelButtonText?: string | undefined;
|
|
74
|
+
cancelButtonVariant?: Variant | undefined;
|
|
75
|
+
} | undefined) => Promise<boolean>;
|
|
51
76
|
/**
|
|
52
77
|
* Show a fatal error message
|
|
53
78
|
* @author Gabe Abrams
|
|
@@ -75,21 +100,6 @@ declare type Props$c = {
|
|
|
75
100
|
};
|
|
76
101
|
declare const ErrorBox: React.FC<Props$c>;
|
|
77
102
|
|
|
78
|
-
/**
|
|
79
|
-
* Bootstrap variants
|
|
80
|
-
* @author Gabe Abrams
|
|
81
|
-
*/
|
|
82
|
-
declare enum Variant {
|
|
83
|
-
Primary = "primary",
|
|
84
|
-
Secondary = "secondary",
|
|
85
|
-
Success = "success",
|
|
86
|
-
Warning = "warning",
|
|
87
|
-
Info = "info",
|
|
88
|
-
Danger = "danger",
|
|
89
|
-
Light = "light",
|
|
90
|
-
Dark = "dark"
|
|
91
|
-
}
|
|
92
|
-
|
|
93
103
|
/**
|
|
94
104
|
* Types of buttons in the modal
|
|
95
105
|
* @author Gabe Abrams
|
|
@@ -505,6 +515,135 @@ declare enum ParamType {
|
|
|
505
515
|
StringOptional = "string-optional"
|
|
506
516
|
}
|
|
507
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
|
+
category: string;
|
|
548
|
+
subcategory: 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
|
+
Expand = "expand",
|
|
584
|
+
Collapse = "collapse",
|
|
585
|
+
View = "view",
|
|
586
|
+
Interrupt = "interrupt",
|
|
587
|
+
Create = "create",
|
|
588
|
+
Edit = "edit",
|
|
589
|
+
Delete = "delete",
|
|
590
|
+
Add = "add",
|
|
591
|
+
Remove = "remove",
|
|
592
|
+
Activate = "activate",
|
|
593
|
+
Deactivate = "deactivate",
|
|
594
|
+
Peek = "peek",
|
|
595
|
+
Unknown = "unknown"
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* Type of a log event
|
|
600
|
+
* @author Gabe Abrams
|
|
601
|
+
*/
|
|
602
|
+
declare enum LogType {
|
|
603
|
+
Action = "action",
|
|
604
|
+
Error = "error"
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Log info that is specific to the type of log
|
|
609
|
+
* @author Gabe Abrams
|
|
610
|
+
*/
|
|
611
|
+
declare type LogTypeSpecificInfo = ({
|
|
612
|
+
type: LogType.Error;
|
|
613
|
+
errorMessage: string;
|
|
614
|
+
errorCode: string;
|
|
615
|
+
errorStack: string;
|
|
616
|
+
} | {
|
|
617
|
+
type: LogType.Action;
|
|
618
|
+
target: string;
|
|
619
|
+
action: LogAction;
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* A single log event corresponding to an action performed by a user or an
|
|
624
|
+
* error encountered by a user
|
|
625
|
+
* @author Gabe Abrams
|
|
626
|
+
*/
|
|
627
|
+
declare type Log = (LogMainInfo & LogSourceSpecificInfo & LogTypeSpecificInfo);
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* Type of a log action function
|
|
631
|
+
* @author Gabe Abrams
|
|
632
|
+
*/
|
|
633
|
+
declare type LogFunction = (opts: ({
|
|
634
|
+
category: string;
|
|
635
|
+
subcategory?: string;
|
|
636
|
+
tags?: string[];
|
|
637
|
+
metadata?: {
|
|
638
|
+
[k: string]: any;
|
|
639
|
+
};
|
|
640
|
+
} & ({
|
|
641
|
+
error: any;
|
|
642
|
+
} | {
|
|
643
|
+
target: string;
|
|
644
|
+
action: LogAction;
|
|
645
|
+
}))) => Promise<Log>;
|
|
646
|
+
|
|
508
647
|
/**
|
|
509
648
|
* Generate an express API route handler
|
|
510
649
|
* @author Gabe Abrams
|
|
@@ -546,6 +685,7 @@ declare const genRouteHandler: (opts: {
|
|
|
546
685
|
pageTitle?: string | undefined;
|
|
547
686
|
status?: number | undefined;
|
|
548
687
|
} | undefined) => void;
|
|
688
|
+
logServerEvent: LogFunction;
|
|
549
689
|
}) => any;
|
|
550
690
|
skipSessionCheck?: boolean | undefined;
|
|
551
691
|
}) => (req: any, res: any, next: () => void) => Promise<undefined>;
|
|
@@ -584,10 +724,16 @@ declare type GetLaunchInfoFunction = (req: any) => {
|
|
|
584
724
|
* Prepare dce-reactkit to run on the server
|
|
585
725
|
* @author Gabe Abrams
|
|
586
726
|
* @param opts object containing all arguments
|
|
727
|
+
* @param opts.app express app from inside of the postprocessor function that
|
|
728
|
+
* we will add routes to
|
|
587
729
|
* @param opts.getLaunchInfo CACCL LTI's get launch info function
|
|
730
|
+
* @param [opts.logCollection] mongo collection from dce-mango to use for
|
|
731
|
+
* storing logs. If none is included, logs are written to the console
|
|
588
732
|
*/
|
|
589
733
|
declare const initServer: (opts: {
|
|
734
|
+
app: any;
|
|
590
735
|
getLaunchInfo: GetLaunchInfoFunction;
|
|
736
|
+
logCollection?: any;
|
|
591
737
|
}) => void;
|
|
592
738
|
|
|
593
739
|
/**
|
|
@@ -717,4 +863,4 @@ declare enum DayOfWeek {
|
|
|
717
863
|
Sunday = "u"
|
|
718
864
|
}
|
|
719
865
|
|
|
720
|
-
export { AppWrapper, ButtonInputGroup, CheckboxButton, CopiableBox, DAY_IN_MS, DayOfWeek, Drawer, ErrorBox, ErrorWithCode, HOUR_IN_MS, ItemPicker, LoadingSpinner, 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, initServer, onlyKeepLetters, padDecimalZeros, padZerosLeft, parallelLimit, roundToNumDecimals, showFatalError, startMinWait, stringsToHumanReadableList, stubServerEndpoint, sum, visitServerEndpoint, waitMs };
|
|
866
|
+
export { AppWrapper, ButtonInputGroup, CheckboxButton, CopiableBox, DAY_IN_MS, DayOfWeek, Drawer, ErrorBox, ErrorWithCode, HOUR_IN_MS, ItemPicker, LoadingSpinner, Log, LogAction, 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, initServer, onlyKeepLetters, padDecimalZeros, padZerosLeft, parallelLimit, roundToNumDecimals, showFatalError, startMinWait, stringsToHumanReadableList, stubServerEndpoint, sum, visitServerEndpoint, waitMs };
|
package/package.json
CHANGED
|
@@ -14,6 +14,7 @@ import ErrorBox from './ErrorBox';
|
|
|
14
14
|
import ReactKitErrorCode from '../types/ReactKitErrorCode';
|
|
15
15
|
import ModalButtonType from '../types/ModalButtonType';
|
|
16
16
|
import ModalType from '../types/ModalType';
|
|
17
|
+
import Variant from '../types/Variant';
|
|
17
18
|
|
|
18
19
|
// Import shared components
|
|
19
20
|
import Modal from './Modal';
|
|
@@ -133,8 +134,12 @@ let setConfirmInfo: (
|
|
|
133
134
|
| {
|
|
134
135
|
title: string,
|
|
135
136
|
text: string,
|
|
136
|
-
|
|
137
|
-
|
|
137
|
+
opts: {
|
|
138
|
+
confirmButtonText?: string,
|
|
139
|
+
confirmButtonVariant?: Variant,
|
|
140
|
+
cancelButtonText?: string,
|
|
141
|
+
cancelButtonVariant?: Variant,
|
|
142
|
+
},
|
|
138
143
|
}
|
|
139
144
|
),
|
|
140
145
|
) => void;
|
|
@@ -146,15 +151,24 @@ let onConfirmClosed: (confirmed: boolean) => void;
|
|
|
146
151
|
* @author Gabe Abrams
|
|
147
152
|
* @param title the title text to display at the top of the alert
|
|
148
153
|
* @param text the text to display in the alert
|
|
149
|
-
* @param [
|
|
150
|
-
* @param [
|
|
154
|
+
* @param [opts={}] additional options for the confirmation dialog
|
|
155
|
+
* @param [opts.confirmButtonText=Okay] the text of the confirm button
|
|
156
|
+
* @param [opts.confirmButtonVariant=Variant.Dark] the variant of the confirm
|
|
157
|
+
* button
|
|
158
|
+
* @param [opts.cancelButtonText=Cancel] the text of the cancel button
|
|
159
|
+
* @param [opts.cancelButtonVariant=Variant.Secondary] the variant of the cancel
|
|
160
|
+
* button
|
|
151
161
|
* @returns true if the user confirmed
|
|
152
162
|
*/
|
|
153
163
|
export const confirm = async (
|
|
154
164
|
title: string,
|
|
155
165
|
text: string,
|
|
156
|
-
|
|
157
|
-
|
|
166
|
+
opts?: {
|
|
167
|
+
confirmButtonText?: string,
|
|
168
|
+
confirmButtonVariant?: Variant,
|
|
169
|
+
cancelButtonText?: string,
|
|
170
|
+
cancelButtonVariant?: Variant,
|
|
171
|
+
},
|
|
158
172
|
): Promise<boolean> => {
|
|
159
173
|
// Fallback if confirm is not available
|
|
160
174
|
if (!setConfirmInfo) {
|
|
@@ -172,8 +186,7 @@ export const confirm = async (
|
|
|
172
186
|
setConfirmInfo({
|
|
173
187
|
title,
|
|
174
188
|
text,
|
|
175
|
-
|
|
176
|
-
cancelButtonText,
|
|
189
|
+
opts: (opts ?? {}),
|
|
177
190
|
});
|
|
178
191
|
});
|
|
179
192
|
};
|
|
@@ -290,7 +303,7 @@ const AppWrapper: React.FC<Props> = (props: Props): React.ReactElement => {
|
|
|
290
303
|
undefined
|
|
291
304
|
| {
|
|
292
305
|
title: string,
|
|
293
|
-
text: string
|
|
306
|
+
text: string,
|
|
294
307
|
}
|
|
295
308
|
>(undefined);
|
|
296
309
|
setAlertInfo = setAlertInfoInner;
|
|
@@ -304,8 +317,12 @@ const AppWrapper: React.FC<Props> = (props: Props): React.ReactElement => {
|
|
|
304
317
|
| {
|
|
305
318
|
title: string,
|
|
306
319
|
text: string,
|
|
307
|
-
|
|
308
|
-
|
|
320
|
+
opts: {
|
|
321
|
+
confirmButtonText?: string,
|
|
322
|
+
confirmButtonVariant?: Variant,
|
|
323
|
+
cancelButtonText?: string,
|
|
324
|
+
cancelButtonVariant?: Variant,
|
|
325
|
+
},
|
|
309
326
|
}
|
|
310
327
|
>(undefined);
|
|
311
328
|
setConfirmInfo = setConfirmInfoInner;
|
|
@@ -358,8 +375,10 @@ const AppWrapper: React.FC<Props> = (props: Props): React.ReactElement => {
|
|
|
358
375
|
key={`confirm-${confirmInfo.title}-${confirmInfo.text}`}
|
|
359
376
|
title={confirmInfo.title}
|
|
360
377
|
type={ModalType.OkayCancel}
|
|
361
|
-
okayLabel={confirmInfo.confirmButtonText}
|
|
362
|
-
|
|
378
|
+
okayLabel={confirmInfo.opts.confirmButtonText}
|
|
379
|
+
okayVariant={confirmInfo.opts.confirmButtonVariant}
|
|
380
|
+
cancelLabel={confirmInfo.opts.cancelButtonText}
|
|
381
|
+
cancelVariant={confirmInfo.opts.cancelButtonVariant}
|
|
363
382
|
onClose={(buttonType) => {
|
|
364
383
|
setConfirmInfo(undefined);
|
|
365
384
|
if (onConfirmClosed) {
|
|
@@ -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,17 @@ 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';
|
|
12
26
|
|
|
13
27
|
/**
|
|
14
28
|
* Generate an express API route handler
|
|
@@ -55,6 +69,7 @@ const genRouteHandler = (
|
|
|
55
69
|
status?: number,
|
|
56
70
|
},
|
|
57
71
|
) => void,
|
|
72
|
+
logServerEvent: LogFunction,
|
|
58
73
|
},
|
|
59
74
|
) => any,
|
|
60
75
|
skipSessionCheck?: boolean,
|
|
@@ -439,6 +454,118 @@ const genRouteHandler = (
|
|
|
439
454
|
);
|
|
440
455
|
}
|
|
441
456
|
|
|
457
|
+
/*----------------------------------------*/
|
|
458
|
+
/* Log Handler */
|
|
459
|
+
/*----------------------------------------*/
|
|
460
|
+
|
|
461
|
+
// Create a log handler function
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Log an event on the server
|
|
465
|
+
* @author Gabe Abrams
|
|
466
|
+
*/
|
|
467
|
+
const logServerEvent: LogFunction = async (opts) => {
|
|
468
|
+
// NOTE: internally, we slip through an opts.overrideAsClientEvent boolean
|
|
469
|
+
// that indicates that this is actually a client event, but we don't
|
|
470
|
+
// include that in the LogFunction type because this is internal and
|
|
471
|
+
// hidden from users
|
|
472
|
+
|
|
473
|
+
// Parse user agent
|
|
474
|
+
const {
|
|
475
|
+
browser,
|
|
476
|
+
device,
|
|
477
|
+
} = parseUserAgent(req.headers['user-agent']);
|
|
478
|
+
|
|
479
|
+
// Get time info in ET
|
|
480
|
+
const {
|
|
481
|
+
timestamp,
|
|
482
|
+
year,
|
|
483
|
+
month,
|
|
484
|
+
day,
|
|
485
|
+
hour,
|
|
486
|
+
minute,
|
|
487
|
+
} = getTimeInfoInET();
|
|
488
|
+
|
|
489
|
+
// Main log info
|
|
490
|
+
const mainLogInfo: LogMainInfo = {
|
|
491
|
+
id: `${launchInfo.userId}-${Date.now()}-${Math.floor(Math.random() * 100000)}-${Math.floor(Math.random() * 100000)}`,
|
|
492
|
+
userFirstName: launchInfo.userFirstName,
|
|
493
|
+
userLastName: launchInfo.userLastName,
|
|
494
|
+
userEmail: launchInfo.userEmail,
|
|
495
|
+
userId: launchInfo.userId,
|
|
496
|
+
isLearner: !!launchInfo.isLearner,
|
|
497
|
+
isAdmin: !!launchInfo.isAdmin,
|
|
498
|
+
isTTM: !!launchInfo.isTTM,
|
|
499
|
+
courseId: launchInfo.courseId,
|
|
500
|
+
courseName: launchInfo.courseName,
|
|
501
|
+
browser,
|
|
502
|
+
device,
|
|
503
|
+
year,
|
|
504
|
+
month,
|
|
505
|
+
day,
|
|
506
|
+
hour,
|
|
507
|
+
minute,
|
|
508
|
+
timestamp,
|
|
509
|
+
category: opts.category,
|
|
510
|
+
subcategory: opts.subcategory ?? 'none',
|
|
511
|
+
tags: opts.tags ?? [],
|
|
512
|
+
metadata: opts.metadata ?? {},
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
// Type-specific info
|
|
516
|
+
const typeSpecificInfo: LogTypeSpecificInfo = (
|
|
517
|
+
('error' in opts && opts.error)
|
|
518
|
+
? {
|
|
519
|
+
type: LogType.Error,
|
|
520
|
+
errorMessage: opts.error.message ?? 'Unknown message',
|
|
521
|
+
errorCode: opts.error.code ?? ReactKitErrorCode.NoCode,
|
|
522
|
+
errorStack: opts.error.stack ?? 'No stack',
|
|
523
|
+
}
|
|
524
|
+
: {
|
|
525
|
+
type: LogType.Action,
|
|
526
|
+
target: (opts as any).target ?? 'Unknown',
|
|
527
|
+
action: (opts as any).action ?? 'Unknown'
|
|
528
|
+
}
|
|
529
|
+
);
|
|
530
|
+
|
|
531
|
+
// Source-specific info
|
|
532
|
+
const sourceSpecificInfo: LogSourceSpecificInfo = (
|
|
533
|
+
(opts as any).overrideAsClientEvent
|
|
534
|
+
? {
|
|
535
|
+
source: LogSource.Client,
|
|
536
|
+
}
|
|
537
|
+
: {
|
|
538
|
+
source: LogSource.Server,
|
|
539
|
+
routePath: req.path,
|
|
540
|
+
routeTemplate: req.route.path,
|
|
541
|
+
}
|
|
542
|
+
);
|
|
543
|
+
|
|
544
|
+
// Build log event
|
|
545
|
+
const log: Log = {
|
|
546
|
+
...mainLogInfo,
|
|
547
|
+
...typeSpecificInfo,
|
|
548
|
+
...sourceSpecificInfo,
|
|
549
|
+
};
|
|
550
|
+
|
|
551
|
+
// Either print to console or save to db
|
|
552
|
+
const logCollection = internalGetLogCollection();
|
|
553
|
+
if (logCollection) {
|
|
554
|
+
// Store to the log collection
|
|
555
|
+
await logCollection.insert(log);
|
|
556
|
+
} else {
|
|
557
|
+
// Print to console
|
|
558
|
+
if (log.type === LogType.Error) {
|
|
559
|
+
console.error('dce-reactkit error log:', log);
|
|
560
|
+
} else {
|
|
561
|
+
console.log('dce-reactkit action log:', log);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// Return log entry
|
|
566
|
+
return log;
|
|
567
|
+
};
|
|
568
|
+
|
|
442
569
|
/*------------------------------------------------------------------------*/
|
|
443
570
|
/* Call handler */
|
|
444
571
|
/*------------------------------------------------------------------------*/
|
|
@@ -504,6 +631,7 @@ const genRouteHandler = (
|
|
|
504
631
|
},
|
|
505
632
|
redirect,
|
|
506
633
|
renderErrorPage,
|
|
634
|
+
logServerEvent,
|
|
507
635
|
});
|
|
508
636
|
|
|
509
637
|
// Send results to client (only if next wasn't called)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Import shared types
|
|
2
|
+
import LOG_ROUTE_PATH from '../constants/LOG_ROUTE_PATH';
|
|
3
|
+
import LogFunction from '../types/LogFunction';
|
|
4
|
+
|
|
5
|
+
// Import shared functions
|
|
6
|
+
import visitServerEndpoint from './visitServerEndpoint';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Log a user action on the client (cannot be used on the server)
|
|
10
|
+
* @author Gabe Abrams
|
|
11
|
+
*/
|
|
12
|
+
const logClientEvent: LogFunction = async (opts) => {
|
|
13
|
+
return visitServerEndpoint({
|
|
14
|
+
path: LOG_ROUTE_PATH,
|
|
15
|
+
method: 'POST',
|
|
16
|
+
params: {
|
|
17
|
+
category: opts.category,
|
|
18
|
+
subcategory: opts.subcategory,
|
|
19
|
+
tags: JSON.stringify(opts.tags),
|
|
20
|
+
metadata: JSON.stringify(opts.metadata ?? {}),
|
|
21
|
+
errorMessage: (
|
|
22
|
+
(opts as any).error
|
|
23
|
+
? (opts as any).error.message
|
|
24
|
+
: undefined
|
|
25
|
+
),
|
|
26
|
+
errorCode: (
|
|
27
|
+
(opts as any).error
|
|
28
|
+
? (opts as any).error.code
|
|
29
|
+
: undefined
|
|
30
|
+
),
|
|
31
|
+
errorStack: (
|
|
32
|
+
(opts as any).error
|
|
33
|
+
? (opts as any).error.stack
|
|
34
|
+
: undefined
|
|
35
|
+
),
|
|
36
|
+
target: (
|
|
37
|
+
(opts as any).target
|
|
38
|
+
? (opts as any).target
|
|
39
|
+
: undefined
|
|
40
|
+
),
|
|
41
|
+
action: (
|
|
42
|
+
(opts as any).action
|
|
43
|
+
? (opts as any).action
|
|
44
|
+
: undefined
|
|
45
|
+
),
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export default logClientEvent;
|