dce-reactkit 3.1.14 → 3.1.16

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.
@@ -6,14 +6,16 @@ import ParamType from '../types/ParamType';
6
6
  * @param opts.paramTypes map containing the types for each parameter that is
7
7
  * included in the request (map: param name => type)
8
8
  * @param opts.handler function that processes the request
9
- * @param [opts.allowNotLoggedIn] if true, allow the user to not be logged
10
- * in (the user will be allowed to not have launched via LTI)
11
9
  * @returns express route handler that takes the following arguments:
12
- * params (map: param name => value), handleSuccess (function for handling
13
- * successful requests), handleError (function for handling failed requests),
14
- * req (express request object), redirect (a function that takes a url and
15
- * redirects the user to the url), and send (a function that sends a string
16
- * and optional http status code). Params also has userId, userFirstName,
10
+ * params (map: param name => value),
11
+ * req (express request object),
12
+ * next (express next function),
13
+ * send (a function that sends a string to the client),
14
+ * redirect (takes a url and redirects the user to that url),
15
+ * renderErrorPage (shows a static error page to the user),
16
+ * and returns the value to send to the client as a JSON API response, or
17
+ * calls next() or redirect(...) or send(...) or renderErrorPage(...).
18
+ * Note: params also has userId, userFirstName,
17
19
  * userLastName, isLearner, isTTM, isAdmin, and any other variables that
18
20
  * are directly added to the session
19
21
  */
@@ -29,7 +31,13 @@ declare const genRouteHandler: (opts: {
29
31
  next: () => void;
30
32
  redirect: (pathOrURL: string) => void;
31
33
  send: (text: string, status?: number | undefined) => void;
34
+ renderErrorPage: (opts?: {
35
+ title?: string | undefined;
36
+ description?: string | undefined;
37
+ code?: string | undefined;
38
+ pageTitle?: string | undefined;
39
+ status?: number | undefined;
40
+ } | undefined) => void;
32
41
  }) => any;
33
- allowNotLoggedIn?: boolean | undefined;
34
42
  }) => (req: any, res: any, next: () => void) => Promise<undefined>;
35
43
  export default genRouteHandler;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Generate a static error page
3
+ * @author Gabe Abrams
4
+ * @param opts object containing all arguments
5
+ * @param [opts.title=An Error Occurred] title of the error box
6
+ * @param [opts.description=An unknown server error occurred. Please contact support.]
7
+ * a human-readable description of the error
8
+ * @param [opts.code=ReactKitErrorCode.NoCode] error code to show
9
+ * @param [opts.pageTitle=opts.title] title of the page/tab if it differs from
10
+ * the title of the error
11
+ * @returns html of the page
12
+ */
13
+ declare const genErrorPage: (opts?: {
14
+ title?: string;
15
+ description?: string;
16
+ code?: string;
17
+ pageTitle?: string;
18
+ }) => string;
19
+ export default genErrorPage;
package/dist/index.d.ts CHANGED
@@ -509,14 +509,16 @@ declare enum ParamType {
509
509
  * @param opts.paramTypes map containing the types for each parameter that is
510
510
  * included in the request (map: param name => type)
511
511
  * @param opts.handler function that processes the request
512
- * @param [opts.allowNotLoggedIn] if true, allow the user to not be logged
513
- * in (the user will be allowed to not have launched via LTI)
514
512
  * @returns express route handler that takes the following arguments:
515
- * params (map: param name => value), handleSuccess (function for handling
516
- * successful requests), handleError (function for handling failed requests),
517
- * req (express request object), redirect (a function that takes a url and
518
- * redirects the user to the url), and send (a function that sends a string
519
- * and optional http status code). Params also has userId, userFirstName,
513
+ * params (map: param name => value),
514
+ * req (express request object),
515
+ * next (express next function),
516
+ * send (a function that sends a string to the client),
517
+ * redirect (takes a url and redirects the user to that url),
518
+ * renderErrorPage (shows a static error page to the user),
519
+ * and returns the value to send to the client as a JSON API response, or
520
+ * calls next() or redirect(...) or send(...) or renderErrorPage(...).
521
+ * Note: params also has userId, userFirstName,
520
522
  * userLastName, isLearner, isTTM, isAdmin, and any other variables that
521
523
  * are directly added to the session
522
524
  */
@@ -532,8 +534,14 @@ declare const genRouteHandler: (opts: {
532
534
  next: () => void;
533
535
  redirect: (pathOrURL: string) => void;
534
536
  send: (text: string, status?: number | undefined) => void;
537
+ renderErrorPage: (opts?: {
538
+ title?: string | undefined;
539
+ description?: string | undefined;
540
+ code?: string | undefined;
541
+ pageTitle?: string | undefined;
542
+ status?: number | undefined;
543
+ } | undefined) => void;
535
544
  }) => any;
536
- allowNotLoggedIn?: boolean | undefined;
537
545
  }) => (req: any, res: any, next: () => void) => Promise<undefined>;
