| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527 |
- const mongoose = require('mongoose');
- const express = require('express');
- const { body, query } = require('express-validator');
- const axios = require('axios');
- const urljoin = require('url-join');
- const loggerFactory = require('@alias/logger');
- const { getConnectionStatuses, relationTestToSlack } = require('@growi/slack');
- const ErrorV3 = require('../../models/vo/error-apiv3');
- const logger = loggerFactory('growi:routes:apiv3:slack-integration-settings');
- const router = express.Router();
- /**
- * @swagger
- * tags:
- * name: SlackIntegrationSettings
- */
- /**
- * @swagger
- *
- * components:
- * schemas:
- * BotType:
- * description: BotType
- * properties:
- * currentBotType:
- * type: string
- * SlackIntegration:
- * description: SlackIntegration
- * type: object
- * properties:
- * currentBotType:
- * type: string
- */
- module.exports = (crowi) => {
- const accessTokenParser = require('../../middlewares/access-token-parser')(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 SlackAppIntegration = mongoose.model('SlackAppIntegration');
- const validator = {
- BotType: [
- body('currentBotType').isString(),
- ],
- SlackIntegration: [
- body('currentBotType')
- .isIn(['officialBot', 'customBotWithoutProxy', 'customBotWithProxy']),
- ],
- proxyUri: [
- body('proxyUri').if(value => value !== '').trim().matches(/^(https?:\/\/)/)
- .isURL({ require_tld: false }),
- ],
- AccessTokens: [
- query('tokenGtoP').trim().not().isEmpty()
- .isString()
- .isLength({ min: 1 }),
- query('tokenPtoG').trim().not().isEmpty()
- .isString()
- .isLength({ min: 1 }),
- ],
- RelationTest: [
- body('slackappintegrationsId').isMongoId(),
- ],
- SlackChannel: [
- body('channel').trim().not().isEmpty()
- .isString(),
- ],
- };
- async function resetAllBotSettings() {
- const params = {
- 'slackbot:currentBotType': null,
- 'slackbot:signingSecret': null,
- 'slackbot:token': null,
- 'slackbot:proxyServerUri': null,
- };
- const { configManager } = crowi;
- // update config without publishing S2sMessage
- return configManager.updateConfigsInTheSameNamespace('crowi', params, true);
- }
- async function updateSlackBotSettings(params) {
- const { configManager } = crowi;
- // update config without publishing S2sMessage
- return configManager.updateConfigsInTheSameNamespace('crowi', params, true);
- }
- async function getConnectionStatusesFromProxy(tokens) {
- const csv = tokens.join(',');
- const proxyUri = crowi.configManager.getConfig('crowi', 'slackbot:proxyServerUri');
- const result = await axios.get(urljoin(proxyUri, '/g2s/connection-status'), {
- headers: {
- 'x-growi-gtop-tokens': csv,
- },
- });
- return result.data;
- }
- async function postRelationTest(token) {
- const proxyUri = crowi.configManager.getConfig('crowi', 'slackbot:proxyServerUri');
- const result = await axios.get(urljoin(proxyUri, '/g2s/relation-test'), {
- headers: {
- 'x-growi-gtop-tokens': token,
- },
- });
- return result.data;
- }
- /**
- * @swagger
- *
- * /slack-integration-settings/:
- * get:
- * tags: [SlackBotSettingParams]
- * operationId: getSlackBotSettingParams
- * summary: get /slack-integration
- * description: Get current settings and connection statuses.
- * responses:
- * 200:
- * description: Succeeded to get info.
- */
- router.get('/', accessTokenParser, loginRequiredStrictly, adminRequired, async(req, res) => {
- const { configManager } = crowi;
- const currentBotType = configManager.getConfig('crowi', 'slackbot:currentBotType');
- // retrieve settings
- const settings = {};
- if (currentBotType === 'customBotWithoutProxy') {
- settings.slackSigningSecretEnvVars = configManager.getConfigFromEnvVars('crowi', 'slackbot:signingSecret');
- settings.slackBotTokenEnvVars = configManager.getConfigFromEnvVars('crowi', 'slackbot:token');
- settings.slackSigningSecret = configManager.getConfig('crowi', 'slackbot:signingSecret');
- settings.slackBotToken = configManager.getConfig('crowi', 'slackbot:token');
- }
- else {
- settings.proxyServerUri = crowi.configManager.getConfig('crowi', 'slackbot:proxyServerUri');
- settings.proxyUriEnvVars = configManager.getConfigFromEnvVars('crowi', 'slackbot:proxyServerUri');
- }
- // retrieve connection statuses
- let connectionStatuses;
- if (currentBotType == null) {
- // TODO imple null action
- }
- else if (currentBotType === 'customBotWithoutProxy') {
- const token = settings.slackBotToken;
- // check the token is not null
- if (token != null) {
- try {
- connectionStatuses = await getConnectionStatuses([token]);
- }
- catch (error) {
- const msg = 'Error occured in getting connection statuses';
- logger.error('Error', error);
- return res.apiv3Err(new ErrorV3(msg, 'get-connection-failed'), 500);
- }
- }
- }
- else {
- // const proxyServerUri = settings.proxyServerUri;
- // if (proxyServerUri != null) {
- try {
- const slackAppIntegrations = await SlackAppIntegration.find();
- settings.slackAppIntegrations = slackAppIntegrations;
- if (slackAppIntegrations.length > 0) {
- const tokenGtoPs = slackAppIntegrations.map(slackAppIntegration => slackAppIntegration.tokenGtoP);
- connectionStatuses = (await getConnectionStatusesFromProxy(tokenGtoPs)).connectionStatuses;
- }
- }
- catch (error) {
- const msg = 'Error occured in getting connection statuses';
- logger.error('Error', error);
- return res.apiv3Err(new ErrorV3(msg, 'get-connection-failed'), 500);
- }
- // }
- }
- return res.apiv3({ currentBotType, settings, connectionStatuses });
- });
- /**
- * @swagger
- *
- * /slack-integration-settings/:
- * put:
- * tags: [SlackIntegration]
- * operationId: putSlackIntegration
- * summary: put /slack-integration
- * description: Put SlackIntegration setting.
- * requestBody:
- * required: true
- * content:
- * application/json:
- * schema:
- * $ref: '#/components/schemas/SlackIntegration'
- * responses:
- * 200:
- * description: Succeeded to put Slack Integration setting.
- */
- router.put('/', accessTokenParser, loginRequiredStrictly, adminRequired, csrf, validator.SlackIntegration, apiV3FormValidator, async(req, res) => {
- const { currentBotType } = req.body;
- const requestParams = {
- 'slackbot:currentBotType': currentBotType,
- };
- try {
- await updateSlackBotSettings(requestParams);
- crowi.slackBotService.publishUpdatedMessage();
- const slackIntegrationSettingsParams = {
- currentBotType: crowi.configManager.getConfig('crowi', 'slackbot:currentBotType'),
- };
- return res.apiv3({ slackIntegrationSettingsParams });
- }
- catch (error) {
- const msg = 'Error occured in updating Slack bot setting';
- logger.error('Error', error);
- return res.apiv3Err(new ErrorV3(msg, 'update-SlackIntegrationSetting-failed'), 500);
- }
- });
- /**
- * @swagger
- *
- * /slack-integration-settings/bot-type/:
- * put:
- * tags: [botType]
- * operationId: putBotType
- * summary: /slack-integration/bot-type
- * description: Put botType setting.
- * requestBody:
- * required: true
- * content:
- * application/json:
- * schema:
- * $ref: '#/components/schemas/BotType'
- * responses:
- * 200:
- * description: Succeeded to put botType setting.
- */
- router.put('/bot-type', accessTokenParser, loginRequiredStrictly, adminRequired, csrf, validator.BotType, apiV3FormValidator, async(req, res) => {
- const { currentBotType } = req.body;
- await resetAllBotSettings();
- const requestParams = { 'slackbot:currentBotType': currentBotType };
- try {
- await updateSlackBotSettings(requestParams);
- crowi.slackBotService.publishUpdatedMessage();
- // TODO Impl to delete AccessToken both of Proxy and GROWI when botType changes.
- const slackBotTypeParam = { slackBotType: crowi.configManager.getConfig('crowi', 'slackbot:currentBotType') };
- return res.apiv3({ slackBotTypeParam });
- }
- catch (error) {
- const msg = 'Error occured in updating Custom bot setting';
- logger.error('Error', error);
- return res.apiv3Err(new ErrorV3(msg, 'update-CustomBotSetting-failed'), 500);
- }
- });
- /**
- * @swagger
- *
- * /slack-integration/bot-type/:
- * delete:
- * tags: [botType]
- * operationId: deleteBotType
- * summary: /slack-integration/bot-type
- * description: Delete botType setting.
- * requestBody:
- * content:
- * application/json:
- * schema:
- * $ref: '#/components/schemas/BotType'
- * responses:
- * 200:
- * description: Succeeded to delete botType setting.
- */
- router.delete('/bot-type', accessTokenParser, loginRequiredStrictly, adminRequired, csrf, apiV3FormValidator, async(req, res) => {
- await resetAllBotSettings();
- const params = { 'slackbot:currentBotType': null };
- try {
- await updateSlackBotSettings(params);
- crowi.slackBotService.publishUpdatedMessage();
- // TODO Impl to delete AccessToken both of Proxy and GROWI when botType changes.
- const slackBotTypeParam = { slackBotType: crowi.configManager.getConfig('crowi', 'slackbot:currentBotType') };
- return res.apiv3({ slackBotTypeParam });
- }
- catch (error) {
- const msg = 'Error occured in updating Custom bot setting';
- logger.error('Error', error);
- return res.apiv3Err(new ErrorV3(msg, 'update-CustomBotSetting-failed'), 500);
- }
- });
- /**
- * @swagger
- *
- * /slack-integration-settings/without-proxy/update-settings/:
- * put:
- * tags: [UpdateWithoutProxySettings]
- * operationId: putWithoutProxySettings
- * summary: update customBotWithoutProxy settings
- * description: Update customBotWithoutProxy setting.
- * responses:
- * 200:
- * description: Succeeded to put CustomBotWithoutProxy setting.
- */
- router.put('/without-proxy/update-settings', loginRequiredStrictly, adminRequired, csrf, async(req, res) => {
- const currentBotType = crowi.configManager.getConfig('crowi', 'slackbot:currentBotType');
- if (currentBotType !== 'customBotWithoutProxy') {
- const msg = 'Not CustomBotWithoutProxy';
- return res.apiv3Err(new ErrorV3(msg, 'not-customBotWithoutProxy'), 400);
- }
- const { slackSigningSecret, slackBotToken } = req.body;
- const requestParams = {
- 'slackbot:signingSecret': slackSigningSecret,
- 'slackbot:token': slackBotToken,
- };
- try {
- await updateSlackBotSettings(requestParams);
- crowi.slackBotService.publishUpdatedMessage();
- const customBotWithoutProxySettingParams = {
- slackSigningSecret: crowi.configManager.getConfig('crowi', 'slackbot:signingSecret'),
- slackBotToken: crowi.configManager.getConfig('crowi', 'slackbot:token'),
- };
- return res.apiv3({ customBotWithoutProxySettingParams });
- }
- catch (error) {
- const msg = 'Error occured in updating Custom bot setting';
- logger.error('Error', error);
- return res.apiv3Err(new ErrorV3(msg, 'update-CustomBotSetting-failed'), 500);
- }
- });
- /**
- * @swagger
- *
- * /slack-integration-settings/access-tokens:
- * put:
- * tags: [SlackIntegration]
- * operationId: putAccessTokens
- * summary: /slack-integration
- * description: Generate accessTokens
- * responses:
- * 200:
- * description: Succeeded to update access tokens for slack
- */
- router.put('/access-tokens', loginRequiredStrictly, adminRequired, csrf, async(req, res) => {
- let checkTokens;
- let tokenGtoP;
- let tokenPtoG;
- let generateTokens;
- do {
- generateTokens = SlackAppIntegration.generateAccessToken();
- tokenGtoP = generateTokens[0];
- tokenPtoG = generateTokens[1];
- // eslint-disable-next-line no-await-in-loop
- checkTokens = await SlackAppIntegration.findOne({ $or: [{ tokenGtoP }, { tokenPtoG }] });
- } while (checkTokens != null);
- try {
- const slackAppTokens = await SlackAppIntegration.create({ tokenGtoP, tokenPtoG });
- return res.apiv3(slackAppTokens, 200);
- }
- catch (error) {
- const msg = 'Error occured in updating access token for slack app tokens';
- logger.error('Error', error);
- return res.apiv3Err(new ErrorV3(msg, 'update-slackAppTokens-failed'), 500);
- }
- });
- /**
- * @swagger
- *
- * /slack-integration-settings/slack-app-integration:
- * delete:
- * tags: [SlackIntegration]
- * operationId: deleteAccessTokens
- * summary: delete accessTokens
- * description: Delete accessTokens
- * responses:
- * 200:
- * description: Succeeded to delete access tokens for slack
- */
- router.delete('/slack-app-integration', validator.AccessTokens, apiV3FormValidator, async(req, res) => {
- const SlackAppIntegration = mongoose.model('SlackAppIntegration');
- const { tokenGtoP, tokenPtoG } = req.query;
- try {
- const response = await SlackAppIntegration.findOneAndDelete({ tokenGtoP, tokenPtoG });
- return res.apiv3({ response });
- }
- catch (error) {
- const msg = 'Error occured in deleting access token for slack app tokens';
- logger.error('Error', error);
- return res.apiv3Err(new ErrorV3(msg, 'update-slackAppTokens-failed'), 500);
- }
- });
- router.put('/proxy-uri', loginRequiredStrictly, adminRequired, csrf, validator.proxyUri, apiV3FormValidator, async(req, res) => {
- const { proxyUri } = req.body;
- const requestParams = { 'slackbot:proxyServerUri': proxyUri };
- try {
- await updateSlackBotSettings(requestParams);
- crowi.slackBotService.publishUpdatedMessage();
- return res.apiv3({});
- }
- catch (error) {
- const msg = 'Error occured in updating Custom bot setting';
- logger.error('Error', error);
- return res.apiv3Err(new ErrorV3(msg, 'update-CustomBotSetting-failed'), 500);
- }
- });
- /**
- * @swagger
- *
- * /slack-integration-settings/with-proxy/relation-test:
- * post:
- * tags: [botType]
- * operationId: postRelationTest
- * summary: /slack-integration/bot-type
- * description: Delete botType setting.
- * requestBody:
- * content:
- * application/json:
- * schema:
- * properties:
- * slackappintegrationsId:
- * type: string
- * responses:
- * 200:
- * description: Succeeded to delete botType setting.
- */
- router.post('/with-proxy/relation-test', loginRequiredStrictly, adminRequired, csrf, validator.RelationTest, apiV3FormValidator, async(req, res) => {
- const currentBotType = crowi.configManager.getConfig('crowi', 'slackbot:currentBotType');
- if (currentBotType === 'customBotWithoutProxy') {
- const msg = 'Not Proxy Type';
- return res.apiv3Err(new ErrorV3(msg, 'not-proxy-type'), 400);
- }
- const { slackappintegrationsId } = req.body;
- try {
- const slackAppIntegration = await SlackAppIntegration.findOne({ _id: slackappintegrationsId });
- if (slackAppIntegration == null) {
- const msg = 'Could not find SlackAppIntegration by id';
- return res.apiv3Err(new ErrorV3(msg, 'find-slackAppIntegration-failed'), 400);
- }
- const response = await postRelationTest(slackAppIntegration.tokenGtoP);
- return res.apiv3({ response });
- }
- catch (error) {
- const msg = 'Error occured in updating Custom bot setting';
- logger.error('Error', error);
- return res.apiv3Err(new ErrorV3(msg, 'update-CustomBotSetting-failed'), 500);
- }
- });
- /**
- * @swagger
- *
- * /slack-integration-settings/without-proxy/test:
- * post:
- * tags: [botType]
- * operationId: postTest
- * summary: test the connection
- * description: Test the connection with slack work space.
- * requestBody:
- * content:
- * application/json:
- * schema:
- * properties:
- * testChannel:
- * type: string
- * responses:
- * 200:
- * description: Succeeded to connect to slack work space.
- */
- router.post('/without-proxy/test', loginRequiredStrictly, adminRequired, csrf, validator.SlackChannel, apiV3FormValidator, async(req, res) => {
- const currentBotType = crowi.configManager.getConfig('crowi', 'slackbot:currentBotType');
- if (currentBotType !== 'customBotWithoutProxy') {
- const msg = 'Select Without Proxy Type';
- return res.apiv3Err(new ErrorV3(msg, 'select-not-proxy-type'), 400);
- }
- // TODO impl req.body at GW-5998
- // const { channel } = req.body;
- const slackBotToken = crowi.configManager.getConfig('crowi', 'slackbot:token');
- try {
- await relationTestToSlack(slackBotToken);
- // TODO impl return response after imple 5996, 6002
- }
- catch (error) {
- logger.error('Error', error);
- return res.apiv3Err(new ErrorV3(`Error occured while testing. Cause: ${error.message}`, 'test-failed', error.stack));
- }
- });
- return router;
- };
|