Server.ts 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  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 ? swaggerSettingsForProd : swaggerSettingsForDev;
  40. const helmetOptions = isProduction ? {
  41. contentSecurityPolicy: false,
  42. expectCt: false,
  43. referrerPolicy: false,
  44. permittedCrossDomainPolicies: false,
  45. } : {
  46. contentSecurityPolicy: {
  47. directives: {
  48. defaultSrc: ['\'self\''],
  49. styleSrc: ['\'self\'', '\'unsafe-inline\''],
  50. imgSrc: ['\'self\'', 'data:', 'https:'],
  51. scriptSrc: ['\'self\'', 'https: \'unsafe-inline\''],
  52. },
  53. },
  54. expectCt: false,
  55. referrerPolicy: false,
  56. permittedCrossDomainPolicies: false,
  57. };
  58. @Configuration({
  59. rootDir,
  60. acceptMimes: ['application/json'],
  61. httpPort: process.env.PORT || 8080,
  62. httpsPort: false, // CHANGE
  63. // disable RequestLogger of @tsed/logger
  64. logger: { logRequest: false },
  65. mount: {
  66. '/': [
  67. `${rootDir}/controllers/*.ts`,
  68. `${rootDir}/middlewares/*.ts`,
  69. ],
  70. },
  71. middlewares: [
  72. helmet(helmetOptions),
  73. ],
  74. componentsScan: [
  75. `${rootDir}/services/*.ts`,
  76. ],
  77. typeorm: [
  78. {
  79. ...connectionOptions,
  80. entities: [
  81. `${rootDir}/entities/*{.ts,.js}`,
  82. ],
  83. migrations: [
  84. `${rootDir}/migrations/*{.ts,.js}`,
  85. ],
  86. subscribers: [
  87. `${rootDir}/subscribers/*{.ts,.js}`,
  88. ],
  89. } as ConnectionOptions,
  90. ],
  91. swagger: swaggerSettings,
  92. exclude: [
  93. '**/*.spec.ts',
  94. ],
  95. viewsDir: `${rootDir}/views`,
  96. views: {
  97. root: `${rootDir}/views`,
  98. viewEngine: 'ejs',
  99. extensions: {
  100. ejs: 'ejs',
  101. },
  102. },
  103. statics: {
  104. '/': [
  105. {
  106. root: `${rootDir}/public`,
  107. },
  108. ],
  109. },
  110. })
  111. export class Server {
  112. @Inject()
  113. app: PlatformApplication<Express>;
  114. @Configuration()
  115. settings: Configuration;
  116. @Inject()
  117. injector: InjectorService;
  118. $beforeInit(): Promise<any> | void {
  119. const serverUri = process.env.SERVER_URI;
  120. if (serverUri === undefined) {
  121. throw new Error('The environment variable \'SERVER_URI\' must be defined.');
  122. }
  123. }
  124. $beforeRoutesInit(): void {
  125. this.app
  126. .use(cookieParser())
  127. .use(compress({}))
  128. .use(methodOverride())
  129. .use(bodyParser.json())
  130. .use(bodyParser.urlencoded({
  131. extended: true,
  132. }));
  133. this.setupLogger();
  134. }
  135. $afterRoutesInit(): void {
  136. this.app.use(GlobalHttpErrorHandlingMiddleware);
  137. }
  138. $beforeListen(): void {
  139. const server = this.injector.get<HttpServer>(HttpServer);
  140. // init terminus
  141. createTerminus(server, {
  142. onSignal: async() => {
  143. logger.info('server is starting cleanup');
  144. const connectionManager = getConnectionManager();
  145. const defaultConnection = connectionManager.get('default');
  146. await defaultConnection.close();
  147. },
  148. onShutdown: async() => {
  149. logger.info('cleanup finished, server is shutting down');
  150. },
  151. });
  152. }
  153. /**
  154. * Setup logger for requests
  155. */
  156. private setupLogger(): void {
  157. // use bunyan
  158. if (isProduction) {
  159. const logger = loggerFactory('express');
  160. this.app.use(expressBunyanLogger({
  161. logger,
  162. excludes: ['*'],
  163. }));
  164. }
  165. // use morgan
  166. else {
  167. // eslint-disable-next-line @typescript-eslint/no-var-requires
  168. const morgan = require('morgan');
  169. this.app.use(morgan('dev'));
  170. }
  171. }
  172. }