notification-setting.js 19 KB

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