Server.ts 4.9 KB

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