Просмотр исходного кода

Merge remote-tracking branch 'origin/feat/growi-bot' into feat/growi-bot-proxy

Yuki Takei 5 лет назад
Родитель
Сommit
867c4d6ceb

+ 10 - 0
resource/locales/en_US/admin/admin.json

@@ -252,6 +252,16 @@
     "download": "Download",
     "download": "Download",
     "delete": "Delete"
     "delete": "Delete"
   },
   },
+  "slack_integration": {
+    "access_token_settings": {
+      "discard": "Discard",
+      "generate": "Generate"
+    },
+    "custom_bot_non_proxy_settings": "Custom bot (non-proxy) Settings",
+    "non_proxy": {
+      "create_bot": "Create Bot"
+    }
+  },
   "user_management": {
   "user_management": {
     "invite_users": "Invite new users",
     "invite_users": "Invite new users",
     "click_twice_same_checkbox": "You should check at least one checkbox.",
     "click_twice_same_checkbox": "You should check at least one checkbox.",

+ 3 - 1
resource/locales/en_US/translation.json

@@ -108,8 +108,10 @@
   "Site URL settings": "Site URL settings",
   "Site URL settings": "Site URL settings",
   "Markdown Settings": "Markdown Settings",
   "Markdown Settings": "Markdown Settings",
   "Customize": "Customize",
   "Customize": "Customize",
+  "Notification Settings": "Notification Settings",
+  "slack_integration": "Slack Integration",
   "External_Notification": "External Notification",
   "External_Notification": "External Notification",
-  "Legacy_External_Notification": "Legacy External Notification",
+  "Legacy_Slack_Integration": "Legacy Slack Integration",
   "User_Management": "User Management",
   "User_Management": "User Management",
   "external_account_management": "External Account Management",
   "external_account_management": "External Account Management",
   "UserGroup Management": "UserGroup Management",
   "UserGroup Management": "UserGroup Management",

+ 10 - 0
resource/locales/ja_JP/admin/admin.json

@@ -250,6 +250,16 @@
     "page_skip": "既に GROWI 側に同名のページが存在する場合、そのページはスキップされます",
     "page_skip": "既に GROWI 側に同名のページが存在する場合、そのページはスキップされます",
     "Directory_hierarchy_tag": "ディレクトリ階層タグ"
     "Directory_hierarchy_tag": "ディレクトリ階層タグ"
   },
   },
