|
@@ -1,5 +1,9 @@
|
|
|
-import { markdownSectionBlock, InvalidGrowiCommandError } from '@growi/slack';
|
|
|
|
|
|
|
+import {
|
|
|
|
|
+ markdownSectionBlock, InvalidGrowiCommandError, generateRespondUtil, supportedGrowiCommands,
|
|
|
|
|
+} from '@growi/slack';
|
|
|
|
|
+import createError from 'http-errors';
|
|
|
import loggerFactory from '~/utils/logger';
|
|
import loggerFactory from '~/utils/logger';
|
|
|
|
|
+import { SlackCommandHandlerError } from '~/server/models/vo/slack-command-handler-error';
|
|
|
|
|
|
|
|
const express = require('express');
|
|
const express = require('express');
|
|
|
const mongoose = require('mongoose');
|
|
const mongoose = require('mongoose');
|
|
@@ -27,7 +31,7 @@ module.exports = (crowi) => {
|
|
|
if (tokenPtoG == null) {
|
|
if (tokenPtoG == null) {
|
|
|
const message = 'The value of header \'x-growi-ptog-tokens\' must not be empty.';
|
|
const message = 'The value of header \'x-growi-ptog-tokens\' must not be empty.';
|
|
|
logger.warn(message, { body: req.body });
|
|
logger.warn(message, { body: req.body });
|
|
|
- return res.status(400).send({ message });
|
|
|
|
|
|
|
+ return next(createError(400, message));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const SlackAppIntegrationCount = await SlackAppIntegration.countDocuments({ tokenPtoG });
|
|
const SlackAppIntegrationCount = await SlackAppIntegration.countDocuments({ tokenPtoG });
|
|
@@ -57,12 +61,37 @@ module.exports = (crowi) => {
|
|
|
return { permissionsForBroadcastUseCommands, permissionsForSingleUseCommands };
|
|
return { permissionsForBroadcastUseCommands, permissionsForSingleUseCommands };
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // REFACTORIMG THIS MIDDLEWARE GW-7441
|
|
|
|
|
|
|
+ // TODO: move this middleware to each controller
|
|
|
|
|
+ // no res.send() is allowed after this middleware
|
|
|
async function checkCommandsPermission(req, res, next) {
|
|
async function checkCommandsPermission(req, res, next) {
|
|
|
- let { growiCommand } = req.body;
|
|
|
|
|
|
|
+ // Send response immediately to avoid opelation_timeout error
|
|
|
|
|
+ // See https://api.slack.com/apis/connections/events-api#the-events-api__responding-to-events
|
|
|
|
|
+ // for without proxy
|
|
|
|
|
+ res.send();
|
|
|
|
|
+
|
|
|
|
|
+ let growiCommand;
|
|
|
|
|
+ try {
|
|
|
|
|
+ growiCommand = getGrowiCommand(req.body);
|
|
|
|
|
+ }
|
|
|
|
|
+ catch (err) {
|
|
|
|
|
+ logger.error(err.message);
|
|
|
|
|
+ return next(err);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // when /relation-test or from proxy
|
|
|
|
|
- if (req.body.text == null && growiCommand == null) return next();
|
|
|
|
|
|
|
+ // not supported commands
|
|
|
|
|
+ if (!supportedGrowiCommands.includes(growiCommand.growiCommandType)) {
|
|
|
|
|
+ const options = {
|
|
|
|
|
+ respondBody: {
|
|
|
|
|
+ text: 'Command is not supported',
|
|
|
|
|
+ blocks: [
|
|
|
|
|
+ markdownSectionBlock('*Command is not supported*'),
|
|
|
|
|
+ // eslint-disable-next-line max-len
|
|
|
|
|
+ markdownSectionBlock(`\`/growi ${growiCommand.growiCommandType}\` command is not supported in this version of GROWI bot. Run \`/growi help\` to see all supported commands.`),
|
|
|
|
|
+ ],
|
|
|
|
|
+ },
|
|
|
|
|
+ };
|
|
|
|
|
+ return next(new SlackCommandHandlerError('Command type is not specified', options));
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
const tokenPtoG = req.headers['x-growi-ptog-tokens'];
|
|
const tokenPtoG = req.headers['x-growi-ptog-tokens'];
|
|
|
const extractPermissions = await extractPermissionsCommands(tokenPtoG);
|
|
const extractPermissions = await extractPermissionsCommands(tokenPtoG);
|
|
@@ -75,11 +104,11 @@ module.exports = (crowi) => {
|
|
|
commandPermission = Object.fromEntries([...permissionsForBroadcastUseCommands, ...permissionsForSingleUseCommands]);
|
|
commandPermission = Object.fromEntries([...permissionsForBroadcastUseCommands, ...permissionsForSingleUseCommands]);
|
|
|
const isPermitted = checkPermission(commandPermission, growiCommand.growiCommandType, fromChannel);
|
|
const isPermitted = checkPermission(commandPermission, growiCommand.growiCommandType, fromChannel);
|
|
|
if (isPermitted) return next();
|
|
if (isPermitted) return next();
|
|
|
- return res.status(403).send(`It is not allowed to send \`/growi ${growiCommand.growiCommandType}\` command to this GROWI: ${siteUrl}`);
|
|
|
|
|
|
|
+
|
|
|
|
|
+ return next(createError(403, `It is not allowed to send \`/growi ${growiCommand.growiCommandType}\` command to this GROWI: ${siteUrl}`));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// without proxy
|
|
// without proxy
|
|
|
- growiCommand = parseSlashCommand(req.body);
|
|
|
|
|
commandPermission = JSON.parse(configManager.getConfig('crowi', 'slackbot:withoutProxy:commandPermission'));
|
|
commandPermission = JSON.parse(configManager.getConfig('crowi', 'slackbot:withoutProxy:commandPermission'));
|
|
|
|
|
|
|
|
const isPermitted = checkPermission(commandPermission, growiCommand.growiCommandType, fromChannel);
|
|
const isPermitted = checkPermission(commandPermission, growiCommand.growiCommandType, fromChannel);
|
|
@@ -87,17 +116,26 @@ module.exports = (crowi) => {
|
|
|
return next();
|
|
return next();
|
|
|
}
|
|
}
|
|
|
// show ephemeral error message if not permitted
|
|
// show ephemeral error message if not permitted
|
|
|
- res.json({
|
|
|
|
|
- response_type: 'ephemeral',
|
|
|
|
|
- text: 'Command forbidden',
|
|
|
|
|
- blocks: [
|
|
|
|
|
- markdownSectionBlock(`It is not allowed to send \`/growi ${growiCommand.growiCommandType}\` command to this GROWI: ${siteUrl}`),
|
|
|
|
|
- ],
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ const options = {
|
|
|
|
|
+ respondBody: {
|
|
|
|
|
+ text: 'Command forbidden',
|
|
|
|
|
+ blocks: [
|
|
|
|
|
+ markdownSectionBlock('*Command is not supported*'),
|
|
|
|
|
+ markdownSectionBlock(`It is not allowed to send \`/growi ${growiCommand.growiCommandType}\` command to this GROWI: ${siteUrl}`),
|
|
|
|
|
+ ],
|
|
|
|
|
+ },
|
|
|
|
|
+ };
|
|
|
|
|
+ return next(new SlackCommandHandlerError('Command type is not specified', options));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // REFACTORIMG THIS MIDDLEWARE GW-7441
|
|
|
|
|
|
|
+ // TODO: move this middleware to each controller
|
|
|
|
|
+ // no res.send() is allowed after this middleware
|
|
|
async function checkInteractionsPermission(req, res, next) {
|
|
async function checkInteractionsPermission(req, res, next) {
|
|
|
|
|
+ // Send response immediately to avoid opelation_timeout error
|
|
|
|
|
+ // See https://api.slack.com/apis/connections/events-api#the-events-api__responding-to-events
|
|
|
|
|
+ // for without proxy
|
|
|
|
|
+ res.send();
|
|
|
|
|
+
|
|
|
const { interactionPayload, interactionPayloadAccessor } = req;
|
|
const { interactionPayload, interactionPayloadAccessor } = req;
|
|
|
const siteUrl = crowi.appService.getSiteUrl();
|
|
const siteUrl = crowi.appService.getSiteUrl();
|
|
|
|
|
|
|
@@ -114,7 +152,7 @@ module.exports = (crowi) => {
|
|
|
const isPermitted = checkPermission(commandPermission, callbacIdkOrActionId, fromChannel);
|
|
const isPermitted = checkPermission(commandPermission, callbacIdkOrActionId, fromChannel);
|
|
|
if (isPermitted) return next();
|
|
if (isPermitted) return next();
|
|
|
|
|
|
|
|
- return res.status(403).send(`This interaction is forbidden on this GROWI: ${siteUrl}`);
|
|
|
|
|
|
|
+ return next(createError(403, `This interaction is forbidden on this GROWI: ${siteUrl}`));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// without proxy
|
|
// without proxy
|
|
@@ -125,13 +163,16 @@ module.exports = (crowi) => {
|
|
|
return next();
|
|
return next();
|
|
|
}
|
|
}
|
|
|
// show ephemeral error message if not permitted
|
|
// show ephemeral error message if not permitted
|
|
|
- res.json({
|
|
|
|
|
- response_type: 'ephemeral',
|
|
|
|
|
- text: 'Interaction forbidden',
|
|
|
|
|
- blocks: [
|
|
|
|
|
- markdownSectionBlock(`This interaction is forbidden on this GROWI: ${siteUrl}`),
|
|
|
|
|
- ],
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ const options = {
|
|
|
|
|
+ respondBody: {
|
|
|
|
|
+ text: 'Interaction forbidden',
|
|
|
|
|
+ blocks: [
|
|
|
|
|
+ markdownSectionBlock('*Interaction forbidden*'),
|
|
|
|
|
+ markdownSectionBlock(`This interaction is forbidden on this GROWI: ${siteUrl}`),
|
|
|
|
|
+ ],
|
|
|
|
|
+ },
|
|
|
|
|
+ };
|
|
|
|
|
+ return next(new SlackCommandHandlerError('Interaction forbidden', options));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const addSigningSecretToReq = (req, res, next) => {
|
|
const addSigningSecretToReq = (req, res, next) => {
|
|
@@ -150,103 +191,159 @@ module.exports = (crowi) => {
|
|
|
return next();
|
|
return next();
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- async function handleCommands(req, res, client) {
|
|
|
|
|
- const { body } = req;
|
|
|
|
|
- let { growiCommand } = body;
|
|
|
|
|
|
|
+ function getRespondUtil(responseUrl) {
|
|
|
|
|
+ const proxyUri = crowi.slackIntegrationService.proxyUriForCurrentType; // can be null
|
|
|
|
|
+
|
|
|
|
|
+ const appSiteUrl = crowi.appService.getSiteUrl();
|
|
|
|
|
+ if (appSiteUrl == null || appSiteUrl === '') {
|
|
|
|
|
+ logger.error('App site url must exist.');
|
|
|
|
|
+ throw SlackCommandHandlerError('App site url must exist.');
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
|
|
+ return generateRespondUtil(responseUrl, proxyUri, appSiteUrl);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function getGrowiCommand(body) {
|
|
|
|
|
+ let { growiCommand } = body;
|
|
|
if (growiCommand == null) {
|
|
if (growiCommand == null) {
|
|
|
try {
|
|
try {
|
|
|
growiCommand = parseSlashCommand(body);
|
|
growiCommand = parseSlashCommand(body);
|
|
|
}
|
|
}
|
|
|
catch (err) {
|
|
catch (err) {
|
|
|
if (err instanceof InvalidGrowiCommandError) {
|
|
if (err instanceof InvalidGrowiCommandError) {
|
|
|
- res.json({
|
|
|
|
|
- blocks: [
|
|
|
|
|
- markdownSectionBlock('*Command type is not specified.*'),
|
|
|
|
|
- markdownSectionBlock('Run `/growi help` to check the commands you can use.'),
|
|
|
|
|
- ],
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ const options = {
|
|
|
|
|
+ respondBody: {
|
|
|
|
|
+ text: 'Command type is not specified',
|
|
|
|
|
+ blocks: [
|
|
|
|
|
+ markdownSectionBlock('*Command type is not specified.*'),
|
|
|
|
|
+ markdownSectionBlock('Run `/growi help` to check the commands you can use.'),
|
|
|
|
|
+ ],
|
|
|
|
|
+ },
|
|
|
|
|
+ };
|
|
|
|
|
+ throw new SlackCommandHandlerError('Command type is not specified', options);
|
|
|
}
|
|
}
|
|
|
- logger.error(err.message);
|
|
|
|
|
- return;
|
|
|
|
|
|
|
+ throw err;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+ return growiCommand;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ async function handleCommands(body, res, client, responseUrl) {
|
|
|
|
|
+ let growiCommand;
|
|
|
|
|
+ let respondUtil;
|
|
|
|
|
+ try {
|
|
|
|
|
+ growiCommand = getGrowiCommand(body);
|
|
|
|
|
+ respondUtil = getRespondUtil(responseUrl);
|
|
|
|
|
+ }
|
|
|
|
|
+ catch (err) {
|
|
|
|
|
+ logger.error(err.message);
|
|
|
|
|
+ return handleError(err, responseUrl);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
const { text } = growiCommand;
|
|
const { text } = growiCommand;
|
|
|
|
|
|
|
|
-
|
|
|
|
|
if (text == null) {
|
|
if (text == null) {
|
|
|
return 'No text.';
|
|
return 'No text.';
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /*
|
|
|
|
|
- * TODO: use parseSlashCommand
|
|
|
|
|
- */
|
|
|
|
|
-
|
|
|
|
|
// Send response immediately to avoid opelation_timeout error
|
|
// Send response immediately to avoid opelation_timeout error
|
|
|
// See https://api.slack.com/apis/connections/events-api#the-events-api__responding-to-events
|
|
// See https://api.slack.com/apis/connections/events-api#the-events-api__responding-to-events
|
|
|
- res.json({
|
|
|
|
|
- response_type: 'ephemeral',
|
|
|
|
|
|
|
+ const appSiteUrl = crowi.appService.getSiteUrl();
|
|
|
|
|
+ await respondUtil.respond({
|
|
|
text: 'Processing your request ...',
|
|
text: 'Processing your request ...',
|
|
|
|
|
+ blocks: [
|
|
|
|
|
+ markdownSectionBlock(`Processing your request *"/growi ${growiCommand.growiCommandType}"* on GROWI at ${appSiteUrl} ...`),
|
|
|
|
|
+ ],
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
-
|
|
|
|
|
try {
|
|
try {
|
|
|
- await crowi.slackIntegrationService.handleCommandRequest(growiCommand, client, body);
|
|
|
|
|
|
|
+ await crowi.slackIntegrationService.handleCommandRequest(growiCommand, client, body, respondUtil);
|
|
|
}
|
|
}
|
|
|
catch (err) {
|
|
catch (err) {
|
|
|
- await handleError(err, growiCommand.responseUrl);
|
|
|
|
|
|
|
+ return handleError(err, responseUrl);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // TODO: do investigation and fix if needed GW-7519
|
|
|
|
|
|
|
+ // TODO: this method will be a middleware when typescriptize in the future
|
|
|
|
|
+ function getResponseUrl(req) {
|
|
|
|
|
+ const { body } = req;
|
|
|
|
|
+ const responseUrl = body?.growiCommand?.responseUrl;
|
|
|
|
|
+ if (responseUrl == null) {
|
|
|
|
|
+ return body.response_url; // may be null
|
|
|
|
|
+ }
|
|
|
|
|
+ return responseUrl;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
router.post('/commands', addSigningSecretToReq, verifySlackRequest, checkCommandsPermission, async(req, res) => {
|
|
router.post('/commands', addSigningSecretToReq, verifySlackRequest, checkCommandsPermission, async(req, res) => {
|
|
|
- const client = await slackIntegrationService.generateClientForCustomBotWithoutProxy();
|
|
|
|
|
- return handleCommands(req, res, client);
|
|
|
|
|
|
|
+ const { body } = req;
|
|
|
|
|
+ const responseUrl = getResponseUrl(req);
|
|
|
|
|
+
|
|
|
|
|
+ let client;
|
|
|
|
|
+ try {
|
|
|
|
|
+ client = await slackIntegrationService.generateClientForCustomBotWithoutProxy();
|
|
|
|
|
+ }
|
|
|
|
|
+ catch (err) {
|
|
|
|
|
+ logger.error(err.message);
|
|
|
|
|
+ return handleError(err, responseUrl);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return handleCommands(body, res, client, responseUrl);
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- router.post('/proxied/commands', verifyAccessTokenFromProxy, checkCommandsPermission, async(req, res) => {
|
|
|
|
|
|
|
+ // when relation test
|
|
|
|
|
+ router.post('/proxied/verify', verifyAccessTokenFromProxy, async(req, res) => {
|
|
|
const { body } = req;
|
|
const { body } = req;
|
|
|
|
|
+
|
|
|
// eslint-disable-next-line max-len
|
|
// eslint-disable-next-line max-len
|
|
|
// see: https://api.slack.com/apis/connections/events-api#the-events-api__subscribing-to-event-types__events-api-request-urls__request-url-configuration--verification
|
|
// see: https://api.slack.com/apis/connections/events-api#the-events-api__subscribing-to-event-types__events-api-request-urls__request-url-configuration--verification
|
|
|
if (body.type === 'url_verification') {
|
|
if (body.type === 'url_verification') {
|
|
|
return res.send({ challenge: body.challenge });
|
|
return res.send({ challenge: body.challenge });
|
|
|
}
|
|
}
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ router.post('/proxied/commands', verifyAccessTokenFromProxy, checkCommandsPermission, async(req, res) => {
|
|
|
|
|
+ const { body } = req;
|
|
|
|
|
+ const responseUrl = getResponseUrl(req);
|
|
|
|
|
|
|
|
const tokenPtoG = req.headers['x-growi-ptog-tokens'];
|
|
const tokenPtoG = req.headers['x-growi-ptog-tokens'];
|
|
|
- const client = await slackIntegrationService.generateClientByTokenPtoG(tokenPtoG);
|
|
|
|
|
- return handleCommands(req, res, client);
|
|
|
|
|
|
|
+
|
|
|
|
|
+ let client;
|
|
|
|
|
+ try {
|
|
|
|
|
+ client = await slackIntegrationService.generateClientByTokenPtoG(tokenPtoG);
|
|
|
|
|
+ }
|
|
|
|
|
+ catch (err) {
|
|
|
|
|
+ return handleError(err, responseUrl);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return handleCommands(body, res, client, responseUrl);
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
async function handleInteractionsRequest(req, res, client) {
|
|
async function handleInteractionsRequest(req, res, client) {
|
|
|
|
|
|
|
|
- // Send response immediately to avoid opelation_timeout error
|
|
|
|
|
- // See https://api.slack.com/apis/connections/events-api#the-events-api__responding-to-events
|
|
|
|
|
- res.send();
|
|
|
|
|
-
|
|
|
|
|
const { interactionPayload, interactionPayloadAccessor } = req;
|
|
const { interactionPayload, interactionPayloadAccessor } = req;
|
|
|
const { type } = interactionPayload;
|
|
const { type } = interactionPayload;
|
|
|
|
|
+ const responseUrl = interactionPayloadAccessor.getResponseUrl();
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
|
|
+ const respondUtil = getRespondUtil(responseUrl);
|
|
|
switch (type) {
|
|
switch (type) {
|
|
|
case 'block_actions':
|
|
case 'block_actions':
|
|
|
- await crowi.slackIntegrationService.handleBlockActionsRequest(client, interactionPayload, interactionPayloadAccessor);
|
|
|
|
|
|
|
+ await crowi.slackIntegrationService.handleBlockActionsRequest(client, interactionPayload, interactionPayloadAccessor, respondUtil);
|
|
|
break;
|
|
break;
|
|
|
case 'view_submission':
|
|
case 'view_submission':
|
|
|
- await crowi.slackIntegrationService.handleViewSubmissionRequest(client, interactionPayload, interactionPayloadAccessor);
|
|
|
|
|
|
|
+ await crowi.slackIntegrationService.handleViewSubmissionRequest(client, interactionPayload, interactionPayloadAccessor, respondUtil);
|
|
|
break;
|
|
break;
|
|
|
default:
|
|
default:
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- catch (error) {
|
|
|
|
|
- logger.error(error);
|
|
|
|
|
- await handleError(error, interactionPayloadAccessor.getResponseUrl());
|
|
|
|
|
|
|
+ catch (err) {
|
|
|
|
|
+ logger.error(err);
|
|
|
|
|
+ return handleError(err, responseUrl);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // TODO: do investigation and fix if needed GW-7519
|
|
|
|
|
router.post('/interactions', addSigningSecretToReq, verifySlackRequest, parseSlackInteractionRequest, checkInteractionsPermission, async(req, res) => {
|
|
router.post('/interactions', addSigningSecretToReq, verifySlackRequest, parseSlackInteractionRequest, checkInteractionsPermission, async(req, res) => {
|
|
|
const client = await slackIntegrationService.generateClientForCustomBotWithoutProxy();
|
|
const client = await slackIntegrationService.generateClientForCustomBotWithoutProxy();
|
|
|
return handleInteractionsRequest(req, res, client);
|
|
return handleInteractionsRequest(req, res, client);
|
|
@@ -255,7 +352,6 @@ module.exports = (crowi) => {
|
|
|
router.post('/proxied/interactions', verifyAccessTokenFromProxy, parseSlackInteractionRequest, checkInteractionsPermission, async(req, res) => {
|
|
router.post('/proxied/interactions', verifyAccessTokenFromProxy, parseSlackInteractionRequest, checkInteractionsPermission, async(req, res) => {
|
|
|
const tokenPtoG = req.headers['x-growi-ptog-tokens'];
|
|
const tokenPtoG = req.headers['x-growi-ptog-tokens'];
|
|
|
const client = await slackIntegrationService.generateClientByTokenPtoG(tokenPtoG);
|
|
const client = await slackIntegrationService.generateClientByTokenPtoG(tokenPtoG);
|
|
|
-
|
|
|
|
|
return handleInteractionsRequest(req, res, client);
|
|
return handleInteractionsRequest(req, res, client);
|
|
|
});
|
|
});
|
|
|
|
|
|
|
@@ -267,5 +363,17 @@ module.exports = (crowi) => {
|
|
|
return res.apiv3({ permissionsForBroadcastUseCommands, permissionsForSingleUseCommands });
|
|
return res.apiv3({ permissionsForBroadcastUseCommands, permissionsForSingleUseCommands });
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|
|
+ // error handler
|
|
|
|
|
+ router.use(async(err, req, res, next) => {
|
|
|
|
|
+ const responseUrl = getResponseUrl(req);
|
|
|
|
|
+ if (responseUrl == null) {
|
|
|
|
|
+ // pass err to global error handler
|
|
|
|
|
+ return next(err);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ await handleError(err, responseUrl);
|
|
|
|
|
+ return;
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
return router;
|
|
return router;
|
|
|
};
|
|
};
|