notification-setting.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  1. import { ErrorV3 } from '@growi/core';
  2. import { SupportedAction } from '~/interfaces/activity';
  3. import loggerFactory from '~/utils/logger';
  4. import { removeNullPropertyFromObject } from '~/utils/object-utils';
  5. import { generateAddActivityMiddleware } from '../../middlewares/add-activity';
  6. import { apiV3FormValidator } from '../../middlewares/apiv3-form-validator';
  7. import UpdatePost from '../../models/update-post';
  8. // eslint-disable-next-line no-unused-vars
  9. const logger = loggerFactory('growi:routes:apiv3:notification-setting');
  10. const express = require('express');
  11. const router = express.Router();
  12. const { body } = require('express-validator');
  13. const validator = {
  14. userNotification: [
  15. body('pathPattern').isString().trim(),
  16. body('channel').isString().trim(),
  17. ],
  18. globalNotification: [
  19. body('triggerPath').isString().trim().not()
  20. .isEmpty(),
  21. body('notifyType').isString().trim().isIn(['mail', 'slack']),
  22. body('toEmail').trim().custom((value, { req }) => {
  23. return (req.body.notifyType === 'mail') ? (!!value && value.match(/.+@.+\..+/)) : true;
  24. }),
  25. body('slackChannels').trim().custom((value, { req }) => {
  26. return (req.body.notifyType === 'slack') ? !!value : true;
  27. }),
  28. ],
  29. notifyForPageGrant: [
  30. body('isNotificationForOwnerPageEnabled').if(value => value != null).isBoolean(),
  31. body('isNotificationForGroupPageEnabled').if(value => value != null).isBoolean(),
  32. ],
  33. };
  34. /**
  35. * @swagger
  36. * tags:
  37. * name: NotificationSetting
  38. */
  39. /**
  40. * @swagger
  41. *
  42. * components:
  43. * schemas:
  44. * UserNotificationParams:
  45. * type: object
  46. * properties:
  47. * pathPattern:
  48. * type: string
  49. * description: path name of wiki
  50. * channel:
  51. * type: string
  52. * description: slack channel name without '#'
  53. * NotifyForPageGrant:
  54. * type: object
  55. * properties:
  56. * isNotificationForOwnerPageEnabled:
  57. * type: string
  58. * description: Whether to notify on owner page
  59. * isNotificationForGroupPageEnabled:
  60. * type: string
  61. * description: Whether to notify on group page
  62. * GlobalNotificationParams:
  63. * type: object
  64. * properties:
  65. * notifyType:
  66. * type: string
  67. * description: What is type for notify
  68. * toEmail:
  69. * type: string
  70. * description: email for notify
  71. * slackChannels:
  72. * type: string
  73. * description: channels for notify
  74. * triggerPath:
  75. * type: string
  76. * description: trigger path for notify
  77. * triggerEvents:
  78. * type: array
  79. * items:
  80. * type: string
  81. * description: trigger events for notify
  82. */
  83. module.exports = (crowi) => {
  84. const loginRequiredStrictly = require('../../middlewares/login-required')(crowi);
  85. const adminRequired = require('../../middlewares/admin-required')(crowi);
  86. const addActivity = generateAddActivityMiddleware(crowi);
  87. const activityEvent = crowi.event('activity');
  88. const GlobalNotificationSetting = crowi.model('GlobalNotificationSetting');
  89. const GlobalNotificationMailSetting = crowi.models.GlobalNotificationMailSetting;
  90. const GlobalNotificationSlackSetting = crowi.models.GlobalNotificationSlackSetting;
  91. /**
  92. * @swagger
  93. *
  94. * /notification-setting/:
  95. * get:
  96. * tags: [NotificationSetting]
  97. * description: Get notification paramators
  98. * responses:
  99. * 200:
  100. * description: params of notification
  101. * content:
  102. * application/json:
  103. * schema:
  104. * properties:
  105. * notificationParams:
  106. * type: object
  107. * description: notification params
  108. */
  109. router.get('/', loginRequiredStrictly, adminRequired, async(req, res) => {
  110. const notificationParams = {
  111. // status of slack intagration
  112. isSlackbotConfigured: crowi.slackIntegrationService.isSlackbotConfigured,
  113. isSlackLegacyConfigured: crowi.slackIntegrationService.isSlackLegacyConfigured,
  114. currentBotType: await crowi.configManager.getConfig('crowi', 'slackbot:currentBotType'),
  115. userNotifications: await UpdatePost.findAll(),
  116. isNotificationForOwnerPageEnabled: await crowi.configManager.getConfig('notification', 'notification:owner-page:isEnabled'),
  117. isNotificationForGroupPageEnabled: await crowi.configManager.getConfig('notification', 'notification:group-page:isEnabled'),
  118. globalNotifications: await GlobalNotificationSetting.findAll(),
  119. };
  120. return res.apiv3({ notificationParams });
  121. });
  122. /**
  123. * @swagger
  124. *
  125. * /notification-setting/user-notification:
  126. * post:
  127. * tags: [NotificationSetting]
  128. * description: add user notification setting
  129. * requestBody:
  130. * required: true
  131. * content:
  132. * application/json:
  133. * schema:
  134. * $ref: '#/components/schemas/UserNotificationParams'
  135. * responses:
  136. * 200:
  137. * description: Succeeded to add user notification setting
  138. * content:
  139. * application/json:
  140. * schema:
  141. * properties:
  142. * createdUser:
  143. * type: object
  144. * description: user who set notification
  145. * userNotifications:
  146. * type: object
  147. * description: user trigger notifications for updated
  148. */
  149. // eslint-disable-next-line max-len
  150. router.post('/user-notification', loginRequiredStrictly, adminRequired, addActivity, validator.userNotification, apiV3FormValidator, async(req, res) => {
  151. const { pathPattern, channel } = req.body;
  152. try {
  153. logger.info('notification.add', pathPattern, channel);
  154. const responseParams = {
  155. createdUser: await UpdatePost.createUpdatePost(pathPattern, channel, req.user),
  156. userNotifications: await UpdatePost.findAll(),
  157. };
  158. const parameters = { action: SupportedAction.ACTION_ADMIN_USER_NOTIFICATION_SETTINGS_ADD };
  159. activityEvent.emit('update', res.locals.activity._id, parameters);
  160. return res.apiv3({ responseParams }, 201);
  161. }
  162. catch (err) {
  163. const msg = 'Error occurred in updating user notification';
  164. logger.error('Error', err);
  165. return res.apiv3Err(new ErrorV3(msg, 'update-userNotification-failed'));
  166. }
  167. });
  168. /**
  169. * @swagger
  170. *
  171. * /notification-setting/user-notification/{id}:
  172. * delete:
  173. * tags: [NotificationSetting]
  174. * description: delete user trigger notification pattern
  175. * parameters:
  176. * - name: id
  177. * in: path
  178. * required: true
  179. * description: id of user trigger notification
  180. * schema:
  181. * type: string
  182. * responses:
  183. * 200:
  184. * description: Succeeded to delete user trigger notification pattern
  185. * content:
  186. * application/json:
  187. * schema:
  188. * properties:
  189. * deletedNotificaton:
  190. * type: object
  191. * description: deleted notification
  192. */
  193. router.delete('/user-notification/:id', loginRequiredStrictly, adminRequired, addActivity, async(req, res) => {
  194. const { id } = req.params;
  195. try {
  196. const deletedNotificaton = await UpdatePost.findOneAndRemove({ _id: id });
  197. const parameters = { action: SupportedAction.ACTION_ADMIN_USER_NOTIFICATION_SETTINGS_DELETE };
  198. activityEvent.emit('update', res.locals.activity._id, parameters);
  199. return res.apiv3(deletedNotificaton);
  200. }
  201. catch (err) {
  202. const msg = 'Error occurred in delete user trigger notification';
  203. logger.error('Error', err);
  204. return res.apiv3Err(new ErrorV3(msg, 'delete-userTriggerNotification-failed'));
  205. }
  206. });
  207. router.get('/global-notification/:id', loginRequiredStrictly, adminRequired, validator.globalNotification, async(req, res) => {
  208. const notificationSettingId = req.params.id;
  209. let globalNotification;
  210. if (notificationSettingId) {
  211. try {
  212. globalNotification = await GlobalNotificationSetting.findOne({ _id: notificationSettingId });
  213. }
  214. catch (err) {
  215. logger.error(`Error in finding a global notification setting with {_id: ${notificationSettingId}}`);
  216. }
  217. }
  218. return res.apiv3({ globalNotification });
  219. });
  220. /**
  221. * @swagger
  222. *
  223. * /notification-setting/global-notification:
  224. * post:
  225. * tags: [NotificationSetting]
  226. * description: add global notification
  227. * requestBody:
  228. * required: true
  229. * content:
  230. * application/json:
  231. * schema:
  232. * $ref: '#/components/schemas/GlobalNotificationParams'
  233. * responses:
  234. * 200:
  235. * description: Succeeded to add global notification
  236. * content:
  237. * application/json:
  238. * schema:
  239. * properties:
  240. * createdNotification:
  241. * type: object
  242. * description: notification param created
  243. */
  244. // eslint-disable-next-line max-len
  245. router.post('/global-notification', loginRequiredStrictly, adminRequired, addActivity, validator.globalNotification, apiV3FormValidator, async(req, res) => {
  246. const {
  247. notifyType, toEmail, slackChannels, triggerPath, triggerEvents,
  248. } = req.body;
  249. let notification;
  250. if (notifyType === GlobalNotificationSetting.TYPE.MAIL) {
  251. notification = new GlobalNotificationMailSetting(crowi);
  252. notification.toEmail = toEmail;
  253. }
  254. if (notifyType === GlobalNotificationSetting.TYPE.SLACK) {
  255. notification = new GlobalNotificationSlackSetting(crowi);
  256. notification.slackChannels = slackChannels;
  257. }
  258. notification.triggerPath = triggerPath;
  259. notification.triggerEvents = triggerEvents || [];
  260. try {
  261. const createdNotification = await notification.save();
  262. const parameters = { action: SupportedAction.ACTION_ADMIN_GLOBAL_NOTIFICATION_SETTINGS_ADD };
  263. activityEvent.emit('update', res.locals.activity._id, parameters);
  264. return res.apiv3({ createdNotification }, 201);
  265. }
  266. catch (err) {
  267. const msg = 'Error occurred in updating global notification';
  268. logger.error('Error', err);
  269. return res.apiv3Err(new ErrorV3(msg, 'post-globalNotification-failed'));
  270. }
  271. });
  272. /**
  273. * @swagger
  274. *
  275. * /notification-setting/global-notification/{id}:
  276. * put:
  277. * tags: [NotificationSetting]
  278. * description: update global notification
  279. * parameters:
  280. * - name: id
  281. * in: path
  282. * required: true
  283. * description: global notification id for updated
  284. * schema:
  285. * type: string
  286. * requestBody:
  287. * required: true
  288. * content:
  289. * application/json:
  290. * schema:
  291. * $ref: '#/components/schemas/GlobalNotificationParams'
  292. * responses:
  293. * 200:
  294. * description: Succeeded to update global notification
  295. * content:
  296. * application/json:
  297. * schema:
  298. * properties:
  299. * createdNotification:
  300. * type: object
  301. * description: notification param updated
  302. */
  303. // eslint-disable-next-line max-len
  304. router.put('/global-notification/:id', loginRequiredStrictly, adminRequired, addActivity, validator.globalNotification, apiV3FormValidator, async(req, res) => {
  305. const { id } = req.params;
  306. const {
  307. notifyType, toEmail, slackChannels, triggerPath, triggerEvents,
  308. } = req.body;
  309. const models = {
  310. [GlobalNotificationSetting.TYPE.MAIL]: GlobalNotificationMailSetting,
  311. [GlobalNotificationSetting.TYPE.SLACK]: GlobalNotificationSlackSetting,
  312. };
  313. try {
  314. let setting = await GlobalNotificationSetting.findOne({ _id: id });
  315. setting = setting.toObject();
  316. // when switching from one type to another,
  317. // remove toEmail from slack setting and slackChannels from mail setting
  318. if (setting.__t !== notifyType) {
  319. setting = models[setting.__t].hydrate(setting);
  320. setting.toEmail = undefined;
  321. setting.slackChannels = undefined;
  322. await setting.save();
  323. setting = setting.toObject();
  324. }
  325. if (notifyType === GlobalNotificationSetting.TYPE.MAIL) {
  326. setting = GlobalNotificationMailSetting.hydrate(setting);
  327. setting.toEmail = toEmail;
  328. }
  329. if (notifyType === GlobalNotificationSetting.TYPE.SLACK) {
  330. setting = GlobalNotificationSlackSetting.hydrate(setting);
  331. setting.slackChannels = slackChannels;
  332. }
  333. setting.__t = notifyType;
  334. setting.triggerPath = triggerPath;
  335. setting.triggerEvents = triggerEvents || [];
  336. const createdNotification = await setting.save();
  337. const parameters = { action: SupportedAction.ACTION_ADMIN_GLOBAL_NOTIFICATION_SETTINGS_UPDATE };
  338. activityEvent.emit('update', res.locals.activity._id, parameters);
  339. return res.apiv3({ createdNotification });
  340. }
  341. catch (err) {
  342. const msg = 'Error occurred in updating global notification';
  343. logger.error('Error', err);
  344. return res.apiv3Err(new ErrorV3(msg, 'post-globalNotification-failed'));
  345. }
  346. });
  347. /**
  348. * @swagger
  349. *
  350. * /notification-setting/notify-for-page-grant:
  351. * put:
  352. * tags: [NotificationSetting]
  353. * description: Update settings for notify for page grant
  354. * requestBody:
  355. * required: true
  356. * content:
  357. * application/json:
  358. * schema:
  359. * $ref: '#/components/schemas/NotifyForPageGrant'
  360. * responses:
  361. * 200:
  362. * description: Succeeded to settings for notify for page grant
  363. * content:
  364. * application/json:
  365. * schema:
  366. * $ref: '#/components/schemas/NotifyForPageGrant'
  367. */
  368. // eslint-disable-next-line max-len
  369. router.put('/notify-for-page-grant', loginRequiredStrictly, adminRequired, addActivity, validator.notifyForPageGrant, apiV3FormValidator, async(req, res) => {
  370. let requestParams = {
  371. 'notification:owner-page:isEnabled': req.body.isNotificationForOwnerPageEnabled,
  372. 'notification:group-page:isEnabled': req.body.isNotificationForGroupPageEnabled,
  373. };
  374. requestParams = removeNullPropertyFromObject(requestParams);
  375. try {
  376. await crowi.configManager.updateConfigsInTheSameNamespace('notification', requestParams);
  377. const responseParams = {
  378. isNotificationForOwnerPageEnabled: await crowi.configManager.getConfig('notification', 'notification:owner-page:isEnabled'),
  379. isNotificationForGroupPageEnabled: await crowi.configManager.getConfig('notification', 'notification:group-page:isEnabled'),
  380. };
  381. const parameters = { action: SupportedAction.ACTION_ADMIN_NOTIFICATION_GRANT_SETTINGS_UPDATE };
  382. activityEvent.emit('update', res.locals.activity._id, parameters);
  383. return res.apiv3({ responseParams });
  384. }
  385. catch (err) {
  386. const msg = 'Error occurred in updating notify for page grant';
  387. logger.error('Error', err);
  388. return res.apiv3Err(new ErrorV3(msg, 'update-notify-for-page-grant-failed'));
  389. }
  390. });
  391. /**
  392. * @swagger
  393. *
  394. * /notification-setting/global-notification/{id}/enabled:
  395. * put:
  396. * tags: [NotificationSetting]
  397. * description: toggle enabled global notification
  398. * parameters:
  399. * - name: id
  400. * in: path
  401. * required: true
  402. * description: notification id for updated
  403. * schema:
  404. * type: string
  405. * requestBody:
  406. * required: true
  407. * content:
  408. * application/json:
  409. * schema:
  410. * properties:
  411. * isEnabled:
  412. * type: boolean
  413. * description: is notification enabled
  414. * responses:
  415. * 200:
  416. * description: Succeeded to delete global notification pattern
  417. * content:
  418. * application/json:
  419. * schema:
  420. * properties:
  421. * deletedNotificaton:
  422. * type: object
  423. * description: notification id for updated
  424. */
  425. router.put('/global-notification/:id/enabled', loginRequiredStrictly, adminRequired, addActivity, async(req, res) => {
  426. const { id } = req.params;
  427. const { isEnabled } = req.body;
  428. try {
  429. if (isEnabled) {
  430. await GlobalNotificationSetting.enable(id);
  431. }
  432. else {
  433. await GlobalNotificationSetting.disable(id);
  434. }
  435. const parameters = {
  436. action: isEnabled
  437. ? SupportedAction.ACTION_ADMIN_GLOBAL_NOTIFICATION_SETTINGS_ENABLED
  438. : SupportedAction.ACTION_ADMIN_GLOBAL_NOTIFICATION_SETTINGS_DISABLED,
  439. };
  440. activityEvent.emit('update', res.locals.activity._id, parameters);
  441. return res.apiv3({ id });
  442. }
  443. catch (err) {
  444. const msg = 'Error occurred in toggle of global notification';
  445. logger.error('Error', err);
  446. return res.apiv3Err(new ErrorV3(msg, 'toggle-globalNotification-failed'));
  447. }
  448. });
  449. /**
  450. * @swagger
  451. *
  452. * /notification-setting/global-notification/{id}:
  453. * delete:
  454. * tags: [NotificationSetting]
  455. * description: delete global notification pattern
  456. * parameters:
  457. * - name: id
  458. * in: path
  459. * required: true
  460. * description: id of global notification
  461. * schema:
  462. * type: string
  463. * responses:
  464. * 200:
  465. * description: Succeeded to delete global notification pattern
  466. * content:
  467. * application/json:
  468. * schema:
  469. * properties:
  470. * deletedNotificaton:
  471. * type: object
  472. * description: deleted notification
  473. */
  474. router.delete('/global-notification/:id', loginRequiredStrictly, adminRequired, addActivity, async(req, res) => {
  475. const { id } = req.params;
  476. try {
  477. const deletedNotificaton = await GlobalNotificationSetting.findOneAndRemove({ _id: id });
  478. const parameters = { action: SupportedAction.ACTION_ADMIN_GLOBAL_NOTIFICATION_SETTINGS_DELETE };
  479. activityEvent.emit('update', res.locals.activity._id, parameters);
  480. return res.apiv3(deletedNotificaton);
  481. }
  482. catch (err) {
  483. const msg = 'Error occurred in delete global notification';
  484. logger.error('Error', err);
  485. return res.apiv3Err(new ErrorV3(msg, 'delete-globalNotification-failed'));
  486. }
  487. });
  488. return router;
  489. };