slack-integration-settings.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707
  1. import { SlackbotType } from '@growi/slack';
  2. import loggerFactory from '~/utils/logger';
  3. const mongoose = require('mongoose');
  4. const express = require('express');
  5. const { body, query, param } = require('express-validator');
  6. const axios = require('axios');
  7. const urljoin = require('url-join');
  8. const {
  9. getConnectionStatus, getConnectionStatuses,
  10. sendSuccessMessage,
  11. defaultSupportedCommandsNameForBroadcastUse, defaultSupportedCommandsNameForSingleUse,
  12. REQUEST_TIMEOUT_FOR_GTOP,
  13. } = require('@growi/slack');
  14. const ErrorV3 = require('../../models/vo/error-apiv3');
  15. const logger = loggerFactory('growi:routes:apiv3:slack-integration-settings');
  16. const router = express.Router();
  17. const OFFICIAL_SLACKBOT_PROXY_URI = 'https://slackbot-proxy.growi.org';
  18. /**
  19. * @swagger
  20. * tags:
  21. * name: SlackIntegrationSettings
  22. */
  23. /**
  24. * @swagger
  25. *
  26. * components:
  27. * schemas:
  28. * BotType:
  29. * description: BotType
  30. * properties:
  31. * currentBotType:
  32. * type: string
  33. * SlackIntegration:
  34. * description: SlackIntegration
  35. * type: object
  36. * properties:
  37. * currentBotType:
  38. * type: string
  39. */
  40. module.exports = (crowi) => {
  41. const accessTokenParser = require('../../middlewares/access-token-parser')(crowi);
  42. const loginRequiredStrictly = require('../../middlewares/login-required')(crowi);
  43. const adminRequired = require('../../middlewares/admin-required')(crowi);
  44. const csrf = require('../../middlewares/csrf')(crowi);
  45. const apiV3FormValidator = require('../../middlewares/apiv3-form-validator')(crowi);
  46. const SlackAppIntegration = mongoose.model('SlackAppIntegration');
  47. const validator = {
  48. botType: [
  49. body('currentBotType').isString(),
  50. ],
  51. slackIntegration: [
  52. body('currentBotType')
  53. .isIn(Object.values(SlackbotType)),
  54. ],
  55. proxyUri: [
  56. body('proxyUri').if(value => value !== '').trim().matches(/^(https?:\/\/)/)
  57. .isURL({ require_tld: false }),
  58. ],
  59. makePrimary: [
  60. param('id').isMongoId().withMessage('id is required'),
  61. ],
  62. updateSupportedCommands: [
  63. body('supportedCommandsForSingleUse').toArray(),
  64. body('supportedCommandsForBroadcastUse').toArray(),
  65. param('id').isMongoId().withMessage('id is required'),
  66. ],
  67. relationTest: [
  68. param('id').isMongoId(),
  69. body('channel').trim().isString(),
  70. ],
  71. regenerateTokens: [
  72. param('id').isMongoId(),
  73. ],
  74. deleteIntegration: [
  75. param('id').isMongoId(),
  76. ],
  77. slackChannel: [
  78. body('channel').trim().not().isEmpty()
  79. .isString(),
  80. ],
  81. };
  82. async function updateSlackBotSettings(params) {
  83. const { configManager } = crowi;
  84. // update config without publishing S2sMessage
  85. return configManager.updateConfigsInTheSameNamespace('crowi', params, true);
  86. }
  87. async function resetAllBotSettings(initializedType) {
  88. await SlackAppIntegration.deleteMany();
  89. const params = {
  90. 'slackbot:currentBotType': initializedType,
  91. 'slackbot:withoutProxy:signingSecret': null,
  92. 'slackbot:withoutProxy:botToken': null,
  93. 'slackbot:proxyUri': null,
  94. };
  95. // set url if officialBot is specified
  96. if (initializedType === SlackbotType.OFFICIAL) {
  97. params['slackbot:proxyUri'] = OFFICIAL_SLACKBOT_PROXY_URI;
  98. }
  99. return updateSlackBotSettings(params);
  100. }
  101. async function getConnectionStatusesFromProxy(tokens) {
  102. const csv = tokens.join(',');
  103. const proxyUri = crowi.configManager.getConfig('crowi', 'slackbot:proxyUri');
  104. const result = await axios.get(urljoin(proxyUri, '/g2s/connection-status'), {
  105. headers: {
  106. 'x-growi-gtop-tokens': csv,
  107. timeout: REQUEST_TIMEOUT_FOR_GTOP,
  108. },
  109. });
  110. return result.data;
  111. }
  112. async function requestToProxyServer(token, method, endpoint, body) {
  113. const proxyUri = crowi.configManager.getConfig('crowi', 'slackbot:proxyUri');
  114. if (proxyUri == null) {
  115. throw new Error('Proxy URL is not registered');
  116. }
  117. try {
  118. const result = await axios[method](
  119. urljoin(proxyUri, endpoint),
  120. body, {
  121. headers: {
  122. 'x-growi-gtop-tokens': token,
  123. },
  124. timeout: REQUEST_TIMEOUT_FOR_GTOP,
  125. },
  126. );
  127. return result.data;
  128. }
  129. catch (err) {
  130. throw new Error(`Requesting to proxy server failed: ${err.message}`);
  131. }
  132. }
  133. /**
  134. * @swagger
  135. *
  136. * /slack-integration-settings/:
  137. * get:
  138. * tags: [SlackBotSettingParams]
  139. * operationId: getSlackBotSettingParams
  140. * summary: get /slack-integration
  141. * description: Get current settings and connection statuses.
  142. * responses:
  143. * 200:
  144. * description: Succeeded to get info.
  145. */
  146. router.get('/', accessTokenParser, loginRequiredStrictly, adminRequired, async(req, res) => {
  147. const { configManager } = crowi;
  148. const currentBotType = configManager.getConfig('crowi', 'slackbot:currentBotType');
  149. // retrieve settings
  150. const settings = {};
  151. if (currentBotType === SlackbotType.CUSTOM_WITHOUT_PROXY) {
  152. settings.slackSigningSecretEnvVars = configManager.getConfigFromEnvVars('crowi', 'slackbot:withoutProxy:signingSecret');
  153. settings.slackBotTokenEnvVars = configManager.getConfigFromEnvVars('crowi', 'slackbot:withoutProxy:botToken');
  154. settings.slackSigningSecret = configManager.getConfig('crowi', 'slackbot:withoutProxy:signingSecret');
  155. settings.slackBotToken = configManager.getConfig('crowi', 'slackbot:withoutProxy:botToken');
  156. }
  157. else {
  158. settings.proxyServerUri = crowi.configManager.getConfig('crowi', 'slackbot:proxyUri');
  159. settings.proxyUriEnvVars = configManager.getConfigFromEnvVars('crowi', 'slackbot:proxyUri');
  160. }
  161. // retrieve connection statuses
  162. let connectionStatuses = {};
  163. let errorMsg;
  164. let errorCode;
  165. if (currentBotType == null) {
  166. // no need to do anything
  167. }
  168. else if (currentBotType === SlackbotType.CUSTOM_WITHOUT_PROXY) {
  169. const token = settings.slackBotToken;
  170. // check the token is not null
  171. if (token != null) {
  172. try {
  173. connectionStatuses = await getConnectionStatuses([token]);
  174. }
  175. catch (e) {
  176. errorMsg = 'Error occured in getting connection statuses';
  177. errorCode = 'get-connection-failed';
  178. logger.error(errorMsg, e);
  179. }
  180. }
  181. }
  182. else {
  183. try {
  184. const slackAppIntegrations = await SlackAppIntegration.find();
  185. settings.slackAppIntegrations = slackAppIntegrations;
  186. }
  187. catch (e) {
  188. errorMsg = 'Error occured in finding SlackAppIntegration entities.';
  189. errorCode = 'get-slackappintegration-failed';
  190. logger.error(errorMsg, e);
  191. }
  192. const proxyServerUri = settings.proxyServerUri;
  193. if (proxyServerUri != null && settings.slackAppIntegrations != null && settings.slackAppIntegrations.length > 0) {
  194. try {
  195. // key: slackAppIntegration.tokenGtoP, value: slackAppIntegration._id
  196. const tokenGtoPToSlackAppIntegrationId = {};
  197. settings.slackAppIntegrations.forEach((slackAppIntegration) => {
  198. tokenGtoPToSlackAppIntegrationId[slackAppIntegration.tokenGtoP] = slackAppIntegration._id;
  199. });
  200. const result = (await getConnectionStatusesFromProxy(Object.keys(tokenGtoPToSlackAppIntegrationId)));
  201. Object.entries(result.connectionStatuses).forEach(([tokenGtoP, connectionStatus]) => {
  202. connectionStatuses[tokenGtoPToSlackAppIntegrationId[tokenGtoP]] = connectionStatus;
  203. });
  204. }
  205. catch (e) {
  206. errorMsg = 'Something went wrong when retrieving information from Proxy Server.';
  207. errorCode = 'test-connection-failed';
  208. logger.error(errorMsg, e);
  209. }
  210. }
  211. }
  212. return res.apiv3({
  213. currentBotType, settings, connectionStatuses, errorMsg, errorCode,
  214. });
  215. });
  216. const handleBotTypeChanging = async(req, res, initializedBotType) => {
  217. await resetAllBotSettings(initializedBotType);
  218. crowi.slackIntegrationService.publishUpdatedMessage();
  219. // TODO Impl to delete AccessToken both of Proxy and GROWI when botType changes.
  220. const slackBotTypeParam = { slackBotType: crowi.configManager.getConfig('crowi', 'slackbot:currentBotType') };
  221. return res.apiv3({ slackBotTypeParam });
  222. };
  223. /**
  224. * @swagger
  225. *
  226. * /slack-integration-settings/bot-type/:
  227. * put:
  228. * tags: [botType]
  229. * operationId: putBotType
  230. * summary: /slack-integration/bot-type
  231. * description: Put botType setting.
  232. * requestBody:
  233. * required: true
  234. * content:
  235. * application/json:
  236. * schema:
  237. * $ref: '#/components/schemas/BotType'
  238. * responses:
  239. * 200:
  240. * description: Succeeded to put botType setting.
  241. */
  242. router.put('/bot-type', accessTokenParser, loginRequiredStrictly, adminRequired, csrf, validator.botType, apiV3FormValidator, async(req, res) => {
  243. const { currentBotType } = req.body;
  244. if (currentBotType == null) {
  245. return res.apiv3Err(new ErrorV3('The param \'currentBotType\' must be specified.', 'update-CustomBotSetting-failed'), 400);
  246. }
  247. try {
  248. await handleBotTypeChanging(req, res, currentBotType);
  249. }
  250. catch (error) {
  251. const msg = 'Error occured in updating Custom bot setting';
  252. logger.error('Error', error);
  253. return res.apiv3Err(new ErrorV3(msg, 'update-CustomBotSetting-failed'), 500);
  254. }
  255. });
  256. /**
  257. * @swagger
  258. *
  259. * /slack-integration/bot-type/:
  260. * delete:
  261. * tags: [botType]
  262. * operationId: deleteBotType
  263. * summary: /slack-integration/bot-type
  264. * description: Delete botType setting.
  265. * requestBody:
  266. * content:
  267. * application/json:
  268. * schema:
  269. * $ref: '#/components/schemas/BotType'
  270. * responses:
  271. * 200:
  272. * description: Succeeded to delete botType setting.
  273. */
  274. router.delete('/bot-type', accessTokenParser, loginRequiredStrictly, adminRequired, csrf, apiV3FormValidator, async(req, res) => {
  275. try {
  276. await handleBotTypeChanging(req, res, null);
  277. }
  278. catch (error) {
  279. const msg = 'Error occured in resetting all';
  280. logger.error('Error', error);
  281. return res.apiv3Err(new ErrorV3(msg, 'resetting-all-failed'), 500);
  282. }
  283. });
  284. /**
  285. * @swagger
  286. *
  287. * /slack-integration-settings/without-proxy/update-settings/:
  288. * put:
  289. * tags: [UpdateWithoutProxySettings]
  290. * operationId: putWithoutProxySettings
  291. * summary: update customBotWithoutProxy settings
  292. * description: Update customBotWithoutProxy setting.
  293. * responses:
  294. * 200:
  295. * description: Succeeded to put CustomBotWithoutProxy setting.
  296. */
  297. router.put('/without-proxy/update-settings', loginRequiredStrictly, adminRequired, csrf, async(req, res) => {
  298. const currentBotType = crowi.configManager.getConfig('crowi', 'slackbot:currentBotType');
  299. if (currentBotType !== SlackbotType.CUSTOM_WITHOUT_PROXY) {
  300. const msg = 'Not CustomBotWithoutProxy';
  301. return res.apiv3Err(new ErrorV3(msg, 'not-customBotWithoutProxy'), 400);
  302. }
  303. const { slackSigningSecret, slackBotToken } = req.body;
  304. const requestParams = {
  305. 'slackbot:withoutProxy:signingSecret': slackSigningSecret,
  306. 'slackbot:withoutProxy:botToken': slackBotToken,
  307. };
  308. try {
  309. await updateSlackBotSettings(requestParams);
  310. crowi.slackIntegrationService.publishUpdatedMessage();
  311. const customBotWithoutProxySettingParams = {
  312. slackSigningSecret: crowi.configManager.getConfig('crowi', 'slackbot:withoutProxy:signingSecret'),
  313. slackBotToken: crowi.configManager.getConfig('crowi', 'slackbot:withoutProxy:botToken'),
  314. };
  315. return res.apiv3({ customBotWithoutProxySettingParams });
  316. }
  317. catch (error) {
  318. const msg = 'Error occured in updating Custom bot setting';
  319. logger.error('Error', error);
  320. return res.apiv3Err(new ErrorV3(msg, 'update-CustomBotSetting-failed'), 500);
  321. }
  322. });
  323. /**
  324. * @swagger
  325. *
  326. * /slack-integration-settings/slack-app-integrations:
  327. * post:
  328. * tags: [SlackIntegration]
  329. * operationId: putSlackAppIntegrations
  330. * summary: /slack-integration
  331. * description: Generate SlackAppIntegrations
  332. * responses:
  333. * 200:
  334. * description: Succeeded to create slack app integration
  335. */
  336. router.post('/slack-app-integrations', loginRequiredStrictly, adminRequired, csrf, async(req, res) => {
  337. const SlackAppIntegrationRecordsNum = await SlackAppIntegration.countDocuments();
  338. if (SlackAppIntegrationRecordsNum >= 10) {
  339. const msg = 'Not be able to create more than 10 slack workspace integration settings';
  340. logger.error('Error', msg);
  341. return res.apiv3Err(new ErrorV3(msg, 'create-slackAppIntegeration-failed'), 500);
  342. }
  343. const { tokenGtoP, tokenPtoG } = await SlackAppIntegration.generateUniqueAccessTokens();
  344. try {
  345. const initialSupportedCommandsForBroadcastUse = new Map();
  346. const initialSupportedCommandsForSingleUse = new Map();
  347. const slackAppTokens = await SlackAppIntegration.create({
  348. tokenGtoP,
  349. tokenPtoG,
  350. permissionsForBroadcastUseCommands: initialSupportedCommandsForBroadcastUse,
  351. permissionsForSingleUseCommands: initialSupportedCommandsForSingleUse,
  352. });
  353. return res.apiv3(slackAppTokens, 200);
  354. }
  355. catch (error) {
  356. const msg = 'Error occurred during creating slack integration settings procedure';
  357. logger.error('Error', error);
  358. return res.apiv3Err(new ErrorV3(msg, 'creating-slack-integration-settings-procedure-failed'), 500);
  359. }
  360. });
  361. /**
  362. * @swagger
  363. *
  364. * /slack-integration-settings/slack-app-integrations/:id:
  365. * delete:
  366. * tags: [SlackIntegration]
  367. * operationId: deleteAccessTokens
  368. * summary: delete accessTokens
  369. * description: Delete accessTokens
  370. * responses:
  371. * 200:
  372. * description: Succeeded to delete access tokens for slack
  373. */
  374. router.delete('/slack-app-integrations/:id', validator.deleteIntegration, apiV3FormValidator, async(req, res) => {
  375. const SlackAppIntegration = mongoose.model('SlackAppIntegration');
  376. const { id } = req.params;
  377. try {
  378. const response = await SlackAppIntegration.findOneAndDelete({ _id: id });
  379. // update primary
  380. const countOfPrimary = await SlackAppIntegration.countDocuments({ isPrimary: true });
  381. if (countOfPrimary === 0) {
  382. await SlackAppIntegration.updateOne({}, { isPrimary: true });
  383. }
  384. return res.apiv3({ response });
  385. }
  386. catch (error) {
  387. const msg = 'Error occured in deleting access token for slack app tokens';
  388. logger.error('Error', error);
  389. return res.apiv3Err(new ErrorV3(msg, 'update-slackAppTokens-failed'), 500);
  390. }
  391. });
  392. router.put('/proxy-uri', loginRequiredStrictly, adminRequired, csrf, validator.proxyUri, apiV3FormValidator, async(req, res) => {
  393. const { proxyUri } = req.body;
  394. const requestParams = { 'slackbot:proxyUri': proxyUri };
  395. try {
  396. await updateSlackBotSettings(requestParams);
  397. crowi.slackIntegrationService.publishUpdatedMessage();
  398. return res.apiv3({});
  399. }
  400. catch (error) {
  401. const msg = 'Error occured in updating Custom bot setting';
  402. logger.error('Error', error);
  403. return res.apiv3Err(new ErrorV3(msg, 'delete-SlackAppIntegration-failed'), 500);
  404. }
  405. });
  406. /**
  407. * @swagger
  408. *
  409. * /slack-integration-settings/slack-app-integrations/:id/makeprimary:
  410. * put:
  411. * tags: [SlackIntegration]
  412. * operationId: makePrimary
  413. * summary: /slack-integration
  414. * description: Make SlackAppTokens primary
  415. * responses:
  416. * 200:
  417. * description: Succeeded to make it primary
  418. */
  419. // eslint-disable-next-line max-len
  420. router.put('/slack-app-integrations/:id/make-primary', loginRequiredStrictly, adminRequired, csrf, validator.makePrimary, apiV3FormValidator, async(req, res) => {
  421. const { id } = req.params;
  422. try {
  423. await SlackAppIntegration.bulkWrite([
  424. // unset isPrimary for others
  425. {
  426. updateMany: {
  427. filter: { _id: { $ne: id } },
  428. update: { $unset: { isPrimary: '' } },
  429. },
  430. },
  431. // set primary
  432. {
  433. updateOne: {
  434. filter: { _id: id },
  435. update: { isPrimary: true },
  436. },
  437. },
  438. ]);
  439. return res.apiv3();
  440. }
  441. catch (error) {
  442. const msg = 'Error occurred during making SlackAppIntegration primary';
  443. logger.error('Error', error);
  444. return res.apiv3Err(new ErrorV3(msg, 'making-primary-failed'), 500);
  445. }
  446. });
  447. /**
  448. * @swagger
  449. *
  450. * /slack-integration-settings/slack-app-integrations/:id/regenerate-tokens:
  451. * put:
  452. * tags: [SlackIntegration]
  453. * operationId: putRegenerateTokens
  454. * summary: /slack-integration
  455. * description: Regenerate SlackAppTokens
  456. * responses:
  457. * 200:
  458. * description: Succeeded to regenerate slack app tokens
  459. */
  460. // eslint-disable-next-line max-len
  461. router.put('/slack-app-integrations/:id/regenerate-tokens', loginRequiredStrictly, adminRequired, csrf, validator.regenerateTokens, apiV3FormValidator, async(req, res) => {
  462. const { id } = req.params;
  463. try {
  464. const { tokenGtoP, tokenPtoG } = await SlackAppIntegration.generateUniqueAccessTokens();
  465. const slackAppTokens = await SlackAppIntegration.findByIdAndUpdate(id, { tokenGtoP, tokenPtoG });
  466. return res.apiv3(slackAppTokens, 200);
  467. }
  468. catch (error) {
  469. const msg = 'Error occurred during regenerating slack app tokens';
  470. logger.error('Error', error);
  471. return res.apiv3Err(new ErrorV3(msg, 'regenerating-slackAppTokens-failed'), 500);
  472. }
  473. });
  474. /**
  475. * @swagger
  476. *
  477. * /slack-integration-settings/slack-app-integrations/:id/supported-commands:
  478. * put:
  479. * tags: [SlackIntegration]
  480. * operationId: putSupportedCommands
  481. * summary: /slack-integration-settings/:id/supported-commands
  482. * description: update supported commands
  483. * responses:
  484. * 200:
  485. * description: Succeeded to update supported commands
  486. */
  487. // eslint-disable-next-line max-len
  488. router.put('/slack-app-integrations/:id/supported-commands', loginRequiredStrictly, adminRequired, csrf, validator.updateSupportedCommands, apiV3FormValidator, async(req, res) => {
  489. const { supportedCommandsForBroadcastUse, supportedCommandsForSingleUse } = req.body;
  490. const { id } = req.params;
  491. const updatePermissionsForBroadcastUseCommands = new Map();
  492. const updatePermissionsForSingleUseCommands = new Map();
  493. supportedCommandsForBroadcastUse.forEach((commandName) => {
  494. updatePermissionsForBroadcastUseCommands.set(commandName, true);
  495. });
  496. supportedCommandsForSingleUse.forEach((commandName) => {
  497. updatePermissionsForSingleUseCommands.set(commandName, true);
  498. });
  499. try {
  500. const slackAppIntegration = await SlackAppIntegration.findByIdAndUpdate(
  501. id,
  502. {
  503. permissionsForBroadcastUseCommands: updatePermissionsForBroadcastUseCommands,
  504. permissionsForSingleUseCommands: updatePermissionsForSingleUseCommands,
  505. },
  506. { new: true },
  507. );
  508. await requestToProxyServer(
  509. slackAppIntegration.tokenGtoP,
  510. 'put',
  511. '/g2s/supported-commands',
  512. {
  513. permissionsForBroadcastUseCommands: slackAppIntegration.permissionsForBroadcastUseCommands,
  514. permissionsForSingleUseCommands: slackAppIntegration.permissionsForSingleUseCommands,
  515. },
  516. );
  517. return res.apiv3({ slackAppIntegration });
  518. }
  519. catch (error) {
  520. const msg = `Error occured in updating settings. Cause: ${error.message}`;
  521. logger.error('Error', error);
  522. return res.apiv3Err(new ErrorV3(msg, 'update-supported-commands-failed'), 500);
  523. }
  524. });
  525. /**
  526. * @swagger
  527. *
  528. * /slack-integration-settings/slack-app-integrations/:id/relation-test:
  529. * post:
  530. * tags: [botType]
  531. * operationId: postRelationTest
  532. * summary: Test relation
  533. * description: Delete botType setting.
  534. * responses:
  535. * 200:
  536. * description: Succeeded to delete botType setting.
  537. */
  538. // eslint-disable-next-line max-len
  539. router.post('/slack-app-integrations/:id/relation-test', loginRequiredStrictly, adminRequired, csrf, validator.relationTest, apiV3FormValidator, async(req, res) => {
  540. const currentBotType = crowi.configManager.getConfig('crowi', 'slackbot:currentBotType');
  541. if (currentBotType === SlackbotType.CUSTOM_WITHOUT_PROXY) {
  542. const msg = 'Not Proxy Type';
  543. return res.apiv3Err(new ErrorV3(msg, 'not-proxy-type'), 400);
  544. }
  545. const proxyUri = crowi.configManager.getConfig('crowi', 'slackbot:proxyUri');
  546. if (proxyUri == null) {
  547. return res.apiv3Err(new ErrorV3('Proxy URL is null.', 'not-proxy-Uri'), 400);
  548. }
  549. const { id } = req.params;
  550. let slackBotToken;
  551. try {
  552. const slackAppIntegration = await SlackAppIntegration.findOne({ _id: id });
  553. if (slackAppIntegration == null) {
  554. const msg = 'Could not find SlackAppIntegration by id';
  555. return res.apiv3Err(new ErrorV3(msg, 'find-slackAppIntegration-failed'), 400);
  556. }
  557. const result = await requestToProxyServer(
  558. slackAppIntegration.tokenGtoP,
  559. 'post',
  560. '/g2s/relation-test',
  561. {
  562. permissionsForBroadcastUseCommands: slackAppIntegration.permissionsForBroadcastUseCommands,
  563. permissionsForSingleUseCommands: slackAppIntegration.permissionsForSingleUseCommands,
  564. },
  565. );
  566. slackBotToken = result.slackBotToken;
  567. if (slackBotToken == null) {
  568. const msg = 'Could not find slackBotToken by relation';
  569. return res.apiv3Err(new ErrorV3(msg, 'find-slackBotToken-failed'), 400);
  570. }
  571. }
  572. catch (error) {
  573. logger.error('Error', error);
  574. return res.apiv3Err(new ErrorV3(`Error occured while testing. Cause: ${error.message}`, 'test-failed', error.stack));
  575. }
  576. const { channel } = req.body;
  577. const appSiteURL = crowi.configManager.getConfig('crowi', 'app:siteUrl');
  578. try {
  579. await sendSuccessMessage(slackBotToken, channel, appSiteURL);
  580. }
  581. catch (error) {
  582. return res.apiv3Err(new ErrorV3(`Error occured while sending message. Cause: ${error.message}`, 'send-message-failed', error.stack));
  583. }
  584. return res.apiv3();
  585. });
  586. /**
  587. * @swagger
  588. *
  589. * /slack-integration-settings/without-proxy/test:
  590. * post:
  591. * tags: [botType]
  592. * operationId: postTest
  593. * summary: test the connection
  594. * description: Test the connection with slack work space.
  595. * requestBody:
  596. * content:
  597. * application/json:
  598. * schema:
  599. * properties:
  600. * testChannel:
  601. * type: string
  602. * responses:
  603. * 200:
  604. * description: Succeeded to connect to slack work space.
  605. */
  606. router.post('/without-proxy/test', loginRequiredStrictly, adminRequired, csrf, validator.slackChannel, apiV3FormValidator, async(req, res) => {
  607. const currentBotType = crowi.configManager.getConfig('crowi', 'slackbot:currentBotType');
  608. if (currentBotType !== SlackbotType.CUSTOM_WITHOUT_PROXY) {
  609. const msg = 'Select Without Proxy Type';
  610. return res.apiv3Err(new ErrorV3(msg, 'select-not-proxy-type'), 400);
  611. }
  612. const slackBotToken = crowi.configManager.getConfig('crowi', 'slackbot:withoutProxy:botToken');
  613. const status = await getConnectionStatus(slackBotToken);
  614. if (status.error != null) {
  615. return res.apiv3Err(new ErrorV3(`Error occured while getting connection. ${status.error}`, 'send-message-failed'));
  616. }
  617. const { channel } = req.body;
  618. const appSiteURL = crowi.configManager.getConfig('crowi', 'app:siteUrl');
  619. try {
  620. await sendSuccessMessage(slackBotToken, channel, appSiteURL);
  621. }
  622. catch (error) {
  623. return res.apiv3Err(new ErrorV3(`Error occured while sending message. Cause: ${error.message}`, 'send-message-failed', error.stack));
  624. }
  625. return res.apiv3();
  626. });
  627. return router;
  628. };