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

Merge pull request #3787 from weseek/imprv/slackbot-proxy

Imprv/slackbot proxy
Yuki Takei 4 лет назад
Родитель
Сommit
2a0725a7ed

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

@@ -17,7 +17,11 @@
     "test:lint:fix": "eslint src --ext .ts --fix",
     "version": "node -p \"require('./package.json').version\""
   },
+  "// comments for dependencies": {
+    "express-graceful-exit": "0.5.2 includes a typings file error: https://github.com/emostar/express-graceful-exit/issues/24"
+  },
   "dependencies": {
+    "@godaddy/terminus": "^4.8.0",
     "@growi/slack": "^0.9.0-RC",
     "@slack/oauth": "^2.0.1",
     "@slack/web-api": "^6.1.0",
@@ -33,6 +37,9 @@
     "cookie-parser": "^1.4.5",
     "cross-env": "^7.0.0",
     "dotenv-flow": "^3.2.0",
+    "express-bunyan-logger": "^1.3.3",
+    "express-graceful-exit": "=0.5.0",
+    "helmet": "^4.6.0",
     "method-override": "^3.0.0",
     "mysql2": "^2.2.5",
     "typeorm": "^0.2.31",
@@ -47,6 +54,7 @@
     "@typescript-eslint/parser": "^4.18.0",
     "browser-bunyan": "^1.6.3",
     "eslint-import-resolver-typescript": "^2.4.0",
+    "morgan": "^1.10.0",
     "ts-jest": "^26.5.4",
     "ts-node": "^9.1.1",
     "ts-node-dev": "^1.1.6",

+ 83 - 14
packages/slackbot-proxy/src/Server.ts

@@ -1,16 +1,30 @@
 import { Configuration, Inject, InjectorService } from '@tsed/di';
-import { PlatformApplication } from '@tsed/common';
-import '@tsed/platform-express'; // /!\ keep this import
+import { HttpServer, PlatformApplication } from '@tsed/common';
+import '@tsed/platform-express'; // !! DO NOT MODIFY !!
+import '@tsed/typeorm'; // !! DO NOT MODIFY !! -- https://github.com/tsedio/tsed/issues/1332#issuecomment-837840612
+import '@tsed/swagger';
+
 import bodyParser from 'body-parser';
 import compress from 'compression';
 import cookieParser from 'cookie-parser';
 import methodOverride from 'method-override';
-import '@tsed/swagger';
-import { TypeORMService } from '@tsed/typeorm';
+import helmet from 'helmet';
+import { Express } from 'express';
+import expressBunyanLogger from 'express-bunyan-logger';
+import gracefulExit from 'express-graceful-exit';
+
 import { ConnectionOptions } from 'typeorm';
+import { createTerminus } from '@godaddy/terminus';
 
+import swaggerSettingsForDev from '~/config/swagger/config.dev';
+import swaggerSettingsForProd from '~/config/swagger/config.prod';
+import loggerFactory from '~/utils/logger';
 
 export const rootDir = __dirname;
+const isProduction = process.env.NODE_ENV === 'production';
+
+const logger = loggerFactory('slackbot-proxy:server');
+
 
 const connectionOptions: ConnectionOptions = {
   // The 'name' property must be set. Otherwise, the 'name' will be '0' and won't work well. -- 2021.04.05 Yuki Takei
@@ -25,18 +39,34 @@ const connectionOptions: ConnectionOptions = {
   synchronize: true,
 } as ConnectionOptions;
 
+const swaggerSettings = isProduction ? swaggerSettingsForProd : swaggerSettingsForDev;
+const helmetOptions = isProduction ? {} : {
+  contentSecurityPolicy: {
+    directives: {
+      defaultSrc: ['\'self\''],
+      styleSrc: ['\'self\'', '\'unsafe-inline\''],
+      imgSrc: ['\'self\'', 'data:', 'validator.swagger.io'],
+      scriptSrc: ['\'self\'', 'https: \'unsafe-inline\''],
+    },
+  },
+};
 
 @Configuration({
   rootDir,
   acceptMimes: ['application/json'],
   httpPort: process.env.PORT || 8080,
   httpsPort: false, // CHANGE
+  // disable RequestLogger of @tsed/logger
+  logger: { logRequest: false },
   mount: {
     '/': [
       `${rootDir}/controllers/*.ts`,
       `${rootDir}/middlewares/*.ts`,
     ],
   },
+  middlewares: [
+    helmet(helmetOptions),
+  ],
   componentsScan: [
     `${rootDir}/services/*.ts`,
   ],
@@ -54,12 +84,7 @@ const connectionOptions: ConnectionOptions = {
       ],
     } as ConnectionOptions,
   ],
-  swagger: [
-    {
-      path: '/docs',
-      specVersion: '3.0.1',
-    },
-  ],
+  swagger: swaggerSettings,
   exclude: [
     '**/*.spec.ts',
   ],
@@ -67,7 +92,7 @@ const connectionOptions: ConnectionOptions = {
 export class Server {
 
   @Inject()
-  app: PlatformApplication;
+  app: PlatformApplication<Express>;
 
   @Configuration()
   settings: Configuration;
@@ -81,10 +106,19 @@ export class Server {
     if (serverUri === undefined) {
       throw new Error('The environment variable \'SERVER_URI\' must be defined.');
     }
+
+    const server = this.injector.get<HttpServer>(HttpServer);
+
+    // init express-graceful-exit
+    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+    gracefulExit.init(server!);
   }
 
   $beforeRoutesInit(): void {
+    const expressApp = this.app.getApp();
+
     this.app
+      .use(gracefulExit.middleware(expressApp))
       .use(cookieParser())
       .use(compress({}))
       .use(methodOverride())
@@ -92,11 +126,46 @@ export class Server {
       .use(bodyParser.urlencoded({
         extended: true,
       }));
+
+    this.setupLogger();
   }
 
-  async $onReady(): Promise<void> {
-    // for synchromizing when boot
-    this.injector.get<TypeORMService>(TypeORMService);
+  $beforeListen(): void {
+    const expressApp = this.app.getApp();
+    const server = this.injector.get<HttpServer>(HttpServer);
+
+    // init terminus
+    createTerminus(server, {
+      onSignal: async() => {
+        logger.info('server is starting cleanup');
+        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+        gracefulExit.gracefulExitHandler(expressApp, server!);
+      },
+      onShutdown: async() => {
+        logger.info('cleanup finished, server is shutting down');
+      },
+    });
+  }
+
+  /**
+   * Setup logger for requests
+   */
+  private setupLogger(): void {
+    // use bunyan
+    if (isProduction) {
+      const logger = loggerFactory('express');
+
+      this.app.use(expressBunyanLogger({
+        logger,
+        excludes: ['*'],
+      }));
+    }
+    // use morgan
+    else {
+      // eslint-disable-next-line @typescript-eslint/no-var-requires
+      const morgan = require('morgan');
+      this.app.use(morgan('dev'));
+    }
   }
 
 }

+ 10 - 0
packages/slackbot-proxy/src/config/swagger/config.dev.ts

@@ -0,0 +1,10 @@
+import { SwaggerSettings } from '@tsed/swagger';
+
+const settings: SwaggerSettings[] = [
+  {
+    path: '/docs',
+    specVersion: '3.0.1',
+  },
+];
+
+export default settings;

+ 5 - 0
packages/slackbot-proxy/src/config/swagger/config.prod.ts

@@ -0,0 +1,5 @@
+import { SwaggerSettings } from '@tsed/swagger';
+
+const settings: SwaggerSettings[] = [];
+
+export default settings;

+ 47 - 0
yarn.lock

@@ -1433,6 +1433,13 @@
   resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46"
   integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==
 
+"@godaddy/terminus@^4.8.0":
+  version "4.8.0"
+  resolved "https://registry.yarnpkg.com/@godaddy/terminus/-/terminus-4.8.0.tgz#7f1258abd731adcf5f08d8ff1aa0216a02d65062"
+  integrity sha512-C3u+LTmlhtqsk1Sjw9UDTAXfVngby62dJL71LyhpRcwX3FDeLb0yd1Rmxh1OjH5HouOr0IIuU4dhiJMT6NNXog==
+  dependencies:
+    stoppable "^1.1.0"
+
 "@google-cloud/common@^3.6.0":
   version "3.6.0"
   resolved "https://registry.yarnpkg.com/@google-cloud/common/-/common-3.6.0.tgz#c2f6da5f79279a4a9ac7c71fc02d582beab98e8b"
@@ -4368,6 +4375,13 @@ basic-auth@~2.0.0:
   dependencies:
     safe-buffer "5.1.1"
 
+basic-auth@~2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a"
+  integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==
+  dependencies:
+    safe-buffer "5.1.2"
+
 batch@0.6.1:
   version "0.6.1"
   resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16"
@@ -7864,6 +7878,13 @@ express-form@~0.12.0:
     object-additions "^0.5.1"
     validator "^2.1.0"
 
+express-graceful-exit@=0.5.0:
+  version "0.5.0"
+  resolved "https://registry.yarnpkg.com/express-graceful-exit/-/express-graceful-exit-0.5.0.tgz#fcde6495af84f361e66f119bf0e3eca69955f9f1"
+  integrity sha512-gZkxdmgYz6VVHjZiMTaO59N875ugmBnzopF95q8Fzmws86JLNWMwGjLVG4i+99WO4v9L5wl6vKYFyGoHKoNJ7A==
+  dependencies:
+    underscore "^1.4.4"
+
 express-session@^1.16.1:
   version "1.16.1"
   resolved "https://registry.yarnpkg.com/express-session/-/express-session-1.16.1.tgz#251ff9776c59382301de6c8c33411af357ed439c"
@@ -9352,6 +9373,11 @@ helmet@^3.13.0:
     referrer-policy "1.1.0"
     x-xss-protection "1.1.0"
 
+helmet@^4.6.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/helmet/-/helmet-4.6.0.tgz#579971196ba93c5978eb019e4e8ec0e50076b4df"
+  integrity sha512-HVqALKZlR95ROkrnesdhbbZJFi/rIVSoNq6f3jA/9u6MIbTsPh3xZwihjeI5+DO/2sOV6HMHooXcEOuwskHpTg==
+
 hex-color-regex@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e"
@@ -12559,6 +12585,17 @@ mongoose@5.10.11:
     sift "7.0.1"
     sliced "1.0.1"
 
+morgan@^1.10.0:
+  version "1.10.0"
+  resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7"
+  integrity sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==
+  dependencies:
+    basic-auth "~2.0.1"
+    debug "2.6.9"
+    depd "~2.0.0"
+    on-finished "~2.3.0"
+    on-headers "~1.0.2"
+
 morgan@^1.9.0:
   version "1.9.0"
   resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.9.0.tgz#d01fa6c65859b76fcf31b3cb53a3821a311d8051"
@@ -17382,6 +17419,11 @@ sticky-events@^3.1.3:
   resolved "https://registry.yarnpkg.com/sticky-events/-/sticky-events-3.1.3.tgz#7b6b4091988b87b9f4e711c7c6532de07ab156dd"
   integrity sha512-nTm2bDaYTXFHAyQS59mWDRnnno/D8oj3C4JddOdipq6ZRnLLqjj+PeyCSbHPwMVdfvQoKwmMmAztp+YybDhvtA==
 
+stoppable@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/stoppable/-/stoppable-1.1.0.tgz#32da568e83ea488b08e4d7ea2c3bcc9d75015d5b"
+  integrity sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==
+
 stream-browserify@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db"
@@ -18716,6 +18758,11 @@ umask@^1.1.0:
   resolved "https://registry.yarnpkg.com/umask/-/umask-1.1.0.tgz#f29cebf01df517912bb58ff9c4e50fde8e33320d"
   integrity sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0=
 
+underscore@^1.4.4:
+  version "1.13.1"
+  resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.1.tgz#0c1c6bd2df54b6b69f2314066d65b6cde6fcf9d1"
+  integrity sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==
+
 unherit@^1.0.4:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.2.tgz#14f1f397253ee4ec95cec167762e77df83678449"