Server.ts 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  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. /* eslint-disable @typescript-eslint/consistent-type-imports */
  6. import { HttpServer, PlatformApplication } from '@tsed/common';
  7. import { Configuration, Inject, InjectorService } from '@tsed/di';
  8. /* eslint-enable @typescript-eslint/consistent-type-imports */
  9. import bodyParser from 'body-parser';
  10. import compress from 'compression';
  11. import cookieParser from 'cookie-parser';
  12. import type { Express } from 'express';
  13. import expressBunyanLogger from 'express-bunyan-logger';
  14. import helmet from 'helmet';
  15. import methodOverride from 'method-override';
  16. import type { ConnectionOptions } from 'typeorm';
  17. import { getConnectionManager } from 'typeorm';
  18. import swaggerSettingsForDev from '~/config/swagger/config.dev';
  19. import swaggerSettingsForProd from '~/config/swagger/config.prod';
  20. import loggerFactory from '~/utils/logger';
  21. import { GlobalHttpErrorHandlingMiddleware } from './middlewares/GlobalHttpErrorHandlingMiddleware';
  22. import './filters/CustomHttpErrorFilter';
  23. import './filters/ResourceNotFoundFilter';
  24. export const rootDir = __dirname;
  25. const isProduction = process.env.NODE_ENV === 'production';
  26. const logger = loggerFactory('slackbot-proxy:server');
  27. const connectionOptions: ConnectionOptions = {
  28. // The 'name' property must be set. Otherwise, the 'name' will be '0' and won't work well. -- 2021.04.05 Yuki Takei
  29. // see: https://github.com/TypedProject/tsed/blob/7630cda20a1f6fa3a692ecc3e6cd51d37bc3c45f/packages/typeorm/src/utils/createConnection.ts#L10
  30. name: 'default',
  31. type: process.env.TYPEORM_CONNECTION,
  32. host: process.env.TYPEORM_HOST,
  33. port: process.env.TYPEORM_PORT,
  34. database: process.env.TYPEORM_DATABASE,
  35. username: process.env.TYPEORM_USERNAME,
  36. password: process.env.TYPEORM_PASSWORD,
  37. synchronize: true,
  38. } as ConnectionOptions;
  39. const swaggerSettings = isProduction
  40. ? swaggerSettingsForProd
  41. : swaggerSettingsForDev;
  42. const helmetOptions = isProduction
  43. ? {
  44. contentSecurityPolicy: false,
  45. expectCt: false,
  46. referrerPolicy: false,
  47. permittedCrossDomainPolicies: false,
  48. }
  49. : {
  50. contentSecurityPolicy: {
  51. directives: {
  52. defaultSrc: ["'self'"],
  53. styleSrc: ["'self'", "'unsafe-inline'"],
  54. imgSrc: ["'self'", 'data:', 'https:'],
  55. scriptSrc: ["'self'", "https: 'unsafe-inline'"],
  56. },
  57. },
  58. expectCt: false,
  59. referrerPolicy: false,
  60. permittedCrossDomainPolicies: false,
  61. };
  62. @Configuration({
  63. rootDir,
  64. acceptMimes: ['application/json'],
  65. httpPort: process.env.PORT || 8080,
  66. httpsPort: false, // CHANGE
  67. // disable RequestLogger of @tsed/logger
  68. logger: { logRequest: false },
  69. mount: {
  70. '/': [`${rootDir}/controllers/*.ts`, `${rootDir}/middlewares/*.ts`],
  71. },
  72. middlewares: [helmet(helmetOptions)],
  73. componentsScan: [`${rootDir}/services/*.ts`],
  74. typeorm: [
  75. {
  76. ...connectionOptions,
  77. entities: [`${rootDir}/entities/*{.ts,.js}`],
  78. migrations: [`${rootDir}/migrations/*{.ts,.js}`],
  79. subscribers: [`${rootDir}/subscribers/*{.ts,.js}`],
  80. } as ConnectionOptions,
  81. ],
  82. swagger: swaggerSettings,
  83. exclude: ['**/*.spec.ts'],
  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(): 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(
  120. bodyParser.urlencoded({
  121. extended: true,
  122. }),
  123. );
  124. this.setupLogger();
  125. }
  126. $afterRoutesInit(): void {
  127. this.app.use(GlobalHttpErrorHandlingMiddleware);
  128. }
  129. $beforeListen(): void {
  130. const server = this.injector.get<HttpServer>(HttpServer);
  131. // init terminus
  132. createTerminus(server, {
  133. onSignal: async () => {
  134. logger.info('server is starting cleanup');
  135. const connectionManager = getConnectionManager();
  136. const defaultConnection = connectionManager.get('default');
  137. await defaultConnection.close();
  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(
  152. expressBunyanLogger({
  153. logger,
  154. excludes: ['*'],
  155. }),
  156. );
  157. }
  158. // use morgan
  159. else {
  160. // eslint-disable-next-line @typescript-eslint/no-var-requires
  161. const morgan = require('morgan');
  162. this.app.use(morgan('dev'));
  163. }
  164. }
  165. }