const loggerFactory = require('@alias/logger'); // eslint-disable-next-line no-unused-vars const logger = loggerFactory('growi:routes:apiv3:notification-setting'); const express = require('express'); const router = express.Router(); const { body } = require('express-validator'); const ErrorV3 = require('../../models/vo/error-apiv3'); const removeNullPropertyFromObject = require('../../../lib/util/removeNullPropertyFromObject'); const validator = { slackConfiguration: [ body('webhookUrl').if(value => value != null).isString().trim(), body('isIncomingWebhookPrioritized').isBoolean(), body('slackToken').if(value => value != null).isString().trim(), ], userNotification: [ body('pathPattern').isString().trim(), body('channel').isString().trim(), ], globalNotification: [ body('triggerPath').isString().trim().not() .isEmpty(), body('notifyToType').isString().trim().isIn(['mail', 'slack']), body('toEmail').trim().custom((value, { req }) => { return (req.body.notifyToType === 'mail') ? (!!value && value.match(/.+@.+\..+/)) : true; }), body('slackChannels').trim().custom((value, { req }) => { return (req.body.notifyToType === 'slack') ? !!value : true; }), ], notifyForPageGrant: [ body('isNotificationForOwnerPageEnabled').if(value => value != null).isBoolean(), body('isNotificationForGroupPageEnabled').if(value => value != null).isBoolean(), ], }; /** * @swagger * tags: * name: NotificationSetting */ /** * @swagger * * components: * schemas: * SlackConfigurationParams: * type: object * properties: * webhookUrl: * type: string * description: incoming webhooks url * isIncomingWebhookPrioritized: * type: boolean * description: use incoming webhooks even if Slack App settings are enabled * slackToken: * type: string * description: OAuth access token * UserNotificationParams: * type: object * properties: * pathPattern: * type: string * description: path name of wiki * channel: * type: string * description: slack channel name without '#' * NotifyForPageGrant: * type: object * properties: * isNotificationForOwnerPageEnabled: * type: string * description: Whether to notify on owner page * isNotificationForGroupPageEnabled: * type: string * description: Whether to notify on group page * GlobalNotificationParams: * type: object * properties: * notifyToType: * type: string * description: What is type for notify * toEmail: * type: string * description: email for notify * slackChannels: * type: string * description: channels for notify * triggerPath: * type: string * description: trigger path for notify * triggerEvents: * type: array * items: * type: string * description: trigger events for notify */ module.exports = (crowi) => { const loginRequiredStrictly = require('../../middlewares/login-required')(crowi); const adminRequired = require('../../middlewares/admin-required')(crowi); const csrf = require('../../middlewares/csrf')(crowi); const apiV3FormValidator = require('../../middlewares/apiv3-form-validator')(crowi); const UpdatePost = crowi.model('UpdatePost'); const GlobalNotificationSetting = crowi.model('GlobalNotificationSetting'); const GlobalNotificationMailSetting = crowi.models.GlobalNotificationMailSetting; const GlobalNotificationSlackSetting = crowi.models.GlobalNotificationSlackSetting; /** * @swagger * * /notification-setting/: * get: * tags: [NotificationSetting] * description: Get notification paramators * responses: * 200: * description: params of notification * content: * application/json: * schema: * properties: * notificationParams: * type: object * description: notification params */ router.get('/', loginRequiredStrictly, adminRequired, async(req, res) => { const notificationParams = { webhookUrl: await crowi.configManager.getConfig('notification', 'slack:incomingWebhookUrl'), isIncomingWebhookPrioritized: await crowi.configManager.getConfig('notification', 'slack:isIncomingWebhookPrioritized'), slackToken: await crowi.configManager.getConfig('notification', 'slack:token'), userNotifications: await UpdatePost.findAll(), isNotificationForOwnerPageEnabled: await crowi.configManager.getConfig('notification', 'notification:owner-page:isEnabled'), isNotificationForGroupPageEnabled: await crowi.configManager.getConfig('notification', 'notification:group-page:isEnabled'), globalNotifications: await GlobalNotificationSetting.findAll(), }; return res.apiv3({ notificationParams }); }); /** * @swagger * * /notification-setting/slack-configuration: * put: * tags: [NotificationSetting] * description: Update slack configuration setting * requestBody: * required: true * content: * application/json: * schema: * $ref: '#/components/schemas/SlackConfigurationParams' * responses: * 200: * description: Succeeded to update slack configuration setting * content: * application/json: * schema: * $ref: '#/components/schemas/SlackConfigurationParams' */ router.put('/slack-configuration', loginRequiredStrictly, adminRequired, csrf, validator.slackConfiguration, apiV3FormValidator, async(req, res) => { const requestParams = { 'slack:incomingWebhookUrl': req.body.webhookUrl, 'slack:isIncomingWebhookPrioritized': req.body.isIncomingWebhookPrioritized, 'slack:token': req.body.slackToken, }; try { await crowi.configManager.updateConfigsInTheSameNamespace('notification', requestParams); const responseParams = { webhookUrl: await crowi.configManager.getConfig('notification', 'slack:incomingWebhookUrl'), isIncomingWebhookPrioritized: await crowi.configManager.getConfig('notification', 'slack:isIncomingWebhookPrioritized'), slackToken: await crowi.configManager.getConfig('notification', 'slack:token'), }; await crowi.setupSlack(); return res.apiv3({ responseParams }); } catch (err) { const msg = 'Error occurred in updating slack configuration'; logger.error('Error', err); return res.apiv3Err(new ErrorV3(msg, 'update-slackConfiguration-failed')); } }); /** * @swagger * * /notification-setting/user-notification: * post: * tags: [NotificationSetting] * description: add user notification setting * requestBody: * required: true * content: * application/json: * schema: * $ref: '#/components/schemas/UserNotificationParams' * responses: * 200: * description: Succeeded to add user notification setting * content: * application/json: * schema: * properties: * createdUser: * type: object * description: user who set notification * userNotifications: * type: object * description: user trigger notifications for updated */ router.post('/user-notification', loginRequiredStrictly, adminRequired, csrf, validator.userNotification, apiV3FormValidator, async(req, res) => { const { pathPattern, channel } = req.body; const UpdatePost = crowi.model('UpdatePost'); try { logger.info('notification.add', pathPattern, channel); const responseParams = { createdUser: await UpdatePost.create(pathPattern, channel, req.user), userNotifications: await UpdatePost.findAll(), }; return res.apiv3({ responseParams }); } catch (err) { const msg = 'Error occurred in updating user notification'; logger.error('Error', err); return res.apiv3Err(new ErrorV3(msg, 'update-userNotification-failed')); } }); /** * @swagger * * /notification-setting/user-notification/{id}: * delete: * tags: [NotificationSetting] * description: delete user trigger notification pattern * parameters: * - name: id * in: path * required: true * description: id of user trigger notification * schema: * type: string * responses: * 200: * description: Succeeded to delete user trigger notification pattern * content: * application/json: * schema: * properties: * deletedNotificaton: * type: object * description: deleted notification */ router.delete('/user-notification/:id', loginRequiredStrictly, adminRequired, csrf, async(req, res) => { const { id } = req.params; try { const deletedNotificaton = await UpdatePost.remove(id); return res.apiv3(deletedNotificaton); } catch (err) { const msg = 'Error occurred in delete user trigger notification'; logger.error('Error', err); return res.apiv3Err(new ErrorV3(msg, 'delete-userTriggerNotification-failed')); } }); /** * @swagger * * /notification-setting/global-notification: * post: * tags: [NotificationSetting] * description: add global notification * requestBody: * required: true * content: * application/json: * schema: * $ref: '#/components/schemas/GlobalNotificationParams' * responses: * 200: * description: Succeeded to add global notification * content: * application/json: * schema: * properties: * createdNotification: * type: object * description: notification param created */ router.post('/global-notification', loginRequiredStrictly, adminRequired, csrf, validator.globalNotification, apiV3FormValidator, async(req, res) => { const { notifyToType, toEmail, slackChannels, triggerPath, triggerEvents, } = req.body; let notification; if (notifyToType === GlobalNotificationSetting.TYPE.MAIL) { notification = new GlobalNotificationMailSetting(crowi); notification.toEmail = toEmail; } if (notifyToType === GlobalNotificationSetting.TYPE.SLACK) { notification = new GlobalNotificationSlackSetting(crowi); notification.slackChannels = slackChannels; } notification.triggerPath = triggerPath; notification.triggerEvents = triggerEvents || []; try { const createdNotification = await notification.save(); return res.apiv3({ createdNotification }); } catch (err) { const msg = 'Error occurred in updating global notification'; logger.error('Error', err); return res.apiv3Err(new ErrorV3(msg, 'post-globalNotification-failed')); } }); /** * @swagger * * /notification-setting/global-notification/{id}: * put: * tags: [NotificationSetting] * description: update global notification * parameters: * - name: id * in: path * required: true * description: global notification id for updated * schema: * type: string * requestBody: * required: true * content: * application/json: * schema: * $ref: '#/components/schemas/GlobalNotificationParams' * responses: * 200: * description: Succeeded to update global notification * content: * application/json: * schema: * properties: * createdNotification: * type: object * description: notification param updated */ router.put('/global-notification/:id', loginRequiredStrictly, adminRequired, csrf, validator.globalNotification, apiV3FormValidator, async(req, res) => { const { id } = req.params; const { notifyToType, toEmail, slackChannels, triggerPath, triggerEvents, } = req.body; const models = { [GlobalNotificationSetting.TYPE.MAIL]: GlobalNotificationMailSetting, [GlobalNotificationSetting.TYPE.SLACK]: GlobalNotificationSlackSetting, }; try { let setting = await GlobalNotificationSetting.findOne({ _id: id }); setting = setting.toObject(); // when switching from one type to another, // remove toEmail from slack setting and slackChannels from mail setting if (setting.__t !== notifyToType) { setting = models[setting.__t].hydrate(setting); setting.toEmail = undefined; setting.slackChannels = undefined; await setting.save(); setting = setting.toObject(); } if (notifyToType === GlobalNotificationSetting.TYPE.MAIL) { setting = GlobalNotificationMailSetting.hydrate(setting); setting.toEmail = toEmail; } if (notifyToType === GlobalNotificationSetting.TYPE.SLACK) { setting = GlobalNotificationSlackSetting.hydrate(setting); setting.slackChannels = slackChannels; } setting.__t = notifyToType; setting.triggerPath = triggerPath; setting.triggerEvents = triggerEvents || []; const createdNotification = await setting.save(); return res.apiv3({ createdNotification }); } catch (err) { const msg = 'Error occurred in updating global notification'; logger.error('Error', err); return res.apiv3Err(new ErrorV3(msg, 'post-globalNotification-failed')); } }); /** * @swagger * * /notification-setting/notify-for-page-grant: * put: * tags: [NotificationSetting] * description: Update settings for notify for page grant * requestBody: * required: true * content: * application/json: * schema: * $ref: '#/components/schemas/NotifyForPageGrant' * responses: * 200: * description: Succeeded to settings for notify for page grant * content: * application/json: * schema: * $ref: '#/components/schemas/NotifyForPageGrant' */ router.put('/notify-for-page-grant', loginRequiredStrictly, adminRequired, csrf, validator.notifyForPageGrant, apiV3FormValidator, async(req, res) => { let requestParams = { 'notification:owner-page:isEnabled': req.body.isNotificationForOwnerPageEnabled, 'notification:group-page:isEnabled': req.body.isNotificationForGroupPageEnabled, }; requestParams = removeNullPropertyFromObject(requestParams); try { await crowi.configManager.updateConfigsInTheSameNamespace('notification', requestParams); const responseParams = { isNotificationForOwnerPageEnabled: await crowi.configManager.getConfig('notification', 'notification:owner-page:isEnabled'), isNotificationForGroupPageEnabled: await crowi.configManager.getConfig('notification', 'notification:group-page:isEnabled'), }; return res.apiv3({ responseParams }); } catch (err) { const msg = 'Error occurred in updating notify for page grant'; logger.error('Error', err); return res.apiv3Err(new ErrorV3(msg, 'update-notify-for-page-grant-failed')); } }); /** * @swagger * * /notification-setting/global-notification/{id}/enabled: * put: * tags: [NotificationSetting] * description: toggle enabled global notification * parameters: * - name: id * in: path * required: true * description: notification id for updated * schema: * type: string * requestBody: * required: true * content: * application/json: * schema: * properties: * isEnabled: * type: boolean * description: is notification enabled * responses: * 200: * description: Succeeded to delete global notification pattern * content: * application/json: * schema: * properties: * deletedNotificaton: * type: object * description: notification id for updated */ router.put('/global-notification/:id/enabled', loginRequiredStrictly, adminRequired, csrf, async(req, res) => { const { id } = req.params; const { isEnabled } = req.body; try { if (isEnabled) { await GlobalNotificationSetting.enable(id); } else { await GlobalNotificationSetting.disable(id); } return res.apiv3({ id }); } catch (err) { const msg = 'Error occurred in toggle of global notification'; logger.error('Error', err); return res.apiv3Err(new ErrorV3(msg, 'toggle-globalNotification-failed')); } }); /** * @swagger * * /notification-setting/global-notification/{id}: * delete: * tags: [NotificationSetting] * description: delete global notification pattern * parameters: * - name: id * in: path * required: true * description: id of global notification * schema: * type: string * responses: * 200: * description: Succeeded to delete global notification pattern * content: * application/json: * schema: * properties: * deletedNotificaton: * type: object * description: deleted notification */ router.delete('/global-notification/:id', loginRequiredStrictly, adminRequired, csrf, async(req, res) => { const { id } = req.params; try { const deletedNotificaton = await GlobalNotificationSetting.findOneAndRemove({ _id: id }); return res.apiv3(deletedNotificaton); } catch (err) { const msg = 'Error occurred in delete global notification'; logger.error('Error', err); return res.apiv3Err(new ErrorV3(msg, 'delete-globalNotification-failed')); } }); return router; };