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

Merge branch 'feat/growi-bot' into feat/move-verifyingIsSlackRequest-to-slack-package

zahmis 5 лет назад
Родитель
Сommit
75986b1492

+ 8 - 8
.github/workflows/ci-slackbot-proxy.yml

@@ -31,7 +31,7 @@ jobs:
       id: cache-dependencies
       uses: actions/cache@v2
       with:
-        path: node_modules
+        path: '**/node_modules'
         key: ${{ runner.OS }}-node_modules-${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}
     - name: Get yarn cache dir
       if: steps.cache-dependencies.outputs.cache-hit != 'true'
@@ -96,7 +96,7 @@ jobs:
       id: cache-dependencies
       uses: actions/cache@v2
       with:
-        path: node_modules
+        path: '**/node_modules'
         key: ${{ runner.OS }}-node_modules_dev-${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}
     - name: Get yarn cache dir
       if: steps.cache-dependencies.outputs.cache-hit != 'true'
@@ -110,7 +110,7 @@ jobs:
         key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
         restore-keys: |
           ${{ runner.os }}-yarn-
-    - name: Install dependencies
+    - name: lerna bootstrap
       if: steps.cache-dependencies.outputs.cache-hit != 'true'
       run: |
         npx lerna bootstrap
@@ -175,7 +175,7 @@ jobs:
     - name: Cache/Restore node_modules
       uses: actions/cache@v2
       with:
-        path: node_modules
+        path: '**/node_modules'
         key: ${{ runner.OS }}-node_modules_prod-${{ matrix.node-version }}-${{ steps.date.outputs.YmdH }}
         restore-keys: |
           ${{ runner.os }}-node_modules_prod-${{ matrix.node-version }}-${{ steps.date.outputs.Ymd }}
@@ -191,7 +191,7 @@ jobs:
         key: ${{ runner.os }}-yarn-${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}
         restore-keys: |
           ${{ runner.os }}-yarn-
-    - name: Install dependencies
+    - name: lerna bootstrap
       run: |
         npx lerna bootstrap
     - name: Print dependencies
@@ -199,12 +199,12 @@ jobs:
         echo -n "node " && node -v
         echo -n "npm " && npm -v
         yarn list --depth=0
-    - name: yarn build
+    - name: lerna run build
       run: |
         yarn lerna run build
-    - name: yarn install --production
+    - name: lerna bootstrap --production
       run: |
-        yarn lerna exec "yarn install --production"
+        npx lerna bootstrap -- --production
     - name: Print dependencies
       run: |
         echo -n "node " && node -v

+ 13 - 11
.github/workflows/ci.yml

@@ -35,7 +35,7 @@ jobs:
       id: cache-dependencies
       uses: actions/cache@v2
       with:
-        path: node_modules
+        path: '**/node_modules'
         key: ${{ runner.OS }}-node_modules-${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}
     - name: Get yarn cache dir
       if: steps.cache-dependencies.outputs.cache-hit != 'true'
@@ -52,7 +52,7 @@ jobs:
     - name: Install dependencies
       if: steps.cache-dependencies.outputs.cache-hit != 'true'
       run: |
-        yarn add -W growi-plugin-lsx growi-plugin-pukiwiki-like-linker growi-plugin-attachment-refs react-images@1.0.0 react-motion
+        npx lerna bootstrap
     - name: Print dependencies
       run: |
         echo -n "node " && node -v
@@ -100,7 +100,7 @@ jobs:
       id: cache-dependencies
       uses: actions/cache@v2
       with:
-        path: node_modules
+        path: '**/node_modules'
         key: ${{ runner.OS }}-node_modules-${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}
     - name: Get yarn cache dir
       if: steps.cache-dependencies.outputs.cache-hit != 'true'
@@ -117,7 +117,7 @@ jobs:
     - name: Install dependencies
       if: steps.cache-dependencies.outputs.cache-hit != 'true'
       run: |
-        yarn add -W growi-plugin-lsx growi-plugin-pukiwiki-like-linker growi-plugin-attachment-refs react-images@1.0.0 react-motion
+        npx lerna bootstrap
     - name: Print dependencies
       run: |
         echo -n "node " && node -v
@@ -162,7 +162,7 @@ jobs:
       id: cache-dependencies
       uses: actions/cache@v2
       with:
-        path: node_modules
+        path: '**/node_modules'
         key: ${{ runner.OS }}-node_modules_dev-${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}
     - name: Get Date
       id: date
@@ -195,7 +195,8 @@ jobs:
     - name: Install dependencies
       if: steps.cache-dependencies.outputs.cache-hit != 'true'
       run: |
-        yarn add -W growi-plugin-lsx growi-plugin-pukiwiki-like-linker growi-plugin-attachment-refs react-images@1.0.0 react-motion
+        npx lerna bootstrap
+        yarn lerna add growi-plugin-lsx growi-plugin-pukiwiki-like-linker growi-plugin-attachment-refs react-images@1.0.0 react-motion --scope @growi/core
     - name: Print dependencies
       run: |
         echo -n "node " && node -v
@@ -249,7 +250,7 @@ jobs:
     - name: Cache/Restore node_modules
       uses: actions/cache@v2
       with:
-        path: node_modules
+        path: '**/node_modules'
         key: ${{ runner.OS }}-node_modules_prod-${{ matrix.node-version }}-${{ steps.date.outputs.YmdH }}
         restore-keys: |
           ${{ runner.os }}-node_modules_prod-${{ matrix.node-version }}-${{ steps.date.outputs.Ymd }}
@@ -267,8 +268,9 @@ jobs:
           ${{ runner.os }}-yarn-
     - name: Install dependencies
       run: |
-        yarn add -W growi-plugin-lsx growi-plugin-pukiwiki-like-linker growi-plugin-attachment-refs
-        yarn add -W -D react-images@1.0.0 react-motion
+        npx lerna bootstrap
+        yarn lerna add growi-plugin-lsx growi-plugin-pukiwiki-like-linker growi-plugin-attachment-refs --scope @growi/core
+        yarn lerna add -D react-images@1.0.0 react-motion --scope @growi/core
     - name: Print dependencies
       run: |
         echo -n "node " && node -v
@@ -277,9 +279,9 @@ jobs:
     - name: yarn build:prod:analyze
       run: |
         yarn build:prod:analyze
-    - name: yarn install --production
+    - name: lerna bootstrap --production
       run: |
-        yarn install --production
+        npx lerna bootstrap -- --production
     - name: Print dependencies
       run: |
         echo -n "node " && node -v

+ 1 - 200
package.json

@@ -78,208 +78,9 @@
     "webpack": "webpack"
   },
   "dependencies": {
-    "//": [
-      "check-node-version: see https://github.com/parshap/check-node-version/issues/35",
-      "openid-client: Node.js 12 or higher is required for openid-client@3 and above."
-    ],
-    "@google-cloud/storage": "^3.3.0",
-    "@kobalab/socket.io-session": "^1.0.3",
-    "@promster/express": "^5.0.1",
-    "@promster/server": "^6.0.0",
-    "@slack/events-api": "^3.0.0",
-    "@slack/web-api": "^6.1.0",
-    "JSONStream": "^1.3.5",
-    "archiver": "^3.1.1",
-    "array.prototype.flatmap": "^1.2.2",
-    "async-canvas-to-blob": "^1.0.3",
-    "aws-sdk": "^2.88.0",
-    "axios": "^0.21.1",
-    "body-parser": "^1.18.2",
-    "bunyan": "^1.8.12",
-    "bunyan-format": "^0.2.1",
-    "check-node-version": "^4.0.2",
-    "connect-flash": "~0.1.1",
-    "connect-mongo": "^3.2.0",
-    "connect-redis": "^4.0.4",
-    "cookie-parser": "^1.4.3",
-    "cross-env": "^7.0.0",
-    "csrf": "^3.1.0",
-    "date-fns": "^2.0.0",
-    "detect-indent": "^6.0.0",
-    "diff": "^4.0.1",
-    "elasticsearch": "^16.0.0",
-    "entities": "^2.0.0",
-    "env-cmd": "^10.0.1",
-    "esa-nodejs": "^0.0.7",
-    "escape-string-regexp": "^2.0.0",
-    "express": "^4.16.1",
-    "express-bunyan-logger": "^1.3.3",
-    "express-form": "~0.12.0",
-    "express-session": "^1.16.1",
-    "express-validator": "^6.1.1",
-    "express-webpack-assets": "^0.1.0",
-    "graceful-fs": "^4.1.11",
-    "growi-commons": "^5.0.3",
-    "helmet": "^3.13.0",
-    "i18next": "^19.0.0",
-    "i18next-express-middleware": "^1.4.1",
-    "i18next-node-fs-backend": "^2.1.0",
-    "i18next-sprintf-postprocessor": "^0.2.2",
-    "is-iso-date": "^0.0.1",
-    "lucene-query-parser": "^1.2.0",
-    "md5": "^2.2.1",
-    "method-override": "^3.0.0",
-    "migrate-mongo": "^8.1.4",
-    "mkdirp": "^1.0.3",
-    "module-alias": "^2.0.6",
-    "mongoose": "5.10.11",
-    "mongoose-gridfs": "^1.2.42",
-    "mongoose-paginate-v2": "^1.3.9",
-    "mongoose-unique-validator": "^2.0.3",
-    "multer": "~1.4.0",
-    "multer-autoreap": "^1.0.3",
-    "nodemailer": "^6.0.0",
-    "nodemailer-ses-transport": "~1.5.0",
-    "npm-run-all": "^4.1.2",
-    "openid-client": "=2.5.0",
-    "package-installed-version-sync": "^2.1.0",
-    "passport": "^0.4.0",
-    "passport-github": "^1.1.0",
-    "passport-google-oauth20": "^2.0.0",
-    "passport-http": "^0.3.0",
-    "passport-ldapauth": "^2.0.0",
-    "passport-local": "^1.0.0",
-    "passport-saml": "^1.0.0",
-    "passport-twitter": "^1.0.4",
-    "prom-client": "^13.0.0",
-    "react-card-flip": "^1.0.10",
-    "react-image-crop": "^8.3.0",
-    "reconnecting-websocket": "^4.4.0",
-    "redis": "^3.0.2",
-    "rimraf": "^3.0.0",
-    "slack-node": "^0.1.8",
-    "socket.io": "^2.3.0",
-    "stream-to-promise": "^2.2.0",
-    "string-width": "^4.1.0",
-    "swig-templates": "^2.0.2",
-    "uglifycss": "^0.0.29",
-    "unzipper": "^0.10.5",
-    "url-join": "^4.0.0",
-    "validator": "^12.0.0",
-    "ws": "^7.3.1",
-    "xss": "^1.0.6"
   },
   "devDependencies": {
-    "//": [
-      "@handsontable/react: v3 requires handsontable >= 7.0.0.",
-      "handsontable: v7.0.0 or above is no loger MIT lisence."
-    ],
-    "@alienfast/i18next-loader": "^1.0.16",
-    "@atlaskit/drawer": "^5.3.7",
-    "@atlaskit/navigation-next": "^8.0.5",
-    "@babel/core": "^7.4.5",
-    "@babel/plugin-proposal-class-properties": "^7.8.3",
-    "@babel/plugin-proposal-optional-chaining": "^7.9.0",
-    "@babel/polyfill": "^7.4.4",
-    "@babel/preset-env": "^7.4.5",
-    "@babel/preset-react": "^7.0.0",
-    "@handsontable/react": "=2.1.0",
-    "@types/compression": "^1.7.0",
-    "@types/express": "^4.17.11",
-    "@types/multer": "^1.4.5",
-    "@types/node": "^14.14.35",
-    "autoprefixer": "^9.0.0",
-    "babel-eslint": "^10.0.1",
-    "babel-loader": "^8.0.6",
-    "babel-plugin-lodash": "^3.3.4",
-    "babel-plugin-transform-imports": "^2.0.0",
-    "bootstrap": "^4.5.0",
-    "browser-bunyan": "^1.3.0",
-    "browser-sync": "^2.26.3",
-    "bunyan-debug": "^2.0.0",
-    "cli": "~1.0.1",
-    "codemirror": "^5.48.4",
-    "colors": "^1.2.5",
-    "connect-browser-sync": "^2.1.0",
-    "core-js": "=2.6.9",
-    "css-loader": "^3.0.0",
-    "csv-to-markdown-table": "^1.0.1",
-    "diff2html": "^3.1.2",
-    "eazy-logger": "^3.0.2",
-    "eslint": "^6.0.1",
-    "eslint-config-weseek": "^1.0.8",
-    "eslint-plugin-import": "^2.18.0",
-    "eslint-plugin-jest": "^23.0.3",
-    "eslint-plugin-react": "^7.14.2",
-    "eslint-plugin-react-hooks": "^4.0.4",
-    "file-loader": "^5.0.2",
-    "handsontable": "=6.2.2",
-    "hard-source-webpack-plugin": "^0.13.1",
-    "i18next-browser-languagedetector": "^4.0.1",
-    "imports-loader": "^0.8.0",
-    "jest": "^25.1.0",
-    "jquery-slimscroll": "^1.3.8",
-    "jquery-ui": "^1.12.1",
-    "jquery.cookie": "~1.4.1",
-    "lerna": "^4.0.0",
-    "load-css-file": "^1.0.0",
-    "lodash-webpack-plugin": "^0.11.5",
-    "markdown-it": "^10.0.0",
-    "markdown-it-blockdiag": "^1.1.1",
-    "markdown-it-drawio-viewer": "^1.3.1",
-    "markdown-it-emoji": "^1.4.0",
-    "markdown-it-footnote": "^3.0.1",
-    "markdown-it-mathjax": "^2.0.0",
-    "markdown-it-named-headers": "^0.0.4",
-    "markdown-it-plantuml": "^1.3.0",
-    "markdown-it-task-checkbox": "^1.0.6",
-    "markdown-it-toc-and-anchor-with-slugid": "^1.1.4",
-    "markdown-table": "^1.1.1",
-    "mini-css-extract-plugin": "^0.9.0",
-    "morgan": "^1.9.0",
-    "node-dev": "^4.0.0",
-    "node-sass": "^4.14.1",
-    "normalize-path": "^3.0.0",
-    "null-loader": "^3.0.0",
-    "on-headers": "^1.0.1",
-    "optimize-css-assets-webpack-plugin": "^5.0.3",
-    "penpal": "^4.0.0",
-    "plantuml-encoder": "^1.2.5",
-    "postcss-loader": "^3.0.0",
-    "prettier": "^1.19.1",
-    "react": "^16.8.3",
-    "react-bootstrap-typeahead": "^3.4.7",
-    "react-codemirror2": "^6.0.0",
-    "react-copy-to-clipboard": "^5.0.1",
-    "react-dom": "^16.8.3",
-    "react-dropzone": "^11.2.4",
-    "react-frame-component": "^4.0.0",
-    "react-hotkeys": "^2.0.0",
-    "react-i18next": "^11.1.0",
-    "react-waypoint": "^9.0.0",
-    "reactstrap": "^8.0.1",
-    "replacestream": "^4.0.3",
-    "reveal.js": "^3.5.0",
-    "rs-i18n": "^0.0.9",
-    "sass-loader": "^8.0.0",
-    "simple-load-script": "^1.0.2",
-    "socket.io-client": "^2.3.0",
-    "sticky-events": "^3.1.3",
-    "style-loader": "^1.0.0",
-    "styled-components": "^5.0.1",
-    "stylelint": "^13.2.0",
-    "stylelint-config-recess-order": "^2.0.1",
-    "swagger-jsdoc": "^3.4.0",
-    "swagger2openapi": "^5.3.1",
-    "terser-webpack-plugin": "^4.1.0",
-    "throttle-debounce": "^2.0.0",
-    "toastr": "^2.1.2",
-    "unstated": "^2.1.1",
-    "webpack": "^4.39.3",
-    "webpack-assets-manifest": "^3.1.1",
-    "webpack-bundle-analyzer": "^3.0.2",
-    "webpack-cli": "^3.3.7",
-    "webpack-merge": "^4.2.2"
+    "lerna": "^4.0.0"
   },
   "_moduleAliases": {
     "@root": ".",

+ 211 - 0
packages/core/package.json

@@ -0,0 +1,211 @@
+{
+  "name": "@growi/core",
+  "version": "0.9.0-RC",
+  "license": "MIT",
+  "main": "dist/index.js",
+  "typings": "dist/index.d.ts",
+  "scripts": {
+  },
+  "// comments for dependencies": {
+    "openid-client": "Node.js 12 or higher is required for openid-client@3 and above."
+  },
+  "dependencies": {
+    "@google-cloud/storage": "^3.3.0",
+    "@kobalab/socket.io-session": "^1.0.3",
+    "@promster/express": "^5.0.1",
+    "@promster/server": "^6.0.0",
+    "@slack/events-api": "^3.0.0",
+    "@slack/web-api": "^6.1.0",
+    "JSONStream": "^1.3.5",
+    "archiver": "^3.1.1",
+    "array.prototype.flatmap": "^1.2.2",
+    "async-canvas-to-blob": "^1.0.3",
+    "aws-sdk": "^2.88.0",
+    "axios": "^0.21.1",
+    "body-parser": "^1.18.2",
+    "bunyan": "^1.8.12",
+    "bunyan-format": "^0.2.1",
+    "check-node-version": "^4.1.0",
+    "connect-flash": "~0.1.1",
+    "connect-mongo": "^3.2.0",
+    "connect-redis": "^4.0.4",
+    "cookie-parser": "^1.4.5",
+    "cross-env": "^7.0.0",
+    "csrf": "^3.1.0",
+    "date-fns": "^2.0.0",
+    "detect-indent": "^6.0.0",
+    "diff": "^4.0.1",
+    "elasticsearch": "^16.0.0",
+    "entities": "^2.0.0",
+    "env-cmd": "^10.0.1",
+    "esa-nodejs": "^0.0.7",
+    "escape-string-regexp": "^2.0.0",
+    "express": "^4.16.1",
+    "express-bunyan-logger": "^1.3.3",
+    "express-form": "~0.12.0",
+    "express-session": "^1.16.1",
+    "express-validator": "^6.1.1",
+    "express-webpack-assets": "^0.1.0",
+    "graceful-fs": "^4.1.11",
+    "growi-commons": "^5.0.3",
+    "helmet": "^3.13.0",
+    "i18next": "^19.0.0",
+    "i18next-express-middleware": "^1.4.1",
+    "i18next-node-fs-backend": "^2.1.0",
+    "i18next-sprintf-postprocessor": "^0.2.2",
+    "is-iso-date": "^0.0.1",
+    "lucene-query-parser": "^1.2.0",
+    "md5": "^2.2.1",
+    "method-override": "^3.0.0",
+    "migrate-mongo": "^8.1.4",
+    "mkdirp": "^1.0.3",
+    "module-alias": "^2.0.6",
+    "mongoose": "5.10.11",
+    "mongoose-gridfs": "^1.2.42",
+    "mongoose-paginate-v2": "^1.3.9",
+    "mongoose-unique-validator": "^2.0.3",
+    "multer": "~1.4.0",
+    "multer-autoreap": "^1.0.3",
+    "nodemailer": "^6.0.0",
+    "nodemailer-ses-transport": "~1.5.0",
+    "npm-run-all": "^4.1.2",
+    "openid-client": "=2.5.0",
+    "package-installed-version-sync": "^2.1.0",
+    "passport": "^0.4.0",
+    "passport-github": "^1.1.0",
+    "passport-google-oauth20": "^2.0.0",
+    "passport-http": "^0.3.0",
+    "passport-ldapauth": "^2.0.0",
+    "passport-local": "^1.0.0",
+    "passport-saml": "^1.0.0",
+    "passport-twitter": "^1.0.4",
+    "prom-client": "^13.0.0",
+    "react-card-flip": "^1.0.10",
+    "react-image-crop": "^8.3.0",
+    "reconnecting-websocket": "^4.4.0",
+    "redis": "^3.0.2",
+    "rimraf": "^3.0.0",
+    "slack-node": "^0.1.8",
+    "socket.io": "^2.3.0",
+    "stream-to-promise": "^2.2.0",
+    "string-width": "^4.1.0",
+    "swig-templates": "^2.0.2",
+    "uglifycss": "^0.0.29",
+    "unzipper": "^0.10.5",
+    "url-join": "^4.0.0",
+    "validator": "^12.0.0",
+    "ws": "^7.3.1",
+    "xss": "^1.0.6"
+  },
+  "// comments for defDependencies": {
+    "@handsontable/react": "v3 requires handsontable >= 7.0.0.",
+    "handsontable": "v7.0.0 or above is no loger MIT lisence."
+  },
+  "devDependencies": {
+    "@alienfast/i18next-loader": "^1.0.16",
+    "@atlaskit/drawer": "^5.3.7",
+    "@atlaskit/navigation-next": "^8.0.5",
+    "@babel/core": "^7.4.5",
+    "@babel/plugin-proposal-class-properties": "^7.8.3",
+    "@babel/plugin-proposal-optional-chaining": "^7.9.0",
+    "@babel/polyfill": "^7.4.4",
+    "@babel/preset-env": "^7.4.5",
+    "@babel/preset-react": "^7.0.0",
+    "@handsontable/react": "=2.1.0",
+    "@types/compression": "^1.7.0",
+    "@types/express": "^4.17.11",
+    "@types/multer": "^1.4.5",
+    "@types/node": "^14.14.35",
+    "autoprefixer": "^9.0.0",
+    "babel-eslint": "^10.0.1",
+    "babel-loader": "^8.0.6",
+    "babel-plugin-lodash": "^3.3.4",
+    "babel-plugin-transform-imports": "^2.0.0",
+    "bootstrap": "^4.5.0",
+    "browser-bunyan": "^1.3.0",
+    "browser-sync": "^2.26.3",
+    "bunyan-debug": "^2.0.0",
+    "cli": "~1.0.1",
+    "codemirror": "^5.48.4",
+    "colors": "^1.2.5",
+    "connect-browser-sync": "^2.1.0",
+    "core-js": "=2.6.9",
+    "css-loader": "^3.0.0",
+    "csv-to-markdown-table": "^1.0.1",
+    "diff2html": "^3.1.2",
+    "eazy-logger": "^3.0.2",
+    "eslint": "^6.0.1",
+    "eslint-config-weseek": "^1.0.8",
+    "eslint-plugin-import": "^2.18.0",
+    "eslint-plugin-jest": "^23.0.3",
+    "eslint-plugin-react": "^7.14.2",
+    "eslint-plugin-react-hooks": "^4.0.4",
+    "file-loader": "^5.0.2",
+    "handsontable": "=6.2.2",
+    "hard-source-webpack-plugin": "^0.13.1",
+    "i18next-browser-languagedetector": "^4.0.1",
+    "imports-loader": "^0.8.0",
+    "jest": "^25.1.0",
+    "jquery-slimscroll": "^1.3.8",
+    "jquery-ui": "^1.12.1",
+    "jquery.cookie": "~1.4.1",
+    "load-css-file": "^1.0.0",
+    "lodash-webpack-plugin": "^0.11.5",
+    "markdown-it": "^10.0.0",
+    "markdown-it-blockdiag": "^1.1.1",
+    "markdown-it-drawio-viewer": "^1.3.1",
+    "markdown-it-emoji": "^1.4.0",
+    "markdown-it-footnote": "^3.0.1",
+    "markdown-it-mathjax": "^2.0.0",
+    "markdown-it-named-headers": "^0.0.4",
+    "markdown-it-plantuml": "^1.3.0",
+    "markdown-it-task-checkbox": "^1.0.6",
+    "markdown-it-toc-and-anchor-with-slugid": "^1.1.4",
+    "markdown-table": "^1.1.1",
+    "mini-css-extract-plugin": "^0.9.0",
+    "morgan": "^1.9.0",
+    "node-dev": "^4.0.0",
+    "node-sass": "^4.14.1",
+    "normalize-path": "^3.0.0",
+    "null-loader": "^3.0.0",
+    "on-headers": "^1.0.1",
+    "optimize-css-assets-webpack-plugin": "^5.0.3",
+    "penpal": "^4.0.0",
+    "plantuml-encoder": "^1.2.5",
+    "postcss-loader": "^3.0.0",
+    "prettier": "^1.19.1",
+    "react": "^16.8.3",
+    "react-bootstrap-typeahead": "^3.4.7",
+    "react-codemirror2": "^6.0.0",
+    "react-copy-to-clipboard": "^5.0.1",
+    "react-dom": "^16.8.3",
+    "react-dropzone": "^11.2.4",
+    "react-frame-component": "^4.0.0",
+    "react-hotkeys": "^2.0.0",
+    "react-i18next": "^11.1.0",
+    "react-waypoint": "^9.0.0",
+    "reactstrap": "^8.0.1",
+    "replacestream": "^4.0.3",
+    "reveal.js": "^3.5.0",
+    "rs-i18n": "^0.0.9",
+    "sass-loader": "^8.0.0",
+    "simple-load-script": "^1.0.2",
+    "socket.io-client": "^2.3.0",
+    "sticky-events": "^3.1.3",
+    "style-loader": "^1.0.0",
+    "styled-components": "^5.0.1",
+    "stylelint": "^13.2.0",
+    "stylelint-config-recess-order": "^2.0.1",
+    "swagger-jsdoc": "^3.4.0",
+    "swagger2openapi": "^5.3.1",
+    "terser-webpack-plugin": "^4.1.0",
+    "throttle-debounce": "^2.0.0",
+    "toastr": "^2.1.2",
+    "unstated": "^2.1.1",
+    "webpack": "^4.39.3",
+    "webpack-assets-manifest": "^3.1.1",
+    "webpack-bundle-analyzer": "^3.0.2",
+    "webpack-cli": "^3.3.7",
+    "webpack-merge": "^4.2.2"
+  }
+}

+ 1 - 4
packages/slack/package.json

@@ -19,13 +19,10 @@
   },
   "devDependencies": {
     "@slack/bolt": "^3.3.0",
-    "@tsed/core": "^6.43.0",
-    "@tsed/exceptions": "^6.43.0",
-    "@tsed/json-mapper": "^6.43.0",
-    "@tsed/schema": "^6.43.0",
     "@types/jest": "^26.0.22",
     "@typescript-eslint/eslint-plugin": "^4.18.0",
     "@typescript-eslint/parser": "^4.18.0",
+    "cross-env": "^7.0.0",
     "eslint-import-resolver-typescript": "^2.4.0",
     "eslint-plugin-jest": "^24.3.2",
     "ts-jest": "^26.5.4",

+ 101 - 0
packages/slackbot-proxy/docker/Dockerfile

@@ -0,0 +1,101 @@
+# syntax = docker/dockerfile:1.2
+
+##
+## deps-resolver-base
+##
+FROM node:14-slim AS deps-resolver-base
+
+ENV appDir /opt
+
+WORKDIR ${appDir}
+COPY ./package.json ./
+COPY ./yarn.lock ./
+COPY ./lerna.json ./
+COPY ./packages/slack/package.json ./packages/slack/package.json
+COPY ./packages/slackbot-proxy/package.json ./packages/slackbot-proxy/package.json
+
+# setup
+RUN yarn config set network-timeout 300000
+
+
+
+##
+## deps-resolver-dev
+##
+FROM deps-resolver-base AS deps-resolver-dev
+RUN npx lerna bootstrap
+
+
+
+##
+## deps-resolver-prod
+##
+FROM deps-resolver-base AS deps-resolver-prod
+RUN npx lerna bootstrap -- --production
+# make artifacts
+RUN tar cf node_modules.tar node_modules
+
+
+##
+## builder
+##
+FROM node:14-slim AS builder
+
+ENV appDir /opt
+
+WORKDIR ${appDir}
+
+COPY --from=deps-resolver-dev ${appDir}/node_modules node_modules
+
+# copy all related packages
+COPY packages/slack packages/slack
+COPY packages/slackbot-proxy packages/slackbot-proxy
+
+COPY ./package.json ./
+COPY ./lerna.json ./
+COPY ./tsconfig.base.json ./
+COPY ./packages/slack ./packages/slack
+COPY ./packages/slackbot-proxy ./packages/slackbot-proxy
+
+# build
+RUN yarn lerna run build
+
+# make artifacts
+RUN tar cf packages.tar \
+  packages/slack/package.json \
+  packages/slack/dist \
+  packages/slackbot-proxy/package.json \
+  packages/slackbot-proxy/dist
+
+
+
+##
+## release
+##
+FROM node:14-slim
+LABEL maintainer Yuki Takei <yuki@weseek.co.jp>
+
+ENV NODE_ENV production
+
+ENV appDir /opt
+
+COPY --from=deps-resolver-prod --chown=node:node \
+  ${appDir}/node_modules.tar ${appDir}/
+COPY --from=builder --chown=node:node \
+  ${appDir}/packages.tar ${appDir}/
+
+RUN chown node:node ${appDir}
+
+USER node
+
+# extract node_modules.tar
+WORKDIR ${appDir}
+RUN tar xf node_modules.tar
+RUN tar xf packages.tar
+RUN rm node_modules.tar packages.tar
+
+WORKDIR ${appDir}/packages/slackbot-proxy
+
+EXPOSE 8080
+
+CMD ["node", "-r", "dotenv-flow/config", "dist/index.js"]

+ 66 - 0
packages/slackbot-proxy/docker/README.md

@@ -0,0 +1,66 @@
+
+GROWI Slackbot Proxy Server Official docker image
+==============================================
+
+[![Node CI for slackbot-proxy](https://github.com/weseek/growi/actions/workflows/ci-slackbot-proxy.yml/badge.svg)](https://github.com/weseek/growi/actions/workflows/ci-slackbot-proxy.yml) [![docker-pulls](https://img.shields.io/docker/pulls/weseek/growi-slackbot-proxy.svg)](https://hub.docker.com/r/weseek/growi-slackbot-proxy/)
+
+
+Supported tags and respective Dockerfile links
+------------------------------------------------
+
+* [`1.0.0`, `1.0`, `1`, `latest` (Dockerfile)](https://github.com/weseek/growi/blob/master/docker/Dockerfile)
+
+
+What is GROWI Slackbot Proxy Server?
+----------------------------------
+
+The proxy server produced by GROWI Development Team, provides the backend for Slack App (Bot) to integrate GROWI Apps and Slack workspaces.
+
+see: (TBD)
+
+
+Requirements
+-------------
+
+* MySQL (>= 8.0)
+
+### Optional Dependencies
+
+* 
+
+
+Usage
+-----
+
+Create `.env.production.local`
+
+```
+```
+
+```bash
+docker run -d \
+    -e TYPEORM_CONNECTION=mysql \
+    -e TYPEORM_HOST=mysqlserver \
+    -e TYPEORM_DATABASE=growi-slackbot-proxy \
+    -e TYPEORM_USERNAME=growi-slackbot-proxy \
+    -e TYPEORM_PASSWORD=CHANGE-IT \
+    -e SLACK_CLIENT_ID=000000000000.0000000000000 \
+    -e SLACK_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx \
+    -e SLACK_SIGNING_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx \
+    weseek/growi-slackbot-proxy
+```
+
+and go to `http://localhost:8080/` .
+
+### docker-compose
+
+(TBD)
+
+Configuration
+-----------
+
+(TBD)
+
+### Environment Variables
+
+(TBD)

+ 3 - 0
packages/slackbot-proxy/package.json

@@ -26,7 +26,10 @@
     "@tsed/swagger": "^6.43.0",
     "@tsed/typeorm": "^6.43.0",
     "compression": "^1.7.4",
+    "cookie-parser": "^1.4.5",
+    "cross-env": "^7.0.0",
     "dotenv-flow": "^3.2.0",
+    "method-override": "^3.0.0",
     "mysql2": "^2.2.5",
     "typeorm": "^0.2.31"
   },

BIN
public/images/slack-integration/impossible.png


BIN
public/images/slack-integration/possible.png


BIN
public/images/slack-integration/triangle-basic-gray.png


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

@@ -264,9 +264,6 @@
       "without_proxy": "without proxy",
       "with_proxy": "with proxy",
       "recommended": "Recommended",
-      "for_beginners": "- For beginners -",
-      "for_intermediate": "- For intermediates -",
-      "for_advanced": "- For advanced -",
       "set_up": "Set up",
       "multiple_workspaces_integration": "Multiple workspaces integration",
       "security_control": "Security control",

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

@@ -262,9 +262,6 @@
       "without_proxy": "without proxy",
       "with_proxy": "with proxy",
       "recommended": "おすすめ",
-      "for_beginners": "- 初心者向け -",
-      "for_intermediate": "- 中級者向け -",
-      "for_advanced": "- 上級者向け -",
       "set_up": "セットアップ",
       "multiple_workspaces_integration": "複数ワークスペースとの連携",
       "security_control": "セキュリティコントロール",

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

@@ -272,9 +272,6 @@
       "without_proxy": "without proxy",
       "with_proxy": "with proxy",
       "recommended": "受到推崇的",
-      "for_beginners": "- 对于初学者 -",
-      "for_intermediate": "- 对于中级 -",
-      "for_advanced": "- 对于高级 -",
       "set_up": "设置",
       "multiple_workspaces_integration": "集成到多个工作区",
       "security_control": "安全控制",

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

@@ -0,0 +1,99 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { useTranslation } from 'react-i18next';
+
+
+const botDetails = {
+  officialBot: {
+    botType: 'officialBot',
+    botTypeCategory: 'official_bot',
+    setUp: 'easy',
+    multiWSIntegration: 'possible',
+    securityControl: 'impossible',
+  },
+  customBotWithoutProxy: {
+    botType: 'customBotWithoutProxy',
+    botTypeCategory: 'custom_bot',
+    supplementaryBotName: 'without_proxy',
+    setUp: 'normal',
+    multiWSIntegration: 'impossible',
+    securityControl: 'possible',
+  },
+  customBotWithProxy: {
+    botType: 'customBotWithProxy',
+    botTypeCategory: 'custom_bot',
+    supplementaryBotName: 'with_proxy',
+    setUp: 'hard',
+    multiWSIntegration: 'possible',
+    securityControl: 'possible',
+  },
+};
+
+const BotTypeCard = (props) => {
+  const { t } = useTranslation('admin');
+
+  return (
+    <div
+      className={`card admin-bot-card rounded border-radius-sm shadow ${props.isActive ? 'border-primary' : ''}`}
+      onClick={() => props.handleBotTypeSelect(botDetails[props.botType].botType)}
+      role="button"
+      key={props.botType}
+    >
+      <div>
+        <h3 className={`card-header mb-0 py-3
+              ${props.botType === 'officialBot' ? 'd-flex align-items-center justify-content-center' : 'text-center'}
+              ${props.isActive ? 'bg-primary text-light' : ''}`}
+        >
+          <span className="mr-2">
+            {t(`admin:slack_integration.selecting_bot_types.${botDetails[props.botType].botTypeCategory}`)}
+          </span>
+
+          {/*  A recommended badge is shown on official bot card, supplementary names are shown on Custom bot cards   */}
+          {props.botType === 'officialBot'
+          ? (
+            <span className="badge badge-info mr-2">
+              {t('admin:slack_integration.selecting_bot_types.recommended')}
+            </span>
+          ) : (
+            <span className="supplementary-bot-name mr-2">
+              {t(`admin:slack_integration.selecting_bot_types.${botDetails[props.botType].supplementaryBotName}`)}
+            </span>
+          )}
+
+          {/* TODO: add an appropriate links by GW-5614 */}
+          <i className={`fa fa-external-link btn-link ${props.isActive ? 'bg-primary text-light' : ''}`} aria-hidden="true"></i>
+        </h3>
+      </div>
+      <div className="card-body p-4">
+        <div className="card-text">
+          <div className="my-2">
+            <div className="d-flex justify-content-between mb-3">
+              {/* TODO add image of difficulties by GW-5638
+               <span>{t('admin:slack_integration.selecting_bot_types.set_up')}</span>
+               <span className={`bot-type-disc-${value.setUp}`}>{t(`admin:slack_integration.selecting_bot_types.${value.setUp}`)}</span>  */}
+
+
+            </div>
+            <div className="d-flex justify-content-between mb-3">
+              <span>{t('admin:slack_integration.selecting_bot_types.multiple_workspaces_integration')}</span>
+              <img className="bot-type-disc" src={`/images/slack-integration/${botDetails[props.botType].multiWSIntegration}.png`} alt="" />
+            </div>
+            <div className="d-flex justify-content-between">
+              <span>{t('admin:slack_integration.selecting_bot_types.security_control')}</span>
+              <img className="bot-type-disc" src={`/images/slack-integration/${botDetails[props.botType].securityControl}.png`} alt="" />
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  );
+
+};
+
+BotTypeCard.propTypes = {
+  isActive: PropTypes.bool.isRequired,
+  botType: PropTypes.string.isRequired,
+  handleBotTypeSelect: PropTypes.func.isRequired,
+};
+
+export default BotTypeCard;

+ 3 - 2
src/client/js/components/Admin/SlackIntegration/CustomBotWithoutProxySettings.jsx

@@ -7,7 +7,7 @@ import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 import AdminUpdateButtonRow from '../Common/AdminUpdateButtonRow';
 import SlackGrowiBridging from './SlackGrowiBridging';
-import CustomBotWithoutProxySettingsAccordion from './CustomBotWithoutProxySettingsAccordion';
+import CustomBotWithoutProxySettingsAccordion, { botInstallationStep } from './CustomBotWithoutProxySettingsAccordion';
 
 
 const CustomBotWithoutProxySettings = (props) => {
@@ -152,7 +152,8 @@ const CustomBotWithoutProxySettings = (props) => {
       <AdminUpdateButtonRow onClick={updateHandler} disabled={false} />
 
       <div className="my-5 mx-3">
-        <CustomBotWithoutProxySettingsAccordion />
+        {/* TODO GW-5644 active create bot step temporary */}
+        <CustomBotWithoutProxySettingsAccordion activeStep={botInstallationStep.CREATE_BOT} />
       </div>
 
     </>

+ 30 - 19
src/client/js/components/Admin/SlackIntegration/CustomBotWithoutProxySettingsAccordion.jsx

@@ -5,20 +5,29 @@ import { Collapse } from 'reactstrap';
 import AppContainer from '../../../services/AppContainer';
 import { withUnstatedContainers } from '../../UnstatedUtils';
 
-const CustomBotWithoutProxySettingsAccordion = (props) => {
-  const { appContainer } = props;
+
+export const botInstallationStep = {
+  CREATE_BOT: 'create-bot',
+  INSTALL_BOT: 'install-bot',
+  REGISTER_SLACK_CONFIGURATION: 'register-slack-configuration',
+  CONNECTION_TEST: 'connection-test',
+};
+
+const CustomBotWithoutProxySettingsAccordion = ({ appContainer, activeStep }) => {
   const { t } = useTranslation('admin');
-  const [openAccordionIndexes, setOpenAccordionIndexes] = useState(new Set());
+  const [openAccordionIndexes, setOpenAccordionIndexes] = useState(new Set([activeStep]));
+
   const [connectionErrorCode, setConnectionErrorCode] = useState(null);
   const [connectionErrorMessage, setConnectionErrorMessage] = useState(null);
 
-  const onToggleAccordionHandler = (i) => {
+
+  const onToggleAccordionHandler = (installationStep) => {
     const accordionIndexes = new Set(openAccordionIndexes);
-    if (accordionIndexes.has(i)) {
-      accordionIndexes.delete(i);
+    if (accordionIndexes.has(installationStep)) {
+      accordionIndexes.delete(installationStep);
     }
     else {
-      accordionIndexes.add(i);
+      accordionIndexes.add(installationStep);
     }
     setOpenAccordionIndexes(accordionIndexes);
   };
@@ -45,15 +54,15 @@ const CustomBotWithoutProxySettingsAccordion = (props) => {
         <div
           className="card-header font-weight-normal py-3 d-flex justify-content-between"
           role="button"
-          onClick={() => onToggleAccordionHandler(0)}
+          onClick={() => onToggleAccordionHandler(botInstallationStep.CREATE_BOT)}
         >
           <p className="mb-0 text-primary"><span className="mr-2">①</span>{t('slack_integration.without_proxy.create_bot')}</p>
-          {openAccordionIndexes.has(0)
+          {openAccordionIndexes.has(botInstallationStep.CREATE_BOT)
             ? <i className="fa fa-chevron-up" />
             : <i className="fa fa-chevron-down" />
           }
         </div>
-        <Collapse isOpen={openAccordionIndexes.has(0)}>
+        <Collapse isOpen={openAccordionIndexes.has(botInstallationStep.CREATE_BOT)}>
           <div className="card-body">
 
             <div className="row my-5">
@@ -83,15 +92,15 @@ const CustomBotWithoutProxySettingsAccordion = (props) => {
         <div
           className="card-header font-weight-normal py-3 d-flex justify-content-between"
           role="button"
-          onClick={() => onToggleAccordionHandler(1)}
+          onClick={() => onToggleAccordionHandler(botInstallationStep.INSTALL_BOT)}
         >
           <p className="mb-0 text-primary"><span className="mr-2">②</span>{t('slack_integration.without_proxy.install_bot_to_slack')}</p>
-          {openAccordionIndexes.has(1)
+          {openAccordionIndexes.has(botInstallationStep.INSTALL_BOT)
             ? <i className="fa fa-chevron-up" />
             : <i className="fa fa-chevron-down" />
           }
         </div>
-        <Collapse isOpen={openAccordionIndexes.has(1)}>
+        <Collapse isOpen={openAccordionIndexes.has(botInstallationStep.INSTALL_BOT)}>
           <div className="card-body py-5">
             <div className="container w-75">
               <p>1. Install your app をクリックします。</p>
@@ -114,15 +123,15 @@ const CustomBotWithoutProxySettingsAccordion = (props) => {
         <div
           className="card-header font-weight-normal py-3 d-flex justify-content-between"
           role="button"
-          onClick={() => onToggleAccordionHandler(2)}
+          onClick={() => onToggleAccordionHandler(botInstallationStep.REGISTER_SLACK_CONFIGURATION)}
         >
           <p className="mb-0 text-primary"><span className="mr-2">③</span>{t('slack_integration.without_proxy.register_secret_and_token')}</p>
-          {openAccordionIndexes.has(2)
+          {openAccordionIndexes.has(botInstallationStep.REGISTER_SLACK_CONFIGURATION)
             ? <i className="fa fa-chevron-up" />
             : <i className="fa fa-chevron-down" />
           }
         </div>
-        <Collapse isOpen={openAccordionIndexes.has(2)}>
+        <Collapse isOpen={openAccordionIndexes.has(botInstallationStep.REGISTER_SLACK_CONFIGURATION)}>
           <div className="card-body">
             BODY 3
           </div>
@@ -133,15 +142,15 @@ const CustomBotWithoutProxySettingsAccordion = (props) => {
         <div
           className="card-header font-weight-normal py-3 d-flex justify-content-between"
           role="button"
-          onClick={() => onToggleAccordionHandler(3)}
+          onClick={() => onToggleAccordionHandler(botInstallationStep.CONNECTION_TEST)}
         >
           <p className="mb-0 text-primary"><span className="mr-2">④</span>{t('slack_integration.without_proxy.test_connection')}</p>
-          {openAccordionIndexes.has(3)
+          {openAccordionIndexes.has(botInstallationStep.CONNECTION_TEST)
             ? <i className="fa fa-chevron-up" />
             : <i className="fa fa-chevron-down" />
           }
         </div>
-        <Collapse isOpen={openAccordionIndexes.has(3)}>
+        <Collapse isOpen={openAccordionIndexes.has(botInstallationStep.CONNECTION_TEST)}>
           <div className="card-body">
             <p className="text-center m-4">以下のテストボタンを押して、Slack連携が完了しているかの確認をしましょう</p>
             <div className="d-flex justify-content-center">
@@ -170,6 +179,8 @@ const CustomBotWithoutProxySettingsAccordionWrapper = withUnstatedContainers(Cus
 
 CustomBotWithoutProxySettingsAccordion.propTypes = {
   appContainer: PropTypes.instanceOf(AppContainer).isRequired,
+
+  activeStep: PropTypes.oneOf(Object.values(botInstallationStep)).isRequired,
 };
 
 export default CustomBotWithoutProxySettingsAccordionWrapper;

+ 15 - 137
src/client/js/components/Admin/SlackIntegration/SlackIntegration.jsx

@@ -10,7 +10,9 @@ import OfficialBotSettings from './OfficialBotSettings';
 import CustomBotWithoutProxySettings from './CustomBotWithoutProxySettings';
 import CustomBotWithProxySettings from './CustomBotWithProxySettings';
 import ConfirmBotChangeModal from './ConfirmBotChangeModal';
+import BotTypeCard from './BotTypeCard';
 
+const botTypes = ['officialBot', 'customBotWithoutProxy', 'customBotWithProxy'];
 
 const SlackIntegration = (props) => {
   const { appContainer } = props;
@@ -90,28 +92,19 @@ const SlackIntegration = (props) => {
   let settingsComponent = null;
 
   switch (currentBotType) {
-    case 'official-bot':
+    case 'officialBot':
       settingsComponent = <OfficialBotSettings />;
       break;
-    case 'custom-bot-without-proxy':
+    case 'customBotWithoutProxy':
       settingsComponent = (
         <CustomBotWithoutProxySettings />
       );
       break;
-    case 'custom-bot-with-proxy':
+    case 'customBotWithProxy':
       settingsComponent = <CustomBotWithProxySettings />;
       break;
   }
 
-  const showBotTypeLevel = (level) => {
-    return <span>{t(`admin:slack_integration.selecting_bot_types.${level}`)}</span>;
-  };
-  const showBotTypeLabel = (label) => {
-    return <span>{t(`admin:slack_integration.selecting_bot_types.${label}`)}</span>;
-  };
-  const showBotTypeDiscription = (desc) => {
-    return <span className={`bot-type-disc-${desc}`}>{t(`admin:slack_integration.selecting_bot_types.${desc}`)}</span>;
-  };
 
   return (
     <>
@@ -142,131 +135,16 @@ const SlackIntegration = (props) => {
 
         <div className="row my-4">
           <div className="card-deck mx-auto">
-
-            <div
-              className={`card admin-bot-card mx-3 rounded border-radius-sm shadow ${currentBotType === 'official-bot' ? 'border-primary' : ''}`}
-              onClick={() => handleBotTypeSelect('official-bot')}
-              role="button"
-            >
-              <div>
-                <h3 className={`card-header mb-0 py-3 text-center ${currentBotType === 'official-bot' ? 'bg-primary text-light' : ''}`}>
-                  <span className="mr-2">
-                    {t('admin:slack_integration.selecting_bot_types.official_bot')}
-                  </span>
-                  <span className="badge badge-info mr-2">
-                    {t('admin:slack_integration.selecting_bot_types.recommended')}
-                  </span>
-                  {/* TODO: add an appropriate link by GW-5614 */}
-                  <i className={`fa fa-external-link btn-link ${currentBotType === 'official-bot' ? 'bg-primary text-light' : ''}`} aria-hidden="true"></i>
-                </h3>
-              </div>
-              <div className="card-body p-4">
-                <p className="card-text">
-                  <div className="text-center">
-                    {showBotTypeLevel('for_beginners')}
-                  </div>
-                  <div className="my-4">
-                    <div className="d-flex justify-content-between mb-2">
-                      {showBotTypeLabel('set_up')}
-                      {showBotTypeDiscription('easy')}
-                    </div>
-                    <div className="d-flex justify-content-between mb-2">
-                      {showBotTypeLabel('multiple_workspaces_integration')}
-                      {showBotTypeDiscription('possible')}
-                    </div>
-                    <div className="d-flex justify-content-between">
-                      {showBotTypeLabel('security_control')}
-                      {showBotTypeDiscription('impossible')}
-                    </div>
-                  </div>
-                </p>
-              </div>
-            </div>
-
-            <div
-              className={`card admin-bot-card mx-3 rounded shadow ${currentBotType === 'custom-bot-without-proxy' ? 'border-primary' : ''}`}
-              onClick={() => handleBotTypeSelect('custom-bot-without-proxy')}
-              role="button"
-            >
-              <h3 className={`card-header mb-0 py-3 text-center text-nowrap  ${currentBotType === 'custom-bot-without-proxy' ? 'bg-primary text-light' : ''}`}>
-                <span className="mr-2">
-                  {t('admin:slack_integration.selecting_bot_types.custom_bot')}
-                </span>
-                <span className="supplementary-desc mr-2">
-                  {t('admin:slack_integration.selecting_bot_types.without_proxy')}
-                </span>
-                {/* TODO: add an appropriate link by GW-5614 */}
-                <i
-                  className={`fa fa-external-link btn-link ${currentBotType === 'custom-bot-without-proxy' ? 'bg-primary text-light' : ''}`}
-                  aria-hidden="true"
-                >
-                </i>
-              </h3>
-              <div className="card-body p-4">
-                <p className="card-text">
-                  <div className="text-center">
-                    {showBotTypeLevel('for_intermediate')}
-                  </div>
-                  <div className="my-4">
-                    <div className="d-flex justify-content-between mb-2">
-                      {showBotTypeLabel('set_up')}
-                      {showBotTypeDiscription('normal')}
-                    </div>
-                    <div className="d-flex justify-content-between mb-2">
-                      {showBotTypeLabel('multiple_workspaces_integration')}
-                      {showBotTypeDiscription('impossible')}
-                    </div>
-                    <div className="d-flex justify-content-between">
-                      {showBotTypeLabel('security_control')}
-                      {showBotTypeDiscription('possible')}
-                    </div>
-                  </div>
-                </p>
-              </div>
-            </div>
-
-            <div
-              className={`card admin-bot-card mx-3 rounded shadow ${currentBotType === 'custom-bot-with-proxy' ? 'border-primary' : ''}`}
-              onClick={() => handleBotTypeSelect('custom-bot-with-proxy')}
-              role="button"
-            >
-              <h3 className={`card-header mb-0 py-3 text-center text-nowrap ${currentBotType === 'custom-bot-with-proxy' ? 'bg-primary text-light' : ''}`}>
-                <span className="mr-2">
-                  {t('admin:slack_integration.selecting_bot_types.custom_bot')}
-                </span>
-                <span className="supplementary-desc mr-2">
-                  {t('admin:slack_integration.selecting_bot_types.with_proxy')}
-                </span>
-                {/* TODO: add an appropriate link by GW-5614 */}
-                <i
-                  className={`fa fa-external-link btn-link ${currentBotType === 'custom-bot-with-proxy' ? 'bg-primary text-light' : ''}`}
-                  aria-hidden="true"
-                >
-                </i>
-              </h3>
-              <div className="card-body p-4">
-                <p className="card-text">
-                  <div className="text-center">
-                    {showBotTypeLevel('for_advanced')}
-                  </div>
-                  <div className="my-4">
-                    <div className="d-flex justify-content-between mb-2">
-                      {showBotTypeLabel('set_up')}
-                      {showBotTypeDiscription('hard')}
-                    </div>
-                    <div className="d-flex justify-content-between mb-2">
-                      {showBotTypeLabel('multiple_workspaces_integration')}
-                      {showBotTypeDiscription('possible')}
-                    </div>
-                    <div className="d-flex justify-content-between">
-                      {showBotTypeLabel('security_control')}
-                      {showBotTypeDiscription('impossible')}
-                    </div>
-                  </div>
-                </p>
-              </div>
-            </div>
-
+            {botTypes.map((botType) => {
+              return (
+                <BotTypeCard
+                  key={botType}
+                  botType={botType}
+                  isActive={currentBotType === botType}
+                  handleBotTypeSelect={handleBotTypeSelect}
+                />
+              );
+            })}
           </div>
         </div>
       </div>

+ 1 - 1
src/client/styles/scss/_admin.scss

@@ -90,7 +90,7 @@
     .btn-link {
       font-size: 1rem;
     }
-    .supplementary-desc {
+    .supplementary-bot-name {
       font-size: 1rem;
     }
     .badge-info {

+ 2 - 14
src/client/styles/scss/theme/_apply-colors.scss

@@ -596,19 +596,7 @@ mark.rbt-highlight-text {
   Slack Integration
 */
 .selecting-bot-type {
-  .bot-type-disc-easy {
-    color: #33d541;
-  }
-  .bot-type-disc-normal {
-    color: #e6a63c;
-  }
-  .bot-type-disc-hard {
-    color: #ff5757;
-  }
-  .bot-type-disc-possible {
-    color: $info;
-  }
-  .bot-type-disc-impossible {
-    color: $gray-500;
+  .bot-type-disc {
+    width: 20px;
   }
 }

+ 1 - 1
src/server/routes/apiv3/slack-integration.js

@@ -54,7 +54,7 @@ module.exports = (crowi) => {
     ],
     SlackIntegration: [
       body('currentBotType')
-        .isIn(['official-bot', 'custom-bot-without-proxy', 'custom-bot-with-proxy']),
+        .isIn(['officialBot', 'customBotWithoutProxy', 'customBotWithProxy']),
     ],
     NotificationTestToSlackWorkSpace: [
       body('channel').isString(),

+ 1 - 1
src/server/service/config-loader.js

@@ -418,7 +418,7 @@ const ENV_VAR_NAME_TO_CONFIG_INFO = {
   },
   SLACK_BOT_TYPE: {
     ns:      'crowi',
-    key:     'slackbot:currentBotType', // 'official-bot' || 'custom-bot-without-proxy' || 'custom-bot-with-proxy'
+    key:     'slackbot:currentBotType', // 'officialBot' || 'customBotWithoutProxy' || 'customBotWithProxy'
     type:    TYPES.STRING,
     default: null,
   },

+ 9 - 8
yarn.lock

@@ -5293,10 +5293,10 @@ charenc@~0.0.1:
   version "0.0.2"
   resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
 
-check-node-version@^4.0.2:
-  version "4.0.2"
-  resolved "https://registry.yarnpkg.com/check-node-version/-/check-node-version-4.0.2.tgz#0385880c42425651ce4aaee8de8008e4a640f5dc"
-  integrity sha512-PsIRqtX9i4oWuScZrBf7I3fDfGfo8aS5uU7F1jJ771X0lNwW6hd+SYgIfs9w8Cw9mY4bF2QlU8ZZ1KdYdCobFA==
+check-node-version@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/check-node-version/-/check-node-version-4.1.0.tgz#12ff45bfeb8dd591700a0ab848c21b2d8ceeeb94"
+  integrity sha512-TSXGsyfW5/xY2QseuJn8/hleO2AU7HxVCdkc900jp1vcfzF840GkjvRT7CHl8sRtWn23n3X3k0cwH9RXeRwhfw==
   dependencies:
     chalk "^3.0.0"
     map-values "^1.0.1"
@@ -6067,11 +6067,12 @@ convert-source-map@^1.5.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0:
   dependencies:
     safe-buffer "~5.1.1"
 
-cookie-parser@^1.4.3:
-  version "1.4.3"
-  resolved "https://registry.yarnpkg.com/cookie-parser/-/cookie-parser-1.4.3.tgz#0fe31fa19d000b95f4aadf1f53fdc2b8a203baa5"
+cookie-parser@^1.4.5:
+  version "1.4.5"
+  resolved "https://registry.yarnpkg.com/cookie-parser/-/cookie-parser-1.4.5.tgz#3e572d4b7c0c80f9c61daf604e4336831b5d1d49"
+  integrity sha512-f13bPUj/gG/5mDr+xLmSxxDsB9DQiTIfhJS/sqjrmfAWiAN+x2O4i/XguTL9yDZ+/IFDanJ+5x7hC4CXT9Tdzw==
   dependencies:
-    cookie "0.3.1"
+    cookie "0.4.0"
     cookie-signature "1.0.6"
 
 cookie-signature@1.0.6: