dce-expresskit
Version:
Shared functions, helpers, and tools for Harvard DCE Express-based servers
706 lines • 43.4 kB
JavaScript
;
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
// Import dce-reactkit
var dce_reactkit_1 = require("dce-reactkit");
// Import caccl
var server_1 = require("caccl/server");
// Import caccl functions
var initExpressKitCollections_1 = require("./initExpressKitCollections");
// Import shared types
var ExpressKitErrorCode_1 = __importDefault(require("../types/ExpressKitErrorCode"));
// Import helpers
var handleError_1 = __importDefault(require("./handleError"));
var handleSuccess_1 = __importDefault(require("./handleSuccess"));
var genErrorPage_1 = __importDefault(require("../html/genErrorPage"));
var genInfoPage_1 = __importDefault(require("../html/genInfoPage"));
var parseUserAgent_1 = __importDefault(require("./parseUserAgent"));
var dataSigner_1 = require("./dataSigner");
/**
* Generate an express API route handler
* @author Gabe Abrams
* @param opts object containing all arguments
* @param opts.paramTypes map containing the types for each parameter that is
* included in the request (map: param name => type)
* @param opts.handler function that processes the request
* @param [opts.crossServerScope] the scope associated with this endpoint.
* If defined, this is a cross-server endpoint, which will never
* have any launch data, will never check Canvas roles or launch status, and will
* instead use scopes and reactkit credentials to sign and validate requests.
* Never start the path with /api/ttm or /api/admin if the endpoint is a cross-server
* endpoint because those roles will not be validated
* @param [opts.skipSessionCheck=true if crossServerScope defined] if true, skip
* the session check (allow users to not be logged in and launched via LTI).
* If crossServerScope is defined, this is always true
* @param [opts.unhandledErrorMessagePrefix] if included, when an error that
* is not of type ErrorWithCode is thrown, the client will receive an error
* where the error message is prefixed with this string. For example,
* if unhandledErrorMessagePrefix is
* 'While saving progress, we encountered an error:'
* and the error is 'progressInfo is not an object',
* the client will receive an error with the message
* 'While saving progress, we encountered an error: progressInfo is not an object'
* @returns express route handler that takes the following arguments:
* params (map: param name => value),
* req (express request object),
* next (express next function),
* send (a function that sends a string to the client),
* redirect (takes a url and redirects the user to that url),
* renderErrorPage (shows a static error page to the user),
* renderInfoPage (shows a static info page to the user),
* renderCustomHTML (renders custom html and sends it to the user),
* and returns the value to send to the client as a JSON API response, or
* calls next() or redirect(...) or send(...) or renderErrorPage(...).
* Note: params also has userId, userFirstName,
* userLastName, userEmail, userAvatarURL, isLearner, isTTM, isAdmin,
* and any other variables that
* are directly added to the session, if the user does have a session.
*/
var genRouteHandler = function (opts) {
// Return a route handler
return function (req, res, next) { return __awaiter(void 0, void 0, void 0, function () {
var output, crossServerScope, skipSessionCheck, requestBody, paramsToSign, err_1, paramList, i, _a, name_1, type, value, simpleVal, _b, launched, launchInfo, selectAdminCollection, id, match, logServerEvent, responseSent, redirect, send, renderErrorPage, renderInfoPage, renderCustomHTML, results, err_2;
var _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u;
return __generator(this, function (_v) {
switch (_v.label) {
case 0:
output = {};
crossServerScope = null;
if (opts.crossServerScope) {
crossServerScope = (_c = opts.crossServerScope) !== null && _c !== void 0 ? _c : null;
}
skipSessionCheck = !!(opts.skipSessionCheck
|| crossServerScope);
requestBody = __assign(__assign(__assign({}, req.body), req.query), req.params);
if (!crossServerScope) return [3 /*break*/, 4];
_v.label = 1;
case 1:
_v.trys.push([1, 3, , 4]);
paramsToSign = __assign(__assign({}, req.body), req.query);
// Validate the request body
return [4 /*yield*/, (0, dataSigner_1.validateSignedRequest)({
method: (_d = req.method) !== null && _d !== void 0 ? _d : 'GET',
path: req.path,
scope: crossServerScope,
params: paramsToSign,
})];
case 2:
// Validate the request body
_v.sent();
// Valid! Remove oauth values because they're no longer needed, and shouldn't be passed to the handler
Object.keys(requestBody).forEach(function (key) {
if (key.startsWith('oauth_')) {
delete requestBody[key];
}
});
return [3 /*break*/, 4];
case 3:
err_1 = _v.sent();
return [2 /*return*/, (0, handleError_1.default)(res, {
message: "The authenticity of a cross-server request could not be validated because an error occurred: ".concat((_e = err_1.message) !== null && _e !== void 0 ? _e : 'unknown error'),
code: ((_f = err_1.code) !== null && _f !== void 0 ? _f : ExpressKitErrorCode_1.default.UnknownCrossServerError),
status: 401,
})];
case 4:
paramList = Object.entries((_g = opts.paramTypes) !== null && _g !== void 0 ? _g : {});
for (i = 0; i < paramList.length; i++) {
_a = paramList[i], name_1 = _a[0], type = _a[1];
value = requestBody[name_1];
// Parse
if (type === dce_reactkit_1.ParamType.Boolean || type === dce_reactkit_1.ParamType.BooleanOptional) {
// Boolean
// Handle case where value doesn't exist
if (value === undefined) {
if (type === dce_reactkit_1.ParamType.BooleanOptional) {
output[name_1] = undefined;
}
else {
return [2 /*return*/, (0, handleError_1.default)(res, {
message: "Parameter ".concat(name_1, " is required, but it was not included."),
code: ExpressKitErrorCode_1.default.MissingParameter,
status: 422,
})];
}
}
else {
simpleVal = (String(value)
.trim()
.toLowerCase());
// Parse
output[name_1] = ([
'true',
'yes',
'y',
'1',
't',
].indexOf(simpleVal) >= 0);
}
}
else if (type === dce_reactkit_1.ParamType.Float || type === dce_reactkit_1.ParamType.FloatOptional) {
// Float
// Handle case where value doesn't exist
if (value === undefined) {
if (type === dce_reactkit_1.ParamType.FloatOptional) {
output[name_1] = undefined;
}
else {
return [2 /*return*/, (0, handleError_1.default)(res, {
message: "Parameter ".concat(name_1, " is required, but it was not included."),
code: ExpressKitErrorCode_1.default.MissingParameter,
status: 422,
})];
}
}
else if (!Number.isNaN(Number.parseFloat(String(value)))) {
// Value is a number
output[name_1] = Number.parseFloat(String(value));
}
else {
// Issue!
return [2 /*return*/, (0, handleError_1.default)(res, {
message: "Request data was malformed: ".concat(name_1, " was not a valid float."),
code: ExpressKitErrorCode_1.default.InvalidParameter,
status: 422,
})];
}
}
else if (type === dce_reactkit_1.ParamType.Int || type === dce_reactkit_1.ParamType.IntOptional) {
// Int
// Handle case where value doesn't exist
if (value === undefined) {
if (type === dce_reactkit_1.ParamType.IntOptional) {
output[name_1] = undefined;
}
else {
return [2 /*return*/, (0, handleError_1.default)(res, {
message: "Parameter ".concat(name_1, " is required, but it was not included."),
code: ExpressKitErrorCode_1.default.MissingParameter,
status: 422,
})];
}
}
else if (!Number.isNaN(Number.parseInt(String(value), 10))) {
// Value is a number
output[name_1] = Number.parseInt(String(value), 10);
}
else {
// Issue!
return [2 /*return*/, (0, handleError_1.default)(res, {
message: "Request data was malformed: ".concat(name_1, " was not a valid int."),
code: ExpressKitErrorCode_1.default.InvalidParameter,
status: 422,
})];
}
}
else if (type === dce_reactkit_1.ParamType.JSON || type === dce_reactkit_1.ParamType.JSONOptional) {
// Stringified JSON
// Handle case where value doesn't exist
if (value === undefined) {
if (type === dce_reactkit_1.ParamType.JSONOptional) {
output[name_1] = undefined;
}
else {
return [2 /*return*/, (0, handleError_1.default)(res, {
message: "Parameter ".concat(name_1, " is required, but it was not included."),
code: ExpressKitErrorCode_1.default.MissingParameter,
status: 422,
})];
}
}
else {
// Value exists
// Parse
try {
output[name_1] = JSON.parse(String(value));
}
catch (err) {
return [2 /*return*/, (0, handleError_1.default)(res, {
message: "Request data was malformed: ".concat(name_1, " was not a valid JSON payload."),
code: ExpressKitErrorCode_1.default.InvalidParameter,
status: 422,
})];
}
}
}
else if (type === dce_reactkit_1.ParamType.String || type === dce_reactkit_1.ParamType.StringOptional) {
// String
// Handle case where value doesn't exist
if (value === undefined) {
if (type === dce_reactkit_1.ParamType.StringOptional) {
output[name_1] = undefined;
}
else {
return [2 /*return*/, (0, handleError_1.default)(res, {
message: "Parameter ".concat(name_1, " is required, but it was not included."),
code: ExpressKitErrorCode_1.default.MissingParameter,
status: 422,
})];
}
}
else {
// Value exists
// Leave as is
output[name_1] = value;
}
}
else {
// No valid data type
return [2 /*return*/, (0, handleError_1.default)(res, {
message: "An internal error occurred: we could not determine the type of ".concat(name_1, "."),
code: ExpressKitErrorCode_1.default.InvalidParameter,
status: 422,
})];
}
}
_b = (0, server_1.getLaunchInfo)(req), launched = _b.launched, launchInfo = _b.launchInfo;
if (
// Not launched
(!launched || !launchInfo)
// Not skipping the session check
&& !skipSessionCheck) {
return [2 /*return*/, (0, handleError_1.default)(res, {
message: 'Your session has expired. Please refresh the page and try again.',
code: dce_reactkit_1.ReactKitErrorCode.SessionExpired,
status: 401,
})];
}
// Make sure students don't try to get/change data for other students
if (
// launchInfo is defined
launchInfo
// launched is true
&& launched
// user is a student
&& launchInfo.isLearner
// output.userId is defined
&& output.userId
// the launchInfo userId is the "input" userId, check if the request
// output userId differs from the launchInfo userId
&& (launchInfo.userId !== output.userId)) {
return [2 /*return*/, (0, handleError_1.default)(res, {
message: 'We encountered a student ID mismatch. Please refresh or try the action again. Contact support if this issue persists.',
code: ExpressKitErrorCode_1.default.StudentIdMismatch,
status: 401,
})];
}
// Error if user info cannot be found
if (
// User information is incomplete
(!launchInfo
|| !launchInfo.userId
|| !launchInfo.userFirstName
|| !launchInfo.userLastName
|| (launchInfo.notInCourse
&& !launchInfo.isAdmin)
|| (!launchInfo.isTTM
&& !launchInfo.isLearner
&& !launchInfo.isAdmin))
// Not skipping the session check
&& !skipSessionCheck) {
return [2 /*return*/, (0, handleError_1.default)(res, {
message: 'Your session was invalid. Please refresh the page and try again.',
code: dce_reactkit_1.ReactKitErrorCode.SessionExpired,
status: 401,
})];
}
// Add launch info to output
output.userId = (launchInfo
? launchInfo.userId
: ((_h = output.userId) !== null && _h !== void 0 ? _h : undefined));
output.userFirstName = (launchInfo
? launchInfo.userFirstName
: ((_j = output.userFirstName) !== null && _j !== void 0 ? _j : undefined));
output.userLastName = (launchInfo
? launchInfo.userLastName
: ((_k = output.userLastName) !== null && _k !== void 0 ? _k : undefined));
output.userEmail = (launchInfo
? launchInfo.userEmail
: ((_l = output.userEmail) !== null && _l !== void 0 ? _l : undefined));
output.userAvatarURL = (launchInfo
? ((_m = launchInfo.userImage) !== null && _m !== void 0 ? _m : 'http://www.gravatar.com/avatar/?d=identicon')
: ((_o = output.userAvatarURL) !== null && _o !== void 0 ? _o : undefined));
output.isLearner = (launchInfo
? !!launchInfo.isLearner
: ((_p = output.isLearner) !== null && _p !== void 0 ? _p : undefined));
output.isTTM = (launchInfo
? !!launchInfo.isTTM
: ((_q = output.isTTM) !== null && _q !== void 0 ? _q : undefined));
output.isAdmin = (launchInfo
? !!launchInfo.isAdmin
: ((_r = output.isAdmin) !== null && _r !== void 0 ? _r : undefined));
output.courseId = (launchInfo
? ((_s = output.courseId) !== null && _s !== void 0 ? _s : launchInfo.courseId)
: ((_t = output.courseId) !== null && _t !== void 0 ? _t : undefined));
output.courseName = (launchInfo
? launchInfo.contextLabel
: ((_u = output.courseName) !== null && _u !== void 0 ? _u : undefined));
// Add other session variables
Object.keys(req.session).forEach(function (propName) {
// Skip if prop already in output
if (output[propName] !== undefined) {
return;
}
// Add to output
var value = req.session[propName];
if (typeof value === 'string'
|| typeof value === 'boolean'
|| typeof value === 'number') {
output[propName] = value;
}
});
/*----------------------------------------*/
/* ----- Require Course Consistency ----- */
/*----------------------------------------*/
// Make sure the user actually launched from the appropriate course
if (output.courseId
&& launchInfo
&& launchInfo.courseId
&& output.courseId !== launchInfo.courseId
&& !output.isTTM
&& !output.isAdmin) {
// Course of interest is not the launch course
return [2 /*return*/, (0, handleError_1.default)(res, {
message: 'You switched sessions by opening this app in another tab. Please refresh the page and try again.',
code: ExpressKitErrorCode_1.default.WrongCourse,
status: 401,
})];
}
/*----------------------------------------*/
/* Require Proper Permissions */
/*----------------------------------------*/
// Add TTM endpoint security
if (
// This is a TTM endpoint
req.path.startsWith('/api/ttm')
// User is not a TTM
&& (
// User is not a TTM
!output.isTTM
// User is not an admin
&& !output.isAdmin)) {
// User does not have access
return [2 /*return*/, (0, handleError_1.default)(res, {
message: 'This action is only allowed if you are a teaching team member for the course. Please go back to Canvas, log in as a teaching team member, and try again.',
code: ExpressKitErrorCode_1.default.NotTTM,
status: 401,
})];
}
// Add Admin endpoint security
if (
// This is an admin endpoint
req.path.startsWith('/api/admin')
// User is not an admin
&& !output.isAdmin) {
// User does not have access
return [2 /*return*/, (0, handleError_1.default)(res, {
message: 'This action is only allowed if you are a Canvas admin. Please go back to Canvas, log in as an admin, and try again.',
code: ExpressKitErrorCode_1.default.NotAdmin,
status: 401,
})];
}
if (!
// This is a select admin endpoint
req.path.startsWith('/api/admin/select'))
// This is a select admin endpoint
return [3 /*break*/, 7];
return [4 /*yield*/, (0, initExpressKitCollections_1.internalGetSelectAdminCollection)()];
case 5:
selectAdminCollection = _v.sent();
id = output.userId;
return [4 /*yield*/, selectAdminCollection.find({ id: id })];
case 6:
match = (_v.sent())[0];
// Check that user exists in select admin collection
if (!match) {
// User does not have access
return [2 /*return*/, (0, handleError_1.default)(res, {
message: 'This action is only allowed for select Canvas admins. Please go back to Canvas, log in as a select admin, and try again.',
code: ExpressKitErrorCode_1.default.NotSelectAdmin,
status: 401,
})];
}
_v.label = 7;
case 7:
logServerEvent = function (logOpts) { return __awaiter(void 0, void 0, void 0, function () {
var _a, browser, device, _b, timestamp, year, month, day, hour, minute, mainLogInfo, typeSpecificInfo, sourceSpecificInfo, log, logCollection, err_3, dummyMainInfo, dummyTypeSpecificInfo, dummySourceSpecificInfo, log;
var _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
return __generator(this, function (_r) {
switch (_r.label) {
case 0:
_r.trys.push([0, 5, , 6]);
_a = (0, parseUserAgent_1.default)(req.headers['user-agent']), browser = _a.browser, device = _a.device;
_b = (0, dce_reactkit_1.getTimeInfoInET)(), timestamp = _b.timestamp, year = _b.year, month = _b.month, day = _b.day, hour = _b.hour, minute = _b.minute;
mainLogInfo = {
id: "".concat(launchInfo ? launchInfo.userId : 'unknown', "-").concat(Date.now(), "-").concat(Math.floor(Math.random() * 100000), "-").concat(Math.floor(Math.random() * 100000)),
userFirstName: (launchInfo ? launchInfo.userFirstName : 'unknown'),
userLastName: (launchInfo ? launchInfo.userLastName : 'unknown'),
userEmail: (launchInfo ? launchInfo.userEmail : 'unknown'),
userId: (launchInfo ? launchInfo.userId : -1),
isLearner: (launchInfo && !!launchInfo.isLearner),
isAdmin: (launchInfo && !!launchInfo.isAdmin),
isTTM: (launchInfo && !!launchInfo.isTTM),
courseId: (launchInfo ? launchInfo.courseId : -1),
courseName: (launchInfo ? launchInfo.contextLabel : 'unknown'),
browser: browser,
device: device,
year: year,
month: month,
day: day,
hour: hour,
minute: minute,
timestamp: timestamp,
context: (typeof logOpts.context === 'string'
? logOpts.context
: ((_d = ((_c = logOpts.context) !== null && _c !== void 0 ? _c : {})._) !== null && _d !== void 0 ? _d : dce_reactkit_1.LogBuiltInMetadata.Context.Uncategorized)),
subcontext: ((_e = logOpts.subcontext) !== null && _e !== void 0 ? _e : dce_reactkit_1.LogBuiltInMetadata.Context.Uncategorized),
tags: ((_f = logOpts.tags) !== null && _f !== void 0 ? _f : []),
level: ((_g = logOpts.level) !== null && _g !== void 0 ? _g : dce_reactkit_1.LogLevel.Info),
metadata: ((_h = logOpts.metadata) !== null && _h !== void 0 ? _h : {}),
};
typeSpecificInfo = (('error' in logOpts && logOpts.error)
? {
type: dce_reactkit_1.LogType.Error,
errorMessage: (_j = logOpts.error.message) !== null && _j !== void 0 ? _j : 'Unknown message',
errorCode: (_k = logOpts.error.code) !== null && _k !== void 0 ? _k : dce_reactkit_1.ReactKitErrorCode.NoCode,
errorStack: (_l = logOpts.error.stack) !== null && _l !== void 0 ? _l : 'No stack',
}
: {
type: dce_reactkit_1.LogType.Action,
target: ((_m = logOpts.target) !== null && _m !== void 0 ? _m : dce_reactkit_1.LogBuiltInMetadata.Target.NoTarget),
action: ((_o = logOpts.action) !== null && _o !== void 0 ? _o : dce_reactkit_1.LogAction.Unknown),
});
sourceSpecificInfo = (logOpts.overrideAsClientEvent
? {
source: dce_reactkit_1.LogSource.Client,
}
: {
source: dce_reactkit_1.LogSource.Server,
routePath: req.path,
routeTemplate: req.route.path,
});
log = __assign(__assign(__assign({}, mainLogInfo), typeSpecificInfo), sourceSpecificInfo);
return [4 /*yield*/, (0, initExpressKitCollections_1.internalGetLogCollection)()];
case 1:
logCollection = _r.sent();
if (!logCollection) return [3 /*break*/, 3];
// Store to the log collection
return [4 /*yield*/, logCollection.insert(log)];
case 2:
// Store to the log collection
_r.sent();
return [3 /*break*/, 4];
case 3:
if (log.type === dce_reactkit_1.LogType.Error) {
// Print to console
// eslint-disable-next-line no-console
console.error('dce-reactkit error log:', log);
}
else {
// eslint-disable-next-line no-console
console.log('dce-reactkit action log:', log);
}
_r.label = 4;
case 4:
// Return log entry
return [2 /*return*/, log];
case 5:
err_3 = _r.sent();
// Print because we cannot store the error
// eslint-disable-next-line no-console
console.error('Could not log the following:', logOpts, 'due to this error:', ((_p = err_3) !== null && _p !== void 0 ? _p : {}).message, ((_q = err_3) !== null && _q !== void 0 ? _q : {}).stack);
dummyMainInfo = {
id: '-1',
userFirstName: 'Unknown',
userLastName: 'Unknown',
userEmail: 'unknown@harvard.edu',
userId: 1,
isLearner: false,
isAdmin: false,
isTTM: false,
courseId: 1,
courseName: 'Unknown',
browser: {
name: 'Unknown',
version: 'Unknown',
},
device: {
isMobile: false,
os: 'Unknown',
},
year: 1,
month: 1,
day: 1,
hour: 1,
minute: 1,
timestamp: Date.now(),
tags: [],
level: dce_reactkit_1.LogLevel.Warn,
metadata: {},
context: dce_reactkit_1.LogBuiltInMetadata.Context.Uncategorized,
subcontext: dce_reactkit_1.LogBuiltInMetadata.Context.Uncategorized,
};
dummyTypeSpecificInfo = {
type: dce_reactkit_1.LogType.Error,
errorMessage: 'Unknown',
errorCode: 'Unknown',
errorStack: 'No Stack',
};
dummySourceSpecificInfo = {
source: dce_reactkit_1.LogSource.Server,
routePath: req.path,
routeTemplate: req.route.path,
};
log = __assign(__assign(__assign({}, dummyMainInfo), dummyTypeSpecificInfo), dummySourceSpecificInfo);
return [2 /*return*/, log];
case 6: return [2 /*return*/];
}
});
}); };
responseSent = false;
redirect = function (pathOrURL) {
responseSent = true;
res.redirect(pathOrURL);
};
send = function (text, status) {
if (status === void 0) { status = 200; }
responseSent = true;
res.status(status).send(text);
};
renderErrorPage = function (renderOpts) {
var _a, _b, _c;
if (renderOpts === void 0) { renderOpts = {}; }
var html = (0, genErrorPage_1.default)(renderOpts);
send(html, (_a = renderOpts.status) !== null && _a !== void 0 ? _a : 500);
// Log server-side error if not a session expired error or 404
if (renderOpts.status && renderOpts.status === 404) {
return;
}
if ((_b = renderOpts.title) === null || _b === void 0 ? void 0 : _b.toLowerCase().includes('session expired')) {
return;
}
logServerEvent({
context: dce_reactkit_1.LogBuiltInMetadata.Context.ServerRenderedErrorPage,
error: {
message: "".concat(renderOpts.title, ": ").concat(renderOpts.description),
code: renderOpts.code,
},
metadata: {
title: renderOpts.title,
description: renderOpts.description,
code: renderOpts.code,
pageTitle: renderOpts.pageTitle,
status: (_c = renderOpts.status) !== null && _c !== void 0 ? _c : 500,
},
});
};
renderInfoPage = function (renderOpts) {
var html = (0, genInfoPage_1.default)(renderOpts);
send(html, 200);
};
renderCustomHTML = function (htmlOpts) {
var _a;
send(htmlOpts.html, (_a = htmlOpts.status) !== null && _a !== void 0 ? _a : 200);
};
_v.label = 8;
case 8:
_v.trys.push([8, 10, , 11]);
return [4 /*yield*/, opts.handler({
params: output,
req: req,
send: send,
next: function () {
responseSent = true;
next();
},
redirect: redirect,
renderErrorPage: renderErrorPage,
renderInfoPage: renderInfoPage,
renderCustomHTML: renderCustomHTML,
logServerEvent: logServerEvent,
})];
case 9:
results = _v.sent();
// Send results to client (only if next wasn't called)
if (!responseSent) {
return [2 /*return*/, (0, handleSuccess_1.default)(res, results !== null && results !== void 0 ? results : undefined)];
}
return [3 /*break*/, 11];
case 10:
err_2 = _v.sent();
// Prefix error message if needed
if (opts.unhandledErrorMessagePrefix
&& err_2 instanceof Error
&& err_2.message
&& err_2.name !== 'ErrorWithCode') {
err_2.message = "".concat(opts.unhandledErrorMessagePrefix.trim(), " ").concat(err_2.message.trim());
}
// Send error to client (only if next wasn't called)
if (!responseSent) {
(0, handleError_1.default)(res, err_2);
// Log server-side error
logServerEvent({
context: dce_reactkit_1.LogBuiltInMetadata.Context.ServerEndpointError,
error: err_2,
});
return [2 /*return*/];
}
// Log error that was not responded with
// eslint-disable-next-line no-console
console.log('Error occurred but could not be sent to client because a response was already sent:', err_2);
return [3 /*break*/, 11];
case 11: return [2 /*return*/];
}
});
}); };
};
exports.default = genRouteHandler;
//# sourceMappingURL=genRouteHandler.js.map