dce-reactkit 3.1.14 → 3.1.15

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 CHANGED
@@ -2229,6 +2229,140 @@ const handleSuccess = (res, body) => {
2229
2229
  return undefined;
2230
2230
  };
2231
2231
 
2232
+ // Import shared types
2233
+ /**
2234
+ * Generate a static error page
2235
+ * @author Gabe Abrams
2236
+ * @param opts object containing all arguments
2237
+ * @param [opts.title=An Error Occurred] title of the error box
2238
+ * @param [opts.description=An unknown server error occurred. Please contact support.]
2239
+ * a human-readable description of the error
2240
+ * @param [opts.code=ReactKitErrorCode.NoCode] error code to show
2241
+ * @param [opts.pageTitle=opts.title] title of the page/tab if it differs from
2242
+ * the title of the error
2243
+ * @returns html of the page
2244
+ */
2245
+ const genErrorPage = (opts = {}) => {
2246
+ var _a, _b, _c, _d;
2247
+ const title = ((_a = opts.title) !== null && _a !== void 0 ? _a : 'An Error Occurred');
2248
+ const pageTitle = ((_b = opts.pageTitle) !== null && _b !== void 0 ? _b : title);
2249
+ const description = ((_c = opts.description) !== null && _c !== void 0 ? _c : 'An unknown server error occurred. Please contact support.');
2250
+ const code = ((_d = opts.code) !== null && _d !== void 0 ? _d : ReactKitErrorCode$1.NoCode);
2251
+ return `
2252
+ <head>
2253
+ <!-- Metadata -->
2254
+ <meta
2255
+ name="viewport"
2256
+ content="width=device-width, height=device-height, initial-scale=1.0, minimum-scale=1.0"
2257
+ >
2258
+
2259
+ <!-- Title -->
2260
+ <title>${pageTitle}</title>
2261
+
2262
+ <!-- Bootstrap -->
2263
+ <link
2264
+ rel="stylesheet"
2265
+ href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.1/css/bootstrap.min.css"
2266
+ integrity="sha512-siwe/oXMhSjGCwLn+scraPOWrJxHlUgMBMZXdPe2Tnk3I0x3ESCoLz7WZ5NTH6SZrywMY+PB1cjyqJ5jAluCOg=="
2267
+ crossorigin="anonymous"
2268
+ referrerpolicy="no-referrer"
2269
+ />
2270
+ <script
2271
+ src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.1/js/bootstrap.min.js"
2272
+ integrity="sha512-vyRAVI0IEm6LI/fVSv/Wq/d0KUfrg3hJq2Qz5FlfER69sf3ZHlOrsLriNm49FxnpUGmhx+TaJKwJ+ByTLKT+Yg=="
2273
+ crossorigin="anonymous"
2274
+ referrerpolicy="no-referrer"
2275
+ ></script>
2276
+
2277
+ <!-- FontAwesome -->
2278
+ <link
2279
+ rel="stylesheet"
2280
+ href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css"
2281
+ integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A=="
2282
+ crossorigin="anonymous"
2283
+ referrerpolicy="no-referrer"
2284
+ />
2285
+
2286
+ <!-- Style -->
2287
+ <style>
2288
+ .DCEReactKit-pop-in {
2289
+ animation-name: DCEReactKit-pop-in;
2290
+ animation-duration: 0.5s;
2291
+ animation-iteration-count: 1;
2292
+ animation-timing-function: ease-out;
2293
+ animation-fill-mode: both;
2294
+
2295
+ transform-origin: center;
2296
+ }
2297
+
2298
+ @keyframes DCEReactKit-pop-in {
2299
+ 0% {
2300
+ opacity: 0;
2301
+ transform: scale(0.9);
2302
+ }
2303
+ 100% {
2304
+ opacity: 1;
2305
+ transform: scale(1);
2306
+ }
2307
+ }
2308
+
2309
+ .DCEReactKit-slide-in {
2310
+ animation-name: DCEReactKit-slide-in;
2311
+ animation-duration: 1s;
2312
+ animation-iteration-count: 1;
2313
+ animation-timing-function: ease-out;
2314
+ animation-fill-mode: both;
2315
+ animation-delay: 0.2s;
2316
+ }
2317
+
2318
+ @keyframes DCEReactKit-slide-in {
2319
+ 0% {
2320
+ opacity: 0;
2321
+ transform: translate(0, 0.3em);
2322
+ }
2323
+ 100% {
2324
+ opacity: 1;
2325
+ transform: translate(0, 0);
2326
+ }
2327
+ }
2328
+ </style>
2329
+ </head>
2330
+
2331
+ <!-- Body -->
2332
+ <body class="bg-dark text-center pt-3 ps-3 pe-3">
2333
+ <!-- Alert -->
2334
+ <div
2335
+ class="DCEReactKit-pop-in alert alert-warning d-inline-block"
2336
+ style="width: 50em; max-width: 100%"
2337
+ >
2338
+ <!-- Title -->
2339
+ <h2>
2340
+ <i class="me-1 fa-solid fa-triangle-exclamation"></i>
2341
+ ${title}
2342
+ </h2>
2343
+ <!-- Description -->
2344
+ <div>
2345
+ ${description}
2346
+ </div>
2347
+ </div>
2348
+
2349
+ <!-- Error Code -->
2350
+ <div class="DCEReactKit-slide-in text-light">
2351
+ <strong>
2352
+ Error Code:
2353
+ </strong>
2354
+ ${code}
2355
+ </div>
2356
+ </body>
2357
+ `;
2358
+ };
2359
+
2360
+ /*------------------------------------------------------------------------*/
2361
+ /* Helpers */
2362
+ /*------------------------------------------------------------------------*/
2363
+ /*------------------------------------------------------------------------*/
2364
+ /* Main */
2365
+ /*------------------------------------------------------------------------*/
2232
2366
  /**
2233
2367
  * Generate an express API route handler
2234
2368
  * @author Gabe Abrams
@@ -2236,21 +2370,23 @@ const handleSuccess = (res, body) => {
2236
2370
  * @param opts.paramTypes map containing the types for each parameter that is
2237
2371
  * included in the request (map: param name => type)
2238
2372
  * @param opts.handler function that processes the request
2239
- * @param [opts.allowNotLoggedIn] if true, allow the user to not be logged
2240
- * in (the user will be allowed to not have launched via LTI)
2241
2373
  * @returns express route handler that takes the following arguments:
2242
- * params (map: param name => value), handleSuccess (function for handling
2243
- * successful requests), handleError (function for handling failed requests),
2244
- * req (express request object), redirect (a function that takes a url and
2245
- * redirects the user to the url), and send (a function that sends a string
2246
- * and optional http status code). Params also has userId, userFirstName,
2374
+ * params (map: param name => value),
2375
+ * req (express request object),
2376
+ * next (express next function),
2377
+ * send (a function that sends a string to the client),
2378
+ * redirect (takes a url and redirects the user to that url),
2379
+ * renderErrorPage (shows a static error page to the user),
2380
+ * and returns the value to send to the client as a JSON API response, or
2381
+ * calls next() or redirect(...) or send(...) or renderErrorPage(...).
2382
+ * Note: params also has userId, userFirstName,
2247
2383
  * userLastName, isLearner, isTTM, isAdmin, and any other variables that
2248
2384
  * are directly added to the session
2249
2385
  */
2250
2386
  const genRouteHandler = (opts) => {
2251
2387
  // Return a route handler
2252
2388
  return (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
2253
- var _a, _b, _c;
2389
+ var _a, _b;
2254
2390
  // Output params
2255
2391
  const output = {};
2256
2392
  /*----------------------------------------*/
@@ -2417,12 +2553,7 @@ const genRouteHandler = (opts) => {
2417
2553
  /*----------------------------------------*/
2418
2554
  // Get launch info
2419
2555
  const { launched, launchInfo } = cacclGetLaunchInfo(req);
2420
- if (
2421
- // Requiring user to be logged in
2422
- !opts.allowNotLoggedIn
2423
- // User is not logged in
2424
- && (!launched || !launchInfo)) {
2425
- // Throw an error
2556
+ if (!launched || !launchInfo) {
2426
2557
  return handleError(res, {
2427
2558
  message: 'Your session has expired. Please refresh the page and try again.',
2428
2559
  code: ReactKitErrorCode$1.SessionExpired,
@@ -2430,39 +2561,32 @@ const genRouteHandler = (opts) => {
2430
2561
  });
2431
2562
  }
2432
2563
  // Error if user info cannot be found
2433
- if (launched && launchInfo) {
2434
- if (!launchInfo.userId
2435
- || !launchInfo.userFirstName
2436
- || !launchInfo.userLastName
2437
- || (launchInfo.notInCourse
2438
- && !launchInfo.isAdmin)
2439
- || (!launchInfo.isTTM
2440
- && !launchInfo.isLearner
2441
- && !launchInfo.isAdmin)) {
2442
- return handleError(res, {
2443
- message: 'Your session was invalid. Please refresh the page and try again.',
2444
- code: ReactKitErrorCode$1.SessionExpired,
2445
- status: 440,
2446
- });
2447
- }
2448
- else {
2449
- // Add launch info to output
2450
- output.userId = launchInfo.userId;
2451
- output.userFirstName = launchInfo.userFirstName;
2452
- output.userLastName = launchInfo.userLastName;
2453
- output.userEmail = launchInfo.userEmail;
2454
- output.isLearner = !!launchInfo.isLearner;
2455
- output.isTTM = !!launchInfo.isTTM;
2456
- output.isAdmin = !!launchInfo.isAdmin;
2457
- output.courseId = ((_b = output.courseId) !== null && _b !== void 0 ? _b : launchInfo.courseId);
2458
- output.courseName = launchInfo.contextLabel;
2459
- }
2564
+ if (!launchInfo.userId
2565
+ || !launchInfo.userFirstName
2566
+ || !launchInfo.userLastName
2567
+ || (launchInfo.notInCourse
2568
+ && !launchInfo.isAdmin)
2569
+ || (!launchInfo.isTTM
2570
+ && !launchInfo.isLearner
2571
+ && !launchInfo.isAdmin)) {
2572
+ return handleError(res, {
2573
+ message: 'Your session was invalid. Please refresh the page and try again.',
2574
+ code: ReactKitErrorCode$1.SessionExpired,
2575
+ status: 440,
2576
+ });
2460
2577
  }
2461
- /*----------------------------------------*/
2462
- /* Session */
2463
- /*----------------------------------------*/
2578
+ // Add launch info to output
2579
+ output.userId = launchInfo.userId;
2580
+ output.userFirstName = launchInfo.userFirstName;
2581
+ output.userLastName = launchInfo.userLastName;
2582
+ output.userEmail = launchInfo.userEmail;
2583
+ output.isLearner = !!launchInfo.isLearner;
2584
+ output.isTTM = !!launchInfo.isTTM;
2585
+ output.isAdmin = !!launchInfo.isAdmin;
2586
+ output.courseId = ((_b = output.courseId) !== null && _b !== void 0 ? _b : launchInfo.courseId);
2587
+ output.courseName = launchInfo.contextLabel;
2464
2588
  // Add other session variables
2465
- Object.keys((_c = req.session) !== null && _c !== void 0 ? _c : {}).forEach((propName) => {
2589
+ Object.keys(req.session).forEach((propName) => {
2466
2590
  // Skip if prop already in output
2467
2591
  if (output[propName] !== undefined) {
2468
2592
  return;
@@ -2480,7 +2604,6 @@ const genRouteHandler = (opts) => {
2480
2604
  /*----------------------------------------*/
2481
2605
  // Make sure the user actually launched from the appropriate course
2482
2606
  if (output.courseId
2483
- && launchInfo
2484
2607
  && launchInfo.courseId
2485
2608
  && output.courseId !== launchInfo.courseId
2486
2609
  && !output.isTTM
@@ -2549,6 +2672,23 @@ const genRouteHandler = (opts) => {
2549
2672
  responseSent = true;
2550
2673
  res.status(status).send(text);
2551
2674
  };
2675
+ /**
2676
+ * Render an error page
2677
+ * @author Gabe Abrams
2678
+ * @param opts object containing all arguments
2679
+ * @param [opts.title=An Error Occurred] title of the error box
2680
+ * @param [opts.description=An unknown server error occurred. Please contact support.]
2681
+ * a human-readable description of the error
2682
+ * @param [opts.code=ReactKitErrorCode.NoCode] error code to show
2683
+ * @param [opts.pageTitle=opts.title] title of the page/tab if it differs from
2684
+ * the title of the error
2685
+ * @param [opts.status=500] http status code
2686
+ */
2687
+ const renderErrorPage = (opts = {}) => {
2688
+ var _a;
2689
+ const html = genErrorPage(opts);
2690
+ send(html, (_a = opts.status) !== null && _a !== void 0 ? _a : 500);
2691
+ };
2552
2692
  // Call the handler
2553
2693
  try {
2554
2694
  const results = yield opts.handler({
@@ -2560,6 +2700,7 @@ const genRouteHandler = (opts) => {
2560
2700
  next();
2561
2701
  },
2562
2702
  redirect,
2703
+ renderErrorPage,
2563
2704
  });
2564
2705
  // Send results to client (only if next wasn't called)
2565
2706
  if (!responseSent) {