Server.ts 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. import { Configuration, Inject, InjectorService } from '@tsed/di';
  2. import { HttpServer, PlatformApplication } from '@tsed/common';
  3. import '@tsed/platform-express'; // !! DO NOT MODIFY !!
  4. import '@tsed/typeorm'; // !! DO NOT MODIFY !! -- https://github.com/tsedio/tsed/issues/1332#issuecomment-837840612
  5. import '@tsed/swagger';
  6. import bodyParser from 'body-parser';
  7. import compress from 'compression';
  8. import cookieParser from 'cookie-parser';
  9. import methodOverride from 'method-override';
  10. import helmet from 'helmet';
  11. import { Express } from 'express';
  12. import expressBunyanLogger from 'express-bunyan-logger';
  13. import gracefulExit from 'express-graceful-exit';
  14. import { ConnectionOptions } from 'typeorm';
  15. import { createTerminus } from '@godaddy/terminus';
  16. import swaggerSettingsForDev from '~/config/swagger/config.dev';
  17. import swaggerSettingsForProd from '~/config/swagger/config.prod';
  18. import loggerFactory from '~/utils/logger';
  19. export const rootDir = __dirname;
  20. const isProduction = process.env.NODE_ENV === 'production';
  21. const logger = loggerFactory('slackbot-proxy:server');
  22. const connectionOptions: ConnectionOptions = {
  23. // The 'name' property must be set. Otherwise, the 'name' will be '0' and won't work well. -- 2021.04.05 Yuki Takei
  24. // see: https://github.com/TypedProject/tsed/blob/7630cda20a1f6fa3a692ecc3e6cd51d37bc3c45f/packages/typeorm/src/utils/createConnection.ts#L10
  25. name: 'default',
  26. type: process.env.TYPEORM_CONNECTION,
  27. host: process.env.TYPEORM_HOST,
  28. port: process.env.TYPEORM_PORT,
  29. database: process.env.TYPEORM_DATABASE,
  30. username: process.env.TYPEORM_USERNAME,
  31. password: process.env.TYPEORM_PASSWORD,
  32. synchronize: true,
  33. } as ConnectionOptions;
  34. const swaggerSettings = isProduction ? swaggerSettingsForProd : swaggerSettingsForDev;
  35. const helmetOptions = isProduction ? {} : {
  36. contentSecurityPolicy: {
  37. directives: {
  38. defaultSrc: ['\'self\''],
  39. styleSrc: ['\'self\'', '\'unsafe-inline\''],
  40. imgSrc: ['\'self\'', 'data:', 'validator.swagger.io'],
  41. scriptSrc: ['\'self\'', 'https: \'unsafe-inline\''],
  42. },
  43. },
  44. };
  45. @Configuration({
  46. rootDir,
  47. acceptMimes: ['application/json'],
  48. httpPort: process.env.PORT || 8080,
  49. httpsPort: false, // CHANGE
  50. // disable RequestLogger of @tsed/logger
  51. logger: { logRequest: false },
  52. mount: {
  53. '/': [
  54. `${rootDir}/controllers/*.ts`,
  55. `${rootDir}/middlewares/*.ts`,
  56. ],
  57. },
  58. middlewares: [
  59. helmet(helmetOptions),
  60. ],
  61. componentsScan: [
  62. `${rootDir}/services/*.ts`,
  63. ],
  64. typeorm: [
  65. {
  66. ...connectionOptions,
  67. entities: [
  68. `${rootDir}/entities/*{.ts,.js}`,
  69. ],
  70. migrations: [
  71. `${rootDir}/migrations/*{.ts,.js}`,
  72. ],
  73. subscribers: [
  74. `${rootDir}/subscribers/*{.ts,.js}`,
  75. ],
  76. } as ConnectionOptions,
  77. ],
  78. swagger: swaggerSettings,
  79. exclude: [
  80. '**/*.spec.ts',
  81. ],
  82. })
  83. export class Server {
  84. @Inject()
  85. app: PlatformApplication<Express>;
  86. @Configuration()
  87. settings: Configuration;
  88. @Inject()
  89. injector: InjectorService;
  90. $beforeInit(): Promise<any> | void {
  91. const serverUri = process.env.SERVER_URI;
  92. if (serverUri === undefined) {
  93. throw new Error('The environment variable \'SERVER_URI\' must be defined.');
  94. }
  95. const server = this.injector.get<HttpServer>(HttpServer);
  96. // init express-graceful-exit
  97. // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
  98. gracefulExit.init(server!);
  99. }
  100. $beforeRoutesInit(): void {
  101. const expressApp = this.app.getApp();
  102. this.app
  103. .use(gracefulExit.middleware(expressApp))
  104. .use(cookieParser())
  105. .use(compress({}))
  106. .use(methodOverride())
  107. .use(bodyParser.json())
  108. .use(bodyParser.urlencoded({
  109. extended: true,
  110. }));
  111. this.setupLogger();
  112. }
  113. $beforeListen(): void {
  114. const expressApp = this.app.getApp();
  115. const server = this.injector.get<HttpServer>(HttpServer);
  116. // init terminus
  117. createTerminus(server, {
  118. onSignal: async() => {
  119. logger.info('server is starting cleanup');
  120. // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
  121. gracefulExit.gracefulExitHandler(expressApp, server!);
  122. },
  123. onShutdown: async() => {
  124. logger.info('cleanup finished, server is shutting down');
  125. },
  126. });
  127. }
  128. /**
  129. * Setup logger for requests
  130. */
  131. private setupLogger(): void {
  132. // use bunyan
  133. if (isProduction) {
  134. const logger = loggerFactory('express');
  135. this.app.use(expressBunyanLogger({
  136. logger,
  137. excludes: ['*'],
  138. }));
  139. }
  140. // use morgan
  141. else {
  142. // eslint-disable-next-line @typescript-eslint/no-var-requires
  143. const morgan = require('morgan');
  144. this.app.use(morgan('dev'));
  145. }
  146. }
  147. }