Server.ts 4.8 KB

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