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

Merge branch 'feat/growi-bot' into feat/4937-5597-verification-request-by-signing-secret

itizawa 5 лет назад
Родитель
Сommit
ed74f8abdf

+ 0 - 9
packages/slack/package.json

@@ -2,12 +2,7 @@
   "name": "@growi/slack",
   "version": "0.9.0-RC",
   "license": "MIT",
-  "main": "lib/index.js",
-  "files": ["lib"],
   "scripts": {
-    "build": "yarn tsc",
-    "tsc": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
-    "tsc:w": "tsc -w",
     "test": "yarn test:lint && yarn test:coverage",
     "test:unit": "cross-env NODE_ENV=test jest --passWithNoTests",
     "test:coverage": "yarn test:unit",
@@ -29,10 +24,6 @@
     "eslint-import-resolver-typescript": "^2.4.0",
     "eslint-plugin-jest": "^24.3.2",
     "ts-jest": "^26.5.4",
-    "ts-node": "^9.1.1",
-    "ts-node-dev": "^1.1.6",
-    "tsc-alias": "1.2.6",
-    "tsconfig-paths": "^3.9.0",
     "typescript": "^4.2.3"
   }
 }

+ 1 - 1
packages/slack/src/utils/slash-command-parser.test.ts

@@ -1,5 +1,5 @@
 import { SlashCommand } from '@slack/bolt';
-import { InvalidGrowiCommandError } from '~/models/errors';
+import { InvalidGrowiCommandError } from '../models/errors';
 
 import { parse } from './slash-command-parser';
 

+ 8 - 2
packages/slack/tsconfig.build.json

@@ -1,12 +1,18 @@
 {
   "extends": "./tsconfig.json",
   "compilerOptions": {
-    "outDir": "lib",
+    "rootDir": "./src",
+    "outDir": "../../dist/slack",
+    "composite": true,
     "declaration": true,
     "noResolve": false,
     "preserveConstEnums": true,
     "sourceMap": true,
     "noEmit": false,
-    "inlineSources": true
+    "inlineSources": true,
+
+    "baseUrl": ".",
+    "paths": {
+    }
   }
 }

+ 7 - 41
packages/slack/tsconfig.json

@@ -1,46 +1,12 @@
 {
+  "extends": "../../tsconfig.base.json",
   "compilerOptions": {
-    "target": "es2019",
-    "module": "commonjs",
-    "lib": ["dom", "dom.iterable", "esnext"],
-    "sourceMap": true,
-    "noEmit": true,
-    "removeComments": false,
-    "importHelpers": true,
-    "isolatedModules": true,
-
-    /* Strict Type-Checking Options */
-    // "strict": true,
-    "strictNullChecks": true,
-    "noImplicitAny": false,
-
-    /* Additional Checks */
-    "noUnusedLocals": false,
-    "noUnusedParameters": false,
-
-    /* Module Resolution Options */
-    "moduleResolution": "node",
-    "baseUrl": "src",
+    "baseUrl": ".",
     "paths": {
-      "~/*": ["./*"],
-      "^/*": ["../*"],
-    },
-    "typeRoots": [
-      "../../node_modules/@types",
-      "./node_modules/@types"
-    ],
-    "allowSyntheticDefaultImports": true,
-    "esModuleInterop": true,
-
-    /* Misc */
-    "preserveConstEnums": true,
-    "forceConsistentCasingInFileNames": true,
-    "resolveJsonModule": true,
-
-    /* Experimental Options */
-    "experimentalDecorators": true,
-    "emitDecoratorMetadata": true
+    }
   },
-  "exclude": ["node_modules", "./public", "dist", "test"],
-  "include": ["./src/**/*.ts"]
+  "exclude": [
+    "node_modules",
+    "**/*.test.ts"
+  ]
 }

+ 2 - 2
packages/slackbot-proxy/package.json