+  "slack_integration": {
+    "access_token_settings": {
+      "discard": "破棄",
+      "generate": "発行"
+    },
+    "custom_bot_non_proxy_settings": "Custom bot (non-proxy) 設定",
+    "non_proxy": {
+      "create_bot": "Bot を作成する"
+    }
+  },
   "user_management": {
   "user_management": {
     "invite_users": "新規ユーザーの招待",
     "invite_users": "新規ユーザーの招待",
     "click_twice_same_checkbox": "少なくとも一つはチェックしてください。",
     "click_twice_same_checkbox": "少なくとも一つはチェックしてください。",

+ 3 - 1
resource/locales/ja_JP/translation.json

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

+ 10 - 0
resource/locales/zh_CN/admin/admin.json

@@ -259,6 +259,16 @@
 		"export_menu": "导出菜单",
 		"export_menu": "导出菜单",
 		"download": "下载",
 		"download": "下载",
 		"delete": "删除"
 		"delete": "删除"
+  },
+  "slack_integration": {
+    "access_token_settings": {
+      "discard": "丢弃",
+      "generate": "生成"
+    },
+    "custom_bot_non_proxy_settings": "Custom bot (non-proxy) 设置",
+    "non_proxy": {
+      "create_bot": "创建 Bot"
+    }
   },
   },
 	"user_management": {
 	"user_management": {
 		"invite_users": "邀请新用户",
 		"invite_users": "邀请新用户",

+ 3 - 1
resource/locales/zh_CN/translation.json

@@ -117,8 +117,10 @@
 	"Site URL settings": "主页URL设置",
 	"Site URL settings": "主页URL设置",
 	"Markdown Settings": "Markdown设置",
 	"Markdown Settings": "Markdown设置",
 	"Customize": "页面定制",
 	"Customize": "页面定制",
+	"Notification Settings": "通知设置",
+  "slack_integration": "Slack一体化",
   "External_Notification": "外部通知",
   "External_Notification": "外部通知",
-  "Legacy_External_Notification": "旧版外部通知",
+  "Legacy_Slack_Integration": "旧版Slack一体化",
 	"User_Management": "用户管理",
 	"User_Management": "用户管理",
 	"external_account_management": "外部账户管理",
 	"external_account_management": "外部账户管理",
 	"UserGroup Management": "用户组管理",
 	"UserGroup Management": "用户组管理",

+ 2 - 0
src/client/js/admin.jsx

@@ -10,6 +10,7 @@ import ErrorBoundary from './components/ErrorBoudary';
 import AdminHome from './components/Admin/AdminHome/AdminHome';
 import AdminHome from './components/Admin/AdminHome/AdminHome';
 import UserGroupDetailPage from './components/Admin/UserGroupDetail/UserGroupDetailPage';
 import UserGroupDetailPage from './components/Admin/UserGroupDetail/UserGroupDetailPage';
 import NotificationSetting from './components/Admin/Notification/NotificationSetting';
 import NotificationSetting from './components/Admin/Notification/NotificationSetting';
+import SlackIntegration from './components/Admin/SlackIntegration/SlackIntegration';
 import ManageGlobalNotification from './components/Admin/Notification/ManageGlobalNotification';
 import ManageGlobalNotification from './components/Admin/Notification/ManageGlobalNotification';
 import MarkdownSetting from './components/Admin/MarkdownSetting/MarkDownSetting';
 import MarkdownSetting from './components/Admin/MarkdownSetting/MarkDownSetting';
 import UserManagement from './components/Admin/UserManagement';
 import UserManagement from './components/Admin/UserManagement';
@@ -96,6 +97,7 @@ Object.assign(componentMappings, {
   'admin-importer': <ImportDataPage />,
   'admin-importer': <ImportDataPage />,
   'admin-export-page': <ExportArchiveDataPage />,
   'admin-export-page': <ExportArchiveDataPage />,
   'admin-notification-setting': <NotificationSetting />,
   'admin-notification-setting': <NotificationSetting />,
+  'admin-slack-integration': <SlackIntegration />,
   'admin-global-notification-setting': <ManageGlobalNotification />,
   'admin-global-notification-setting': <ManageGlobalNotification />,
   'admin-user-page': <UserManagement />,
   'admin-user-page': <UserManagement />,
   'admin-external-account-setting': <ManageExternalAccount />,
   'admin-external-account-setting': <ManageExternalAccount />,

+ 27 - 24
src/client/js/components/Admin/Common/AdminNavigation.jsx

@@ -14,19 +14,20 @@ const AdminNavigation = (props) => {
   // eslint-disable-next-line react/prop-types
   // eslint-disable-next-line react/prop-types
   const MenuLabel = ({ menu }) => {
   const MenuLabel = ({ menu }) => {
     switch (menu) {
     switch (menu) {
-      case 'app':           return <><i className="icon-fw icon-settings"></i>        { t('App Settings') }</>;
-      case 'security':      return <><i className="icon-fw icon-shield"></i>          { t('security_settings') }</>;
-      case 'markdown':      return <><i className="icon-fw icon-note"></i>            { t('Markdown Settings') }</>;
-      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('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 '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') }</>;
-      case 'search':        return <><i className="icon-fw icon-magnifier"></i>       { t('Full Text Search Management') }</>;
-      default:              return <><i className="icon-fw icon-home"></i>            { t('Wiki Management Home Page') }</>;
+      case 'app':               return <><i className="icon-fw icon-settings"></i>        { t('App Settings') }</>;
+      case 'security':          return <><i className="icon-fw icon-shield"></i>          { t('security_settings') }</>;
+      case 'markdown':          return <><i className="icon-fw icon-note"></i>            { t('Markdown Settings') }</>;
+      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('External_Notification') }</>;
+      // TODO change icon for legacy-slack-integration by GW-5466
+      case 'legacy-slack-integration':  return <> <i className="icon-fw icon-paper-plane"></i>    { t('Legacy_Slack_Integration') }</>;
+      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') }</>;
+      case 'search':            return <><i className="icon-fw icon-magnifier"></i>       { t('Full Text Search Management') }</>;
+      default:                  return <><i className="icon-fw icon-home"></i>            { t('Wiki Management Home Page') }</>;
     }
     }
   };
   };
 
 
@@ -63,7 +64,8 @@ const AdminNavigation = (props) => {
         <MenuLink menu="importer"     isListGroupItems isActive={isActiveMenu('/importer')} />
         <MenuLink menu="importer"     isListGroupItems isActive={isActiveMenu('/importer')} />
         <MenuLink menu="export"       isListGroupItems isActive={isActiveMenu('/export')} />
         <MenuLink menu="export"       isListGroupItems isActive={isActiveMenu('/export')} />
         <MenuLink menu="notification" isListGroupItems isActive={isActiveMenu('/notification') || isActiveMenu('/global-notification')} />
         <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="legacy-slack-integration" isListGroupItems isActive={isActiveMenu('/legacy-slack-integration')} />
         <MenuLink menu="users"        isListGroupItems isActive={isActiveMenu('/users')} />
         <MenuLink menu="users"        isListGroupItems isActive={isActiveMenu('/users')} />
         <MenuLink menu="user-groups"  isListGroupItems isActive={isActiveMenu('/user-groups')} />
         <MenuLink menu="user-groups"  isListGroupItems isActive={isActiveMenu('/user-groups')} />
         <MenuLink menu="search"       isListGroupItems isActive={isActiveMenu('/search')} />
         <MenuLink menu="search"       isListGroupItems isActive={isActiveMenu('/search')} />
@@ -90,17 +92,18 @@ const AdminNavigation = (props) => {
           aria-expanded="false"
           aria-expanded="false"
         >
         >
           <span className="float-left">
           <span className="float-left">
-            {pathname === '/admin' &&         <MenuLabel menu="home" />}
-            {isActiveMenu('/app') &&          <MenuLabel menu="app" />}
-            {isActiveMenu('/security') &&     <MenuLabel menu="security" />}
-            {isActiveMenu('/markdown') &&     <MenuLabel menu="markdown" />}
-            {isActiveMenu('/customize') &&    <MenuLabel menu="customize" />}
-            {isActiveMenu('/importer') &&     <MenuLabel menu="importer" />}
-            {isActiveMenu('/export') &&       <MenuLabel menu="export" />}
+            {pathname === '/admin' &&              <MenuLabel menu="home" />}
+            {isActiveMenu('/app') &&               <MenuLabel menu="app" />}
+            {isActiveMenu('/security') &&          <MenuLabel menu="security" />}
+            {isActiveMenu('/markdown') &&          <MenuLabel menu="markdown" />}
+            {isActiveMenu('/customize') &&         <MenuLabel menu="customize" />}
+            {isActiveMenu('/importer') &&          <MenuLabel menu="importer" />}
+            {isActiveMenu('/export') &&            <MenuLabel menu="export" />}
             {(isActiveMenu('/notification') || isActiveMenu('/global-notification')) && <MenuLabel menu="notification" />}
             {(isActiveMenu('/notification') || isActiveMenu('/global-notification')) && <MenuLabel menu="notification" />}
-            {isActiveMenu('/users') &&        <MenuLabel menu="users" />}
-            {isActiveMenu('/user-groups') &&  <MenuLabel menu="user-groups" />}
-            {isActiveMenu('/search') &&       <MenuLabel menu="search" />}
+            {isActiveMenu('/slack-integration') && <MenuLabel menu="slack-integration" />}
+            {isActiveMenu('/users') &&             <MenuLabel menu="users" />}
+            {isActiveMenu('/user-groups') &&       <MenuLabel menu="user-groups" />}
+            {isActiveMenu('/search') &&            <MenuLabel menu="search" />}
           </span>
           </span>
         </button>
         </button>
         <div className="dropdown-menu" aria-labelledby="dropdown-admin-navigation">
         <div className="dropdown-menu" aria-labelledby="dropdown-admin-navigation">

+ 0 - 1
src/client/js/components/Admin/Common/AdminUpdateButtonRow.jsx

@@ -16,7 +16,6 @@ const AdminUpdateButtonRow = (props) => {
 
 
 AdminUpdateButtonRow.propTypes = {
 AdminUpdateButtonRow.propTypes = {
   t: PropTypes.func.isRequired, // i18next
   t: PropTypes.func.isRequired, // i18next
-
   onClick: PropTypes.func.isRequired,
   onClick: PropTypes.func.isRequired,
   disabled: PropTypes.bool.isRequired,
   disabled: PropTypes.bool.isRequired,
 };
 };

+ 39 - 0
src/client/js/components/Admin/SlackIntegration/AccessTokenSettings.jsx

@@ -0,0 +1,39 @@
+/* eslint-disable no-console */
+import React from 'react';
+import { useTranslation } from 'react-i18next';
+
+function AccessTokenSettings() {
+
+  const { t } = useTranslation('admin');
+  function discardHandler() {
+    console.log('Discard button pressed');
+  }
+
+  function generateHandler() {
+    console.log('Generate button pressed');
+  }
+
+  return (
+    <>
+      <div className="form-group row my-5">
+        <label className="text-left text-md-right col-md-3 col-form-label">Access Token</label>
+        <div className="col-md-6">
+          <input className="form-control" type="text" placeholder="access-token" />
+        </div>
+      </div>
+
+      <div className="row">
+        <div className="mx-auto">
+          <button type="button" className="btn btn-outline-secondary text-nowrap mx-1" onClick={discardHandler}>
+            {t('slack_integration.access_token_settings.discard')}
+          </button>
+          <button type="button" className="btn btn-primary text-nowrap mx-1" onClick={generateHandler}>
+            {t('slack_integration.access_token_settings.generate')}
+          </button>
+        </div>
+      </div>
+    </>
+  );
+}
+
+export default AccessTokenSettings;

+ 54 - 0
src/client/js/components/Admin/SlackIntegration/CustomBotNonProxySettings.jsx

@@ -0,0 +1,54 @@
+/* eslint-disable no-console */
+import React, { useState } from 'react';
+import { useTranslation } from 'react-i18next';
+
+import AdminUpdateButtonRow from '../Common/AdminUpdateButtonRow';
+
+function CustomBotNonProxySettings() {
+
+  const { t } = useTranslation('admin');
+  const [secret, setSecret] = useState('');
+  const [token, setToken] = useState('');
+
+  function updateHandler() {
+    console.log(`Signing Secret: ${secret}, Bot User OAuth Token: ${token}`);
+  }
+
+  return (
+    <>
+      <div className="row my-5">
+        <div className="mx-auto">
+          <button type="button" className="btn btn-primary text-nowrap mx-1" onClick={() => window.open('https://api.slack.com/apps', '_blank')}>
+            {t('slack_integration.non_proxy.create_bot')}
+          </button>
+        </div>
+      </div>
+
+      <div className="form-group row">
+        <label className="text-left text-md-right col-md-3 col-form-label">Signing Secret</label>
+        <div className="col-md-6">
+          <input
+            className="form-control"
+            type="text"
+            onChange={e => setSecret(e.target.value)}
+          />
+        </div>
+      </div>
+
+      <div className="form-group row mb-5">
+        <label className="text-left text-md-right col-md-3 col-form-label">Bot User OAuth Token</label>
+        <div className="col-md-6">
+          <input
+            className="form-control"
+            type="text"
+            onChange={e => setToken(e.target.value)}
+          />
+        </div>
+      </div>
+
+      <AdminUpdateButtonRow onClick={updateHandler} disabled={false} />
+    </>
+  );
+}
+
+export default CustomBotNonProxySettings;

+ 29 - 0
src/client/js/components/Admin/SlackIntegration/SlackIntegration.jsx

@@ -0,0 +1,29 @@
+import React from 'react';
+import { useTranslation } from 'react-i18next';
+
+import AccessTokenSettings from './AccessTokenSettings';
+import CustomBotNonProxySettings from './CustomBotNonProxySettings';
+
+function SlackIntegration() {
+
+  const { t } = useTranslation('admin');
+  return (
+    <>
+      <div className="row">
+        <div className="col-lg-12">
+          <h2 className="admin-setting-header">Access Token</h2>
+          <AccessTokenSettings />
+        </div>
+      </div>
+
+      <div className="row">
+        <div className="col-lg-12">
+          <h2 className="admin-setting-header">{t('slack_integration.custom_bot_non_proxy_settings')}</h2>
+          <CustomBotNonProxySettings />
+        </div>
+      </div>
+    </>
+  );
+}
+
+export default SlackIntegration;

+ 7 - 3
src/server/routes/admin.js

@@ -223,11 +223,15 @@ module.exports = function(crowi, app) {
     return res.render('admin/external-accounts');
     return res.render('admin/external-accounts');
   };
   };
 
 
-  actions.legacyExternalNotification = {};
-  actions.legacyExternalNotification = function(req, res) {
-    return res.render('admin/legacy-external-notification');
+  actions.legacySlackIntegration = {};
+  actions.legacySlackIntegration = function(req, res) {
+    return res.render('admin/legacy-slack-integration');
   };
   };
 
 
+  actions.slackIntegration = {};
+  actions.slackIntegration = function(req, res) {
+    return res.render('admin/slack-integration');
+  };
 
 
   actions.userGroup = {};
   actions.userGroup = {};
   actions.userGroup.index = function(req, res) {
   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('/attachment', require('./attachment')(crowi));
 
 
   router.use('/slack-bot', require('./slack-bot')(crowi));
   router.use('/slack-bot', require('./slack-bot')(crowi));
+  router.use('/slack-integration', require('./slack-integration')(crowi));
   router.use('/staffs', require('./staffs')(crowi));
   router.use('/staffs', require('./staffs')(crowi));
 
 
   return router;
   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;
+};

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

@@ -93,7 +93,8 @@ module.exports = function(crowi, app) {
   app.get('/admin/notification/slackSetting/disconnect' , loginRequiredStrictly , adminRequired , admin.notification.disconnectFromSlack);
   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/new'              , loginRequiredStrictly , adminRequired , admin.globalNotification.detail);
   app.get('/admin/global-notification/:id'              , 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/legacy-slack-integration'         , loginRequiredStrictly , adminRequired,  admin.legacySlackIntegration);
+  app.get('/admin/slack-integration'                    , loginRequiredStrictly , adminRequired,  admin.slackIntegration);
 
 
   app.get('/admin/users'                                , loginRequiredStrictly , adminRequired , admin.user.index);
   app.get('/admin/users'                                , loginRequiredStrictly , adminRequired , admin.user.index);
 
 

+ 1 - 16
src/server/service/bolt.js

@@ -74,21 +74,6 @@ class BoltService {
   }
   }
 
 
   init() {
   init() {
-    // Example of listening for event
-    // See. https://github.com/slackapi/bolt-js#listening-for-events
-    // or https://slack.dev/bolt-js/concepts#basic
-    this.bolt.command('/growi-bot', async({ command, ack, say }) => { // demo
-      await say('Hello');
-    });
-
-    // The echo command simply echoes on command
-    this.bolt.command('/echo', async({ command, ack, say }) => {
-      // Acknowledge command request
-      await ack();
-
-      await say(`${command.text}`);
-    });
-
     this.bolt.command('/growi', async({
     this.bolt.command('/growi', async({
       command, client, body, ack,
       command, client, body, ack,
     }) => {
     }) => {
@@ -271,7 +256,7 @@ class BoltService {
       };
       };
       // show "Next" button if next page exists
       // show "Next" button if next page exists
       if (resultsTotal > offset + PAGINGLIMIT) {
       if (resultsTotal > offset + PAGINGLIMIT) {
-        actionBlocks.elements.push(
+        actionBlocks.elements.unshift(
           {
           {
             type: 'button',
             type: 'button',
             text: {
             text: {

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

@@ -410,6 +410,12 @@ const ENV_VAR_NAME_TO_CONFIG_INFO = {
     type:    TYPES.STRING,
     type:    TYPES.STRING,
     default: null,
     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 {
 class ConfigLoader {

+ 2 - 2
src/server/views/admin/legacy-external-notification.html → src/server/views/admin/legacy-slack-integration.html

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

+ 9 - 0
src/server/views/admin/slack-integration.html

@@ -0,0 +1,9 @@
+{% extends '../layout/admin.html' %}
+
+{% block content_header %}
+<h1 class="title">{{ t('slack_integration') }}</h1>
+{% endblock %}
+
+{% block content_main %}
+<div id="admin-slack-integration" class="admin-slack-integration"></div>
+{% endblock content_main %}