538
546
 
539
547
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dce-reactkit",
3
- "version": "3.1.14",
3
+ "version": "3.1.16",
4
4
  "description": "Shared components for Harvard DCE apps",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -8,6 +8,7 @@ import ParamType from '../types/ParamType';
8
8
  // Import helpers
9
9
  import handleError from './handleError';
10
10
  import handleSuccess from './handleSuccess';
11
+ import genErrorPage from '../html/genErrorPage';
11
12
 
12
13
  /**
13
14
  * Generate an express API route handler
@@ -17,11 +18,15 @@ import handleSuccess from './handleSuccess';
17
18
  * included in the request (map: param name => type)
18
19
  * @param opts.handler function that processes the request
19
20
  * @returns express route handler that takes the following arguments:
20
- * params (map: param name => value), handleSuccess (function for handling
21
- * successful requests), handleError (function for handling failed requests),
22
- * req (express request object), redirect (a function that takes a url and
23
- * redirects the user to the url), and send (a function that sends a string
24
- * and optional http status code). Params also has userId, userFirstName,
21
+ * params (map: param name => value),
22
+ * req (express request object),
23
+ * next (express next function),
24
+ * send (a function that sends a string to the client),
25
+ * redirect (takes a url and redirects the user to that url),
26
+ * renderErrorPage (shows a static error page to the user),
27
+ * and returns the value to send to the client as a JSON API response, or
28
+ * calls next() or redirect(...) or send(...) or renderErrorPage(...).
29
+ * Note: params also has userId, userFirstName,
25
30
  * userLastName, isLearner, isTTM, isAdmin, and any other variables that
26
31
  * are directly added to the session
27
32
  */
@@ -39,6 +44,15 @@ const genRouteHandler = (
39
44
  next: () => void,
40
45
  redirect: (pathOrURL: string) => void,
41
46
  send: (text: string, status?: number) => void,
47
+ renderErrorPage: (
48
+ opts?: {
49
+ title?: string,
50
+ description?: string,
51
+ code?: string,
52
+ pageTitle?: string,
53
+ status?: number,
54
+ },
55
+ ) => void,
42
56
  },
43
57
  ) => any,
44
58
  },
@@ -247,7 +261,7 @@ const genRouteHandler = (
247
261
  {
248
262
  message: 'Your session has expired. Please refresh the page and try again.',
249
263
  code: ReactKitErrorCode.SessionExpired,
250
- status: 440,
264
+ status: 401,
251
265
  },
252
266
  );
253
267
  }
@@ -272,7 +286,7 @@ const genRouteHandler = (
272
286
  {
273
287
  message: 'Your session was invalid. Please refresh the page and try again.',
274
288
  code: ReactKitErrorCode.SessionExpired,
275
- status: 440,
289
+ status: 401,
276
290
  },
277
291
  );
278
292
  }
@@ -402,6 +416,31 @@ const genRouteHandler = (
402
416
  res.status(status).send(text);
403
417
  };
404
418
 
419
+ /**
420
+ * Render an error page
421
+ * @author Gabe Abrams
422
+ * @param opts object containing all arguments
423
+ * @param [opts.title=An Error Occurred] title of the error box
424
+ * @param [opts.description=An unknown server error occurred. Please contact support.]
425
+ * a human-readable description of the error
426
+ * @param [opts.code=ReactKitErrorCode.NoCode] error code to show
427
+ * @param [opts.pageTitle=opts.title] title of the page/tab if it differs from
428
+ * the title of the error
429
+ * @param [opts.status=500] http status code
430
+ */
431
+ const renderErrorPage = (
432
+ opts: {
433
+ title?: string,
434
+ description?: string,
435
+ code?: string,
436
+ pageTitle?: string,
437
+ status?: number,
438
+ } = {},
439
+ ) => {
440
+ const html = genErrorPage(opts);
441
+ send(html, opts.status ?? 500);
442
+ };
443
+
405
444
  // Call the handler
406
445
  try {
407
446
  const results = await opts.handler({
@@ -413,6 +452,7 @@ const genRouteHandler = (
413
452
  next();
414
453
  },
415
454
  redirect,
455
+ renderErrorPage,
416
456
  });
417
457
 
418
458
  // Send results to client (only if next wasn't called)
@@ -0,0 +1,141 @@
1
+ // Import shared types
2
+ import ReactKitErrorCode from '../types/ReactKitErrorCode';
3
+
4
+ /**
5
+ * Generate a static error page
6
+ * @author Gabe Abrams
7
+ * @param opts object containing all arguments
8
+ * @param [opts.title=An Error Occurred] title of the error box
9
+ * @param [opts.description=An unknown server error occurred. Please contact support.]
10
+ * a human-readable description of the error
11
+ * @param [opts.code=ReactKitErrorCode.NoCode] error code to show
12
+ * @param [opts.pageTitle=opts.title] title of the page/tab if it differs from
13
+ * the title of the error
14
+ * @returns html of the page
15
+ */
16
+ const genErrorPage = (
17
+ opts: {
18
+ title?: string,
19
+ description?: string,
20
+ code?: string,
21
+ pageTitle?: string,
22
+ } = {},
23
+ ): string => {
24
+ const title = (opts.title ?? 'An Error Occurred');
25
+ const pageTitle = (opts.pageTitle ?? title);
26
+ const description = (
27
+ opts.description
28
+ ?? 'An unknown server error occurred. Please contact support.'
29
+ );
30
+ const code = (opts.code ?? ReactKitErrorCode.NoCode);
31
+
32
+ return `
33
+ <head>
34
+ <!-- Metadata -->
35
+ <meta
36
+ name="viewport"
37
+ content="width=device-width, height=device-height, initial-scale=1.0, minimum-scale=1.0"
38
+ >
39
+
40
+ <!-- Title -->
41
+ <title>${pageTitle}</title>
42
+
43
+ <!-- Bootstrap -->
44
+ <link
45
+ rel="stylesheet"
46
+ href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.1/css/bootstrap.min.css"
47
+ integrity="sha512-siwe/oXMhSjGCwLn+scraPOWrJxHlUgMBMZXdPe2Tnk3I0x3ESCoLz7WZ5NTH6SZrywMY+PB1cjyqJ5jAluCOg=="
48
+ crossorigin="anonymous"
49
+ referrerpolicy="no-referrer"
50
+ />
51
+ <script
52
+ src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.1/js/bootstrap.min.js"
53
+ integrity="sha512-vyRAVI0IEm6LI/fVSv/Wq/d0KUfrg3hJq2Qz5FlfER69sf3ZHlOrsLriNm49FxnpUGmhx+TaJKwJ+ByTLKT+Yg=="
54
+ crossorigin="anonymous"
55
+ referrerpolicy="no-referrer"
56
+ ></script>
57
+
58
+ <!-- FontAwesome -->
59
+ <link
60
+ rel="stylesheet"
61
+ href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css"
62
+ integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A=="
63
+ crossorigin="anonymous"
64
+ referrerpolicy="no-referrer"
65
+ />
66
+
67
+ <!-- Style -->
68
+ <style>
69
+ .DCEReactKit-pop-in {
70
+ animation-name: DCEReactKit-pop-in;
71
+ animation-duration: 0.5s;
72
+ animation-iteration-count: 1;
73
+ animation-timing-function: ease-out;
74
+ animation-fill-mode: both;
75
+
76
+ transform-origin: center;
77
+ }
78
+
79
+ @keyframes DCEReactKit-pop-in {
80
+ 0% {
81
+ opacity: 0;
82
+ transform: scale(0.9);
83
+ }
84
+ 100% {
85
+ opacity: 1;
86
+ transform: scale(1);
87
+ }
88
+ }
89
+
90
+ .DCEReactKit-slide-in {
91
+ animation-name: DCEReactKit-slide-in;
92
+ animation-duration: 1s;
93
+ animation-iteration-count: 1;
94
+ animation-timing-function: ease-out;
95
+ animation-fill-mode: both;
96
+ animation-delay: 0.2s;
97
+ }
98
+
99
+ @keyframes DCEReactKit-slide-in {
100
+ 0% {
101
+ opacity: 0;
102
+ transform: translate(0, 0.3em);
103
+ }
104
+ 100% {
105
+ opacity: 1;
106
+ transform: translate(0, 0);
107
+ }
108
+ }
109
+ </style>
110
+ </head>
111
+
112
+ <!-- Body -->
113
+ <body class="bg-dark text-center pt-3 ps-3 pe-3">
114
+ <!-- Alert -->
115
+ <div
116
+ class="DCEReactKit-pop-in alert alert-warning d-inline-block"
117
+ style="width: 50em; max-width: 100%"
118
+ >
119
+ <!-- Title -->
120
+ <h2>
121
+ <i class="me-1 fa-solid fa-triangle-exclamation"></i>
122
+ ${title}
123
+ </h2>
124
+ <!-- Description -->
125
+ <div>
126
+ ${description}
127
+ </div>
128
+ </div>
129
+
130
+ <!-- Error Code -->
131
+ <div class="DCEReactKit-slide-in text-light">
132
+ <strong>
133
+ Error Code:
134
+ </strong>
135
+ ${code}
136
+ </div>
137
+ </body>
138
+ `;
139
+ };
140
+
141
+ export default genErrorPage;