@@ -4,12 +4,12 @@
   "license": "MIT",
   "scripts": {
     "build": "yarn tsc",
-    "tsc": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
+    "tsc": "tsc -b --verbose tsconfig.build.json && tsc-alias -p tsconfig.build.json",
     "tsc:w": "tsc -w",
     "dev:ci": "yarn dev --ci",
     "dev": "cross-env NODE_ENV=development ts-node-dev -r tsconfig-paths/register -r dotenv-flow/config src/index.ts",
     "start:prod:ci": "yarn start:prod --ci",
-    "start:prod": "cross-env NODE_ENV=production node -r dotenv-flow/config dist/index.js",
+    "start:prod": "cross-env NODE_ENV=production node -r dotenv-flow/config ../../dist/slackbot-proxy/index.js",
     "test": "yarn test:lint && yarn test:coverage",
     "test:unit": "cross-env NODE_ENV=test jest --passWithNoTests",
     "test:coverage": "yarn test:unit",

+ 5 - 0
packages/slackbot-proxy/src/controllers/slack.ts

@@ -3,9 +3,11 @@ import {
 } from '@tsed/common';
 
 import { Installation } from '~/entities/installation';
+import { Relation } from '~/entities/relation';
 import { Order } from '~/entities/order';
 
 import { InstallationRepository } from '~/repositories/installation';
+import { RelationRepository } from '~/repositories/relation';
 import { OrderRepository } from '~/repositories/order';
 import { InstallerService } from '~/services/InstallerService';
 import { ReceiveService } from '~/services/RecieveService';
@@ -20,6 +22,9 @@ export class SlackCtrl {
   @Inject()
   installationRepository: InstallationRepository;
 
+  @Inject()
+  relationRepository: RelationRepository;
+
   @Inject()
   orderRepository: OrderRepository;
 

+ 0 - 1
packages/slackbot-proxy/src/entities/installation.ts

@@ -6,7 +6,6 @@ import {
 } from 'typeorm';
 
 import { Installation as SlackInstallation } from '@slack/oauth';
-import { Order } from './order';
 
 @Entity()
 export class Installation {

+ 33 - 0
packages/slackbot-proxy/src/entities/relation.ts

@@ -0,0 +1,33 @@
+import {
+  Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn, ManyToOne,Index
+} from 'typeorm';
+import { Installation } from './installation';
+
+@Entity()
+@Index(["installation", "growiUri"], { unique: true })
+export class Relation {
+
+  @PrimaryGeneratedColumn()
+  readonly id: number;
+
+  @CreateDateColumn()
+  readonly createdAt: Date;
+
+  @UpdateDateColumn()
+  readonly updatedAt: Date;
+
+  @ManyToOne(() => Installation)
+  readonly installation: number;
+
+  @Column()
+  @Index({ unique: true })
+  tokenGtoP: string;
+
+  @Column()
+  @Index()
+  tokenPtoG: string;
+
+  @Column()
+  growiUri: string;
+
+}

+ 10 - 0
packages/slackbot-proxy/src/repositories/relation.ts

@@ -0,0 +1,10 @@
+import {
+  Repository, EntityRepository,
+} from 'typeorm';
+
+import { Relation } from '~/entities/relation';
+
+@EntityRepository(Relation)
+export class RelationRepository extends Repository<Relation> {
+
+}

+ 1 - 1
packages/slackbot-proxy/src/services/RecieveService.ts

@@ -1,5 +1,5 @@
 import { Service } from '@tsed/di';
-import { parse } from '@growi/slack/lib/utils/slash-command-parser';
+import { parse } from '@growi/slack/utils/slash-command-parser';
 
 @Service()
 export class ReceiveService {

+ 10 - 0
packages/slackbot-proxy/tsconfig.base.json

@@ -0,0 +1,10 @@
+{
+  "extends": "../../tsconfig.base.json",
+  "compilerOptions": {
+  },
+  "exclude": [
+    "node_modules",
+    "config",
+    "**/*.test.ts"
+  ]
+}

+ 13 - 4
packages/slackbot-proxy/tsconfig.build.json

@@ -1,12 +1,21 @@
 {
-  "extends": "./tsconfig.json",
+  "extends": "./tsconfig.base.json",
   "compilerOptions": {
-    "outDir": "dist",
+    "rootDir": "./src",
+    "outDir": "../../dist/slackbot-proxy",
     "declaration": true,
     "noResolve": false,
     "preserveConstEnums": true,
     "sourceMap": true,
     "noEmit": false,
-    "inlineSources": true
-  }
+    "inlineSources": true,
+    "baseUrl": "./src",
+    "paths": {
+      "~/*": ["./*"],
+      "@growi/*": ["../../../dist/*"]
+    }
+  },
+  "references": [
+    { "path": "../slack/tsconfig.build.json" }
+  ]
 }

+ 6 - 42
packages/slackbot-proxy/tsconfig.json

@@ -1,46 +1,10 @@
 {
+  "extends": "./tsconfig.base.json",
   "compilerOptions": {
-    "target": "es2019",
-    "module": "commonjs",
-    "lib": ["dom", "dom.iterable", "esnext"],
-    "sourceMap": true,
-    "noEmit": true,
-    "removeComments": false,
-    "importHelpers": true,
-    "isolatedModules": true,
-
-    /* Strict Type-Checking Options */
-    // "strict": true,
-    "strictNullChecks": true,
-    "noImplicitAny": false,
-
-    /* Additional Checks */
-    "noUnusedLocals": false,
-    "noUnusedParameters": false,
-
-    /* Module Resolution Options */
-    "moduleResolution": "node",
-    "baseUrl": "src",
+    "baseUrl": ".",
     "paths": {
-      "~/*": ["./*"],
-      "^/*": ["../*"],
-    },
-    "typeRoots": [
-      "../../node_modules/@types",
-      "./node_modules/@types"
-    ],
-    "allowSyntheticDefaultImports": true,
-    "esModuleInterop": true,
-
-    /* Misc */
-    "preserveConstEnums": true,
-    "forceConsistentCasingInFileNames": true,
-    "resolveJsonModule": true,
-
-    /* Experimental Options */
-    "experimentalDecorators": true,
-    "emitDecoratorMetadata": true
-  },
-  "exclude": ["node_modules", "./public", "dist", "test"],
-  "include": ["./src/**/*.ts"]
+      "~/*": ["./src/*"],
+      "@growi/slack/*": ["../slack/src/*"],
+    }
+  }
 }

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

@@ -255,6 +255,27 @@
     "delete": "Delete"
   },
   "slack_integration": {
+    "selecting_bot_types": {
+      "slack_bot": "Slack bot",
+      "detailed_explanation": "Detailed explanation",
+      "selecting_bot_type": "・Select bot type",
+      "official_bot": "Official bot",
+      "custom_bot": "Custom bot",
+      "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",
+      "easy": "Easy",
+      "normal": "Normal",
+      "hard": "Hard",
+      "possible": "Possible",
+      "impossible": "Impossible"
+    },
     "bot_reset_successful": "Bot settings have been reset.",
     "copied_to_clipboard": "Copied to clipboard",
     "modal": {

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

@@ -253,6 +253,27 @@
     "Directory_hierarchy_tag": "ディレクトリ階層タグ"
   },
   "slack_integration": {
+    "selecting_bot_types": {
+      "slack_bot": "Slack bot",
+      "detailed_explanation": "詳しい説明はこちら",
+      "selecting_bot_type": "・Botタイプを選択する",
+      "official_bot": "Official bot",
+      "custom_bot": "Custom bot",
+      "without_proxy": "without proxy",
+      "with_proxy": "with proxy",
+      "recommended": "おすすめ",
+      "for_beginners": "- 初心者向け -",
+      "for_intermediate": "- 中級者向け -",
+      "for_advanced": "- 上級者向け -",
+      "set_up": "セットアップ",
+      "multiple_workspaces_integration": "複数ワークスペースとの連携",
+      "security_control": "セキュリティコントロール",
+      "easy": "かんたん",
+      "normal": "ふつう",
+      "hard": "むずかしい",
+      "possible": "可能",
+      "impossible": "不可"
+    },
     "bot_reset_successful": "Botの設定を消去しました。",
     "copied_to_clipboard": "クリップボードにコピーされました。",
     "modal": {

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

@@ -263,6 +263,27 @@
 		"delete": "删除"
   },
   "slack_integration": {
+    "selecting_bot_types": {
+      "slack_bot": "Slack bot",
+      "detailed_explanation": "详细说明",
+      "selecting_bot_type": "・选择机器人类型",
+      "official_bot": "Official bot",
+      "custom_bot": "Custom bot",
+      "without_proxy": "without proxy",
+      "with_proxy": "with proxy",
+      "recommended": "受到推崇的",
+      "for_beginners": "- 对于初学者 -",
+      "for_intermediate": "- 对于中级 -",
+      "for_advanced": "- 对于高级 -",
+      "set_up": "设置",
+      "multiple_workspaces_integration": "集成到多个工作区",
+      "security_control": "安全控制",
+      "easy": "简单",
+      "normal": "通常",
+      "hard": "难的",
+      "possible": "可能的",
+      "impossible": "不可能"
+    },
     "bot_reset_successful": "删除了BOT设置。",
     "copied_to_clipboard": "它已复制到剪贴板。",
     "modal": {

+ 144 - 26
src/client/js/components/Admin/SlackIntegration/SlackIntegration.jsx

@@ -103,6 +103,16 @@ const SlackIntegration = (props) => {
       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 (
     <>
       <ConfirmBotChangeModal
@@ -117,39 +127,147 @@ const SlackIntegration = (props) => {
         onClickGenerateToken={generateTokenHandler}
       />
 
-      <div className="row my-5">
-        <div className="card-deck mx-auto">
+      <div className="selecting-bot-type my-5">
+        <h2 className="admin-setting-header mb-4">
+          {t('admin:slack_integration.selecting_bot_types.slack_bot')}
+          <span className="ml-2 btn-link">
+            <span className="mr-1">{t('admin:slack_integration.selecting_bot_types.detailed_explanation')}</span>
+            {/* TODO: add an appropriate link by GW-5614 */}
+            <i className="fa fa-external-link" aria-hidden="true"></i>
+          </span>
+
+        </h2>
+
+        {t('admin:slack_integration.selecting_bot_types.selecting_bot_type')}
 
-          <div
-            className={`card admin-bot-card mx-3 py-5 rounded ${currentBotType === 'official-bot' ? 'border-info' : ''}`}
-            onClick={() => handleBotTypeSelect('official-bot')}
-          >
-            <div className="card-body">
-              <h5 className="card-title">Official Bot</h5>
-              <p className="card-text">This is a wider card with supporting text below as a natural lead-in to additional content.</p>
+        <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>
 
-          <div
-            className={`card admin-bot-card mx-3 py-5 rounded ${currentBotType === 'custom-bot-without-proxy' ? 'border-info' : ''}`}
-            onClick={() => handleBotTypeSelect('custom-bot-without-proxy')}
-          >
-            <div className="card-body">
-              <h5 className="card-title">Custom Bot (Without Proxy)</h5>
-              <p className="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. </p>
+            <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>
 
-          <div
-            className={`card admin-bot-card mx-3 py-5 rounded ${currentBotType === 'custom-bot-with-proxy' ? 'border-info' : ''}`}
-            onClick={() => handleBotTypeSelect('custom-bot-with-proxy')}
-          >
-            <div className="card-body">
-              <h5 className="card-title">Custom Bot (With Proxy)</h5>
-              <p className="card-text">This is a wider card with supporting text below as a natural lead-in to additional content.</p>
+            <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>
-          </div>
 
+          </div>
         </div>
       </div>
 

+ 20 - 2
src/client/styles/scss/_admin.scss

@@ -83,8 +83,26 @@
     }
   }
 
-  .admin-bot-card {
-    cursor: pointer;
+  /*
+  Slack Integration
+  */
+  .selecting-bot-type {
+    .btn-link {
+      font-size: 1rem;
+    }
+    .supplementary-desc {
+      font-size: 1rem;
+    }
+    .badge-info {
+      font-size: 0.6rem;
+    }
+    .admin-bot-card {
+      min-width: 280px;
+      border-radius: 8px !important;
+    }
+    .border-primary {
+      border-width: 2px;
+    }
   }
 
   //// TODO: migrate to Bootstrap 4

+ 21 - 0
src/client/styles/scss/theme/_apply-colors.scss

@@ -591,3 +591,24 @@ mark.rbt-highlight-text {
 .grw-btn-page-management:focus {
   background-color: rgba($color-link, 0.15);
 }
+
+/*
+  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;
+  }
+}

+ 38 - 0
tsconfig.base.json

@@ -0,0 +1,38 @@
+{
+  "compilerOptions": {
+    "target": "es2019",
+    "module": "commonjs",
+    "lib": ["dom", "dom.iterable", "esnext"],
+    "sourceMap": true,
+    "noEmit": true,
+    "removeComments": false,
+    "importHelpers": true,
+    "isolatedModules": true,
+
+    /* Strict Type-Checking Options */
+    // "strict": true,
+    "strictNullChecks": true,
+    "noImplicitAny": false,
+
+    /* Additional Checks */
+    "noUnusedLocals": false,
+    "noUnusedParameters": false,
+
+    /* Module Resolution Options */
+    "moduleResolution": "node",
+    "typeRoots": [
+      "./node_modules/@types"
+    ],
+    "allowSyntheticDefaultImports": true,
+    "esModuleInterop": true,
+
+    /* Misc */
+    "preserveConstEnums": true,
+    "forceConsistentCasingInFileNames": true,
+    "resolveJsonModule": true,
+
+    /* Experimental Options */
+    "experimentalDecorators": true,
+    "emitDecoratorMetadata": true
+  }
+}