Server.ts 5.2 KB

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