Procházet zdrojové kódy

Merge branch 'feat/growi-bot' into feat/GW-5459-test

深瀬スティーヴン před 5 roky
rodič
revize
2df2e6a19a

+ 2 - 0
resource/locales/en_US/translation.json

@@ -110,6 +110,8 @@
   "Customize": "Customize",
   "Notification Settings": "Notification Settings",
   "slack_integration": "Slack Integration",
+  "External_Notification": "External Notification",
+  "Legacy_External_Notification": "Legacy External Notification",
   "User_Management": "User Management",
   "external_account_management": "External Account Management",
   "UserGroup Management": "UserGroup Management",

+ 2 - 0
resource/locales/ja_JP/translation.json

@@ -111,6 +111,8 @@
   "Customize": "カスタマイズ",
   "Notification Settings": "通知設定",
   "slack_integration": "Slack連携",
+  "External_Notification": "外部ツールへの通知",
+  "Legacy_External_Notification": "外部ツールへの通知 (レガシー)",
   "User_Management": "ユーザー管理",
   "external_account_management": "外部アカウント管理",
   "UserGroup Management": "グループ管理",

+ 2 - 0
resource/locales/zh_CN/translation.json

@@ -119,6 +119,8 @@
 	"Customize": "页面定制",
 	"Notification Settings": "通知设置",
   "slack_integration": "Slack一体化",
+  "External_Notification": "外部通知",
+  "Legacy_External_Notification": "旧版外部通知",
 	"User_Management": "用户管理",
 	"external_account_management": "外部账户管理",
 	"UserGroup Management": "用户组管理",

+ 15 - 12
src/client/js/components/Admin/Common/AdminNavigation.jsx

@@ -20,7 +20,9 @@ const AdminNavigation = (props) => {
       case 'customize':         return <><i className="icon-fw icon-wrench"></i>          { t('Customize') }</>;
       case 'importer':          return <><i className="icon-fw icon-cloud-upload"></i>    { t('Import Data') }</>;
       case 'export':            return <><i className="icon-fw icon-cloud-download"></i>  { t('Export Archive Data') }</>;
-      case 'notification':      return <><i className="icon-fw icon-bell"></i>            { t('Notification Settings') }</>;
+      case 'notification':      return <><i className="icon-fw icon-bell"></i>            { t('External_Notification') }</>;
+      // TODO change icon for legacy-external-notification by GW-5466
+      case 'legacy-external-notification':  return <> <i className="icon-fw icon-bell"></i>{ t('Legacy_External_Notification') }</>;
       case 'slack-integration': return <><i className="icon-fw icon-paper-plane"></i>     { t('Slack Integration') }</>;
       case 'users':             return <><i className="icon-fw icon-user"></i>            { t('User_Management') }</>;
       case 'user-groups':       return <><i className="icon-fw icon-people"></i>          { t('UserGroup Management') }</>;
@@ -54,18 +56,19 @@ const AdminNavigation = (props) => {
   const getListGroupItemOrDropdownItemList = (isListGroupItems) => {
     return (
       <>
-        <MenuLink menu="home"              isListGroupItems isActive={pathname === '/admin'} isRoot />
-        <MenuLink menu="app"               isListGroupItems isActive={isActiveMenu('/app')} />
-        <MenuLink menu="security"          isListGroupItems isActive={isActiveMenu('/security')} />
-        <MenuLink menu="markdown"          isListGroupItems isActive={isActiveMenu('/markdown')} />
-        <MenuLink menu="customize"         isListGroupItems isActive={isActiveMenu('/customize')} />
-        <MenuLink menu="importer"          isListGroupItems isActive={isActiveMenu('/importer')} />
-        <MenuLink menu="export"            isListGroupItems isActive={isActiveMenu('/export')} />
-        <MenuLink menu="notification"      isListGroupItems isActive={isActiveMenu('/notification') || isActiveMenu('/global-notification')} />
+        <MenuLink menu="home"         isListGroupItems isActive={pathname === '/admin'} isRoot />
+        <MenuLink menu="app"          isListGroupItems isActive={isActiveMenu('/app')} />
+        <MenuLink menu="security"     isListGroupItems isActive={isActiveMenu('/security')} />
+        <MenuLink menu="markdown"     isListGroupItems isActive={isActiveMenu('/markdown')} />
+        <MenuLink menu="customize"    isListGroupItems isActive={isActiveMenu('/customize')} />
+        <MenuLink menu="importer"     isListGroupItems isActive={isActiveMenu('/importer')} />
+        <MenuLink menu="export"       isListGroupItems isActive={isActiveMenu('/export')} />
+        <MenuLink menu="notification" isListGroupItems isActive={isActiveMenu('/notification') || isActiveMenu('/global-notification')} />
+        <MenuLink menu="legacy-external-notification" isListGroupItems isActive={isActiveMenu('/legacy-external-notification')} />
         <MenuLink menu="slack-integration" isListGroupItems isActive={isActiveMenu('/slack-integration')} />
-        <MenuLink menu="users"             isListGroupItems isActive={isActiveMenu('/users')} />
-        <MenuLink menu="user-groups"       isListGroupItems isActive={isActiveMenu('/user-groups')} />
-        <MenuLink menu="search"            isListGroupItems isActive={isActiveMenu('/search')} />
+        <MenuLink menu="users"        isListGroupItems isActive={isActiveMenu('/users')} />
+        <MenuLink menu="user-groups"  isListGroupItems isActive={isActiveMenu('/user-groups')} />
+        <MenuLink menu="search"       isListGroupItems isActive={isActiveMenu('/search')} />
       </>
     );
   };

+ 1 - 1
src/client/js/components/Admin/Notification/GlobalNotification.jsx

@@ -26,7 +26,7 @@ class GlobalNotification extends React.Component {
 
     try {
       await adminNotificationContainer.updateGlobalNotificationForPages();
-      toastSuccess(t('toaster.update_successed', { target: t('Notification Settings') }));
+      toastSuccess(t('toaster.update_successed', { target: t('External_Notification') }));
     }
     catch (err) {
       toastError(err);

+ 5 - 0
src/server/routes/admin.js

@@ -223,6 +223,11 @@ module.exports = function(crowi, app) {
     return res.render('admin/external-accounts');
   };
 
+  actions.legacyExternalNotification = {};
+  actions.legacyExternalNotification = function(req, res) {
+    return res.render('admin/legacy-external-notification');
+  };
+
 
   actions.userGroup = {};
   actions.userGroup.index = function(req, res) {

+ 1 - 0
src/server/routes/apiv3/index.js

@@ -47,6 +47,7 @@ module.exports = (crowi) => {
   router.use('/attachment', require('./attachment')(crowi));
 
   router.use('/slack-bot', require('./slack-bot')(crowi));
+  router.use('/slack-integration', require('./slack-integration')(crowi));
   router.use('/staffs', require('./staffs')(crowi));
 
   return router;

+ 140 - 0
src/server/routes/apiv3/slack-integration.js

@@ -0,0 +1,140 @@
+const loggerFactory = require('@alias/logger');
+
+const logger = loggerFactory('growi:routes:apiv3:notification-setting');
+const express = require('express');
+const { body } = require('express-validator');
+const ErrorV3 = require('../../models/vo/error-apiv3');
+
+const router = express.Router();
+
+/**
+ * @swagger
+ *  tags:
+ *    name: SlackIntegration
+ */
+
+/**
+ * @swagger
+ *
+ *  components:
+ *    schemas:
+ *      CustomBotNonProxy:
+ *        description: CustomBotNonProxy
+ *        type: object
+ *        properties:
+ *          slackSigningSecret:
+ *            type: string
+ *          slackBotToken:
+ *            type: string
+ *          botType:
+ *            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 validator = {
+    CusotmBotNonProxy: [
+      body('slackSigningSecret').isString(),
+      body('slackBotToken').isString(),
+      body('botType').isString(),
+    ],
+  };
+
+  async function updateSlackBotSettings(params) {
+    const { configManager } = crowi;
+    // update config without publishing S2sMessage
+    return configManager.updateConfigsInTheSameNamespace('crowi', params, true);
+  }
+
+  /**
+   * @swagger
+   *
+   *    /slack-integration/:
+   *      get:
+   *        tags: [SlackBotSettingParams]
+   *        operationId: getSlackBotSettingParams
+   *        summary: /slack-integration
+   *        description: Get slackBot setting params.
+   *        responses:
+   *          200:
+   *            description: Succeeded to get slackBot setting params.
+   */
+  router.get('/', accessTokenParser, loginRequiredStrictly, adminRequired, async(req, res) => {
+
+    const slackBotSettingParams = {
+      slackBotType: crowi.configManager.getConfig('crowi', 'slackbot:type'),
+      // TODO impl when creating official bot
+      officialBotSettings: {
+        // TODO impl this after GW-4939
+        // AccessToken: "tempaccessdatahogehoge",
+      },
+      cusotmBotNonProxySettings: {
+        // TODO impl this after GW-4939
+        // AccessToken: "tempaccessdatahogehoge",
+        slackSigningSecret: crowi.configManager.getConfig('crowi', 'slackbot:signingSecret'),
+        slackBotToken: crowi.configManager.getConfig('crowi', 'slackbot:token'),
+      },
+      // TODO imple when creating with proxy
+      cusotmBotWithProxySettings: {
+        // TODO impl this after GW-4939
+        // AccessToken: "tempaccessdatahogehoge",
+      },
+    };
+    return res.apiv3({ slackBotSettingParams });
+  });
+
+  /**
+   * @swagger
+   *
+   *    /slack-integration/custom-bot-non-proxy/:
+   *      put:
+   *        tags: [CustomBotNonProxy]
+   *        operationId: putCustomBotNonProxy
+   *        summary: /slack-integration/custom-bot-non-proxy
+   *        description: Put customBotNonProxy setting.
+   *        requestBody:
+   *          required: true
+   *          content:
+   *            application/json:
+   *              schema:
+   *                $ref: '#/components/schemas/CustomBotNonProxy'
+   *        responses:
+   *           200:
+   *             description: Succeeded to put CustomBotNonProxy setting.
+   */
+  router.put('/custom-bot-non-proxy',
+    accessTokenParser, loginRequiredStrictly, adminRequired, csrf, validator.CusotmBotNonProxy, apiV3FormValidator, async(req, res) => {
+      const { slackSigningSecret, slackBotToken, botType } = req.body;
+
+      const requestParams = {
+        'slackbot:signingSecret': slackSigningSecret,
+        'slackbot:token': slackBotToken,
+        'slackbot:type': botType,
+      };
+
+      try {
+        await updateSlackBotSettings(requestParams);
+        // TODO Impl to delete AccessToken both of Proxy and GROWI when botType changes.
+        const customBotNonProxySettingParams = {
+          slackSigningSecret: crowi.configManager.getConfig('crowi', 'slackbot:signingSecret'),
+          slackBotToken: crowi.configManager.getConfig('crowi', 'slackbot:token'),
+          slackBotType: crowi.configManager.getConfig('crowi', 'slackbot:type'),
+        };
+        return res.apiv3({ customBotNonProxySettingParams });
+      }
+      catch (error) {
+        const msg = 'Error occured in updating Custom bot setting';
+        logger.error('Error', error);
+        return res.apiv3Err(new ErrorV3(msg, 'update-CustomBotSetting-failed'));
+      }
+    });
+
+  return router;
+};

+ 1 - 0
src/server/routes/index.js

@@ -93,6 +93,7 @@ module.exports = function(crowi, app) {
   app.get('/admin/notification/slackSetting/disconnect' , loginRequiredStrictly , adminRequired , admin.notification.disconnectFromSlack);
   app.get('/admin/global-notification/new'              , loginRequiredStrictly , adminRequired , admin.globalNotification.detail);
   app.get('/admin/global-notification/:id'              , loginRequiredStrictly , adminRequired , admin.globalNotification.detail);
+  app.get('/admin/legacy-external-notification'         , loginRequiredStrictly , adminRequired,  admin.legacyExternalNotification);
 
   app.get('/admin/users'                                , loginRequiredStrictly , adminRequired , admin.user.index);
 

+ 6 - 0
src/server/service/config-loader.js

@@ -410,6 +410,12 @@ const ENV_VAR_NAME_TO_CONFIG_INFO = {
     type:    TYPES.STRING,
     default: null,
   },
+  SLACK_BOT_TYPE: {
+    ns:      'crowi',
+    key:     'slackbot:type', // eg. official || custom-non-proxy || custom-with-proxy
+    type:    TYPES.STRING,
+    default: null,
+  },
 };
 
 class ConfigLoader {

+ 2 - 2
src/server/views/admin/global-notification-detail.html

@@ -1,9 +1,9 @@
 {% extends '../layout/admin.html' %}
 
-{% block html_title %}{{ customizeService.generateCustomTitleForFixedPageName(t('Notification Settings')) }}{% endblock %}
+{% block html_title %}{{ customizeService.generateCustomTitleForFixedPageName(t('External_Notification')) }}{% endblock %}
 
 {% block content_header %}
-<h1 class="title">{{ t('Notification Settings') }}</h1>
+<h1 class="title">{{ t('External_Notification') }}</h1>
 {% endblock %}
 
 {% block content_main %}

+ 12 - 0
src/server/views/admin/legacy-external-notification.html

@@ -0,0 +1,12 @@
+{% extends '../layout/admin.html' %}
+
+{% block html_title %}{{ customizeService.generateCustomTitleForFixedPageName(t('Legacy_External_Notification')) }}{% endblock %}
+
+{% block content_header %}
+<h1 class="title">{{ t('Legacy_External_Notification') }}</h1>
+{% endblock %}
+
+{% block content_main %}
+<!-- TODO: move contents from notification settings by GW-5467  -->
+{% endblock content_main %}
+

+ 2 - 2
src/server/views/admin/notification.html

@@ -1,9 +1,9 @@
 {% extends '../layout/admin.html' %}
 
-{% block html_title %}{{ customizeService.generateCustomTitleForFixedPageName(t('Notification Settings')) }}{% endblock %}
+{% block html_title %}{{ customizeService.generateCustomTitleForFixedPageName(t('External_Notification')) }}{% endblock %}
 
 {% block content_header %}
-<h1 class="title">{{ t('Notification Settings') }}</h1>
+<h1 class="title">{{ t('External_Notification') }}</h1>
 {% endblock %}
 
 {% block content_main %}