notification-setting.js 17 KB

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