Server.ts 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. import '@tsed/platform-express'; // !! DO NOT MODIFY !!
  2. import '@tsed/swagger';
  3. import '@tsed/typeorm'; // !! DO NOT MODIFY !! -- https://github.com/tsedio/tsed/issues/1332#issuecomment-837840612
  4. import { createTerminus } from '@godaddy/terminus';
  5. import { HttpServer, PlatformApplication } from '@tsed/common';
  6. import { Configuration, Inject, InjectorService } from '@tsed/di';
  7. import bodyParser from 'body-parser';
  8. import compress from 'compression';
  9. import cookieParser from 'cookie-parser';
  10. import type { Express } from 'express';
  11. import expressBunyanLogger from 'express-bunyan-logger';
  12. import helmet from 'helmet';
  13. import methodOverride from 'method-override';
  14. import type { ConnectionOptions } from 'typeorm';
  15. import { getConnectionManager } from 'typeorm';
  16. import swaggerSettingsForDev from '~/config/swagger/config.dev';
  17. import swaggerSettingsForProd from '~/config/swagger/config.prod';
  18. import loggerFactory from '~/utils/logger';
  19. import { GlobalHttpErrorHandlingMiddleware } from './middlewares/GlobalHttpErrorHandlingMiddleware';
  20. import './filters/CustomHttpErrorFilter';
  21. import './filters/ResourceNotFoundFilter';
  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
  38. ? swaggerSettingsForProd
  39. : swaggerSettingsForDev;
  40. const helmetOptions = isProduction
  41. ? {
  42. contentSecurityPolicy: false,
  43. expectCt: false,
  44. referrerPolicy: false,
  45. permittedCrossDomainPolicies: false,
  46. }
  47. : {
  48. contentSecurityPolicy: {
  49. directives: {
  50. defaultSrc: ["'self'"],
  51. styleSrc: ["'self'", "'unsafe-inline'"],
  52. imgSrc: ["'self'", 'data:', 'https:'],
  53. scriptSrc: ["'self'", "https: 'unsafe-inline'"],
  54. },
  55. },
  56. expectCt: false,
  57. referrerPolicy: false,
  58. permittedCrossDomainPolicies: false,
  59. };
  60. @Configuration({
  61. rootDir,
  62. acceptMimes: ['application/json'],
  63. httpPort: process.env.PORT || 8080,
  64. httpsPort: false, // CHANGE
  65. // disable RequestLogger of @tsed/logger
  66. logger: { logRequest: false },
  67. mount: {
  68. '/': [`${rootDir}/controllers/*.ts`, `${rootDir}/middlewares/*.ts`],
  69. },
  70. middlewares: [helmet(helmetOptions)],
  71. componentsScan: [`${rootDir}/services/*.ts`],
  72. typeorm: [
  73. {
  74. ...connectionOptions,
  75. entities: [`${rootDir}/entities/*{.ts,.js}`],
  76. migrations: [`${rootDir}/migrations/*{.ts,.js}`],
  77. subscribers: [`${rootDir}/subscribers/*{.ts,.js}`],
  78. } as ConnectionOptions,
  79. ],
  80. swagger: swaggerSettings,
  81. exclude: ['**/*.spec.ts'],
  82. viewsDir: `${rootDir}/views`,
  83. views: {
  84. root: `${rootDir}/views`,
  85. viewEngine: 'ejs',
  86. extensions: {
  87. ejs: 'ejs',
  88. },
  89. },
  90. statics: {
  91. '/': [
  92. {
  93. root: `${rootDir}/public`,
  94. },
  95. ],
  96. },
  97. })
  98. export class Server {
  99. @Inject()
  100. app: PlatformApplication<Express>;
  101. @Configuration()
  102. settings: Configuration;
  103. @Inject()
  104. injector: InjectorService;
  105. $beforeInit(): void {
  106. const serverUri = process.env.SERVER_URI;
  107. if (serverUri === undefined) {
  108. throw new Error("The environment variable 'SERVER_URI' must be defined.");
  109. }
  110. }
  111. $beforeRoutesInit(): void {
  112. this.app
  113. .use(cookieParser())
  114. .use(compress({}))
  115. .use(methodOverride())
  116. .use(bodyParser.json())
  117. .use(
  118. bodyParser.urlencoded({
  119. extended: true,
  120. }),
  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(
  150. expressBunyanLogger({
  151. logger,
  152. excludes: ['*'],
  153. }),
  154. );
  155. }
  156. // use morgan
  157. else {
  158. const morgan = require('morgan');
  159. this.app.use(morgan('dev'));
  160. }
  161. }
  162. }