2022-02-02 02:47:41 +08:00
|
|
|
const _ = require("lodash");
|
2022-01-30 05:03:02 +08:00
|
|
|
const joi = require("joi");
|
|
|
|
const MonkeyError = require("../handlers/error");
|
|
|
|
|
2022-02-02 02:47:41 +08:00
|
|
|
/**
|
|
|
|
* This utility checks that the server's configuration matches
|
|
|
|
* the criteria.
|
|
|
|
*/
|
|
|
|
function validateConfiguration(options) {
|
|
|
|
const { criteria, invalidMessage } = options;
|
|
|
|
|
|
|
|
return (req, res, next) => {
|
|
|
|
const configuration = req.context.configuration;
|
|
|
|
|
|
|
|
const validated = criteria(configuration);
|
|
|
|
if (!validated) {
|
|
|
|
throw new MonkeyError(
|
|
|
|
503,
|
|
|
|
invalidMessage ?? "This service is currently unavailable."
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
next();
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-01-31 07:53:56 +08:00
|
|
|
/**
|
|
|
|
* This utility serves as an alternative to wrapping express handlers with try/catch statements.
|
|
|
|
* Any routes that use an async handler function should wrap the handler with this function.
|
|
|
|
* Without this, any errors thrown will not be caught by the error handling middleware, and
|
|
|
|
* the app will hang!
|
|
|
|
*/
|
|
|
|
function asyncHandlerWrapper(handler) {
|
|
|
|
return async (req, res, next) => {
|
|
|
|
try {
|
|
|
|
const handlerData = await handler(req, res);
|
2022-02-02 02:47:41 +08:00
|
|
|
|
|
|
|
if (!res.headersSent) {
|
|
|
|
if (handlerData) {
|
|
|
|
res.json(handlerData);
|
|
|
|
} else {
|
|
|
|
res.sendStatus(204);
|
|
|
|
}
|
2022-01-31 07:53:56 +08:00
|
|
|
}
|
|
|
|
next();
|
|
|
|
} catch (error) {
|
|
|
|
next(error);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-01-30 05:03:02 +08:00
|
|
|
function requestValidation(validationSchema) {
|
2022-02-02 02:47:41 +08:00
|
|
|
/**
|
|
|
|
* In dev environments, as an alternative to token authentication,
|
|
|
|
* you can pass the authentication middleware by having a user id in the body.
|
|
|
|
* Inject the user id into the schema so that validation will not fail.
|
|
|
|
*/
|
|
|
|
if (process.env.MODE === "dev") {
|
|
|
|
validationSchema.body = {
|
|
|
|
uid: joi.any(),
|
|
|
|
...(validationSchema.body ?? {}),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
const { validationErrorMessage } = validationSchema;
|
|
|
|
const normalizedValidationSchema = _.omit(
|
|
|
|
validationSchema,
|
|
|
|
"validationErrorMessage"
|
|
|
|
);
|
2022-01-30 05:03:02 +08:00
|
|
|
|
2022-02-02 02:47:41 +08:00
|
|
|
return (req, res, next) => {
|
|
|
|
_.each(normalizedValidationSchema, (schema, key) => {
|
2022-01-30 05:03:02 +08:00
|
|
|
const joiSchema = joi.object().keys(schema);
|
2022-02-02 02:47:41 +08:00
|
|
|
|
2022-01-30 05:03:02 +08:00
|
|
|
const { error } = joiSchema.validate(req[key] ?? {});
|
|
|
|
if (error) {
|
|
|
|
const errorMessage = error.details[0].message;
|
2022-02-02 02:47:41 +08:00
|
|
|
throw new MonkeyError(
|
2022-02-04 04:59:02 +08:00
|
|
|
500,
|
2022-02-04 05:39:49 +08:00
|
|
|
validationErrorMessage ??
|
2022-02-04 05:42:46 +08:00
|
|
|
`Invalid request: ${errorMessage} (value ${error.details[0].context.value})`
|
2022-02-02 02:47:41 +08:00
|
|
|
);
|
2022-01-30 05:03:02 +08:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
next();
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = {
|
2022-02-02 02:47:41 +08:00
|
|
|
validateConfiguration,
|
2022-01-31 07:53:56 +08:00
|
|
|
asyncHandlerWrapper,
|
2022-01-30 05:03:02 +08:00
|
|
|
requestValidation,
|
|
|
|
};
|