bootstrap.ts 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. import { INestApplication } from '@nestjs/common';
  2. import { NestFactory } from '@nestjs/core';
  3. import { Type } from '@vendure/common/lib/shared-types';
  4. import { EntitySubscriberInterface } from 'typeorm';
  5. import { InternalServerError } from './common/error/errors';
  6. import { ReadOnlyRequired } from './common/types/common-types';
  7. import { getConfig, setConfig } from './config/config-helpers';
  8. import { DefaultLogger } from './config/logger/default-logger';
  9. import { Logger } from './config/logger/vendure-logger';
  10. import { VendureConfig } from './config/vendure-config';
  11. import { registerCustomEntityFields } from './entity/custom-entity-fields';
  12. import { logProxyMiddlewares } from './plugin/plugin-utils';
  13. export type VendureBootstrapFunction = (config: VendureConfig) => Promise<INestApplication>;
  14. /**
  15. * Bootstrap the Vendure server.
  16. */
  17. export async function bootstrap(userConfig: Partial<VendureConfig>): Promise<INestApplication> {
  18. const config = await preBootstrapConfig(userConfig);
  19. Logger.info(`Bootstrapping Vendure Server...`);
  20. // The AppModule *must* be loaded only after the entities have been set in the
  21. // config, so that they are available when the AppModule decorator is evaluated.
  22. // tslint:disable-next-line:whitespace
  23. const appModule = await import('./app.module');
  24. DefaultLogger.hideNestBoostrapLogs();
  25. let app: INestApplication;
  26. app = await NestFactory.create(appModule.AppModule, {
  27. cors: config.cors,
  28. logger: new Logger(),
  29. });
  30. DefaultLogger.restoreOriginalLogLevel();
  31. app.useLogger(new Logger());
  32. await runPluginOnBootstrapMethods(config, app);
  33. await app.listen(config.port, config.hostname);
  34. logWelcomeMessage(config);
  35. return app;
  36. }
  37. /**
  38. * Setting the global config must be done prior to loading the AppModule.
  39. */
  40. export async function preBootstrapConfig(
  41. userConfig: Partial<VendureConfig>,
  42. ): Promise<ReadOnlyRequired<VendureConfig>> {
  43. if (userConfig) {
  44. setConfig(userConfig);
  45. }
  46. // Entities *must* be loaded after the user config is set in order for the
  47. // base VendureEntity to be correctly configured with the primary key type
  48. // specified in the EntityIdStrategy.
  49. // tslint:disable-next-line:whitespace
  50. const pluginEntities = getEntitiesFromPlugins(userConfig);
  51. const entities = await getAllEntities(userConfig);
  52. const { coreSubscribersMap } = await import('./entity/subscribers');
  53. setConfig({
  54. dbConnectionOptions: {
  55. entities,
  56. subscribers: Object.values(coreSubscribersMap) as Array<Type<EntitySubscriberInterface>>,
  57. },
  58. });
  59. let config = getConfig();
  60. Logger.useLogger(config.logger);
  61. config = await runPluginConfigurations(config);
  62. registerCustomEntityFields(config);
  63. return config;
  64. }
  65. /**
  66. * Initialize any configured plugins.
  67. */
  68. async function runPluginConfigurations(
  69. config: ReadOnlyRequired<VendureConfig>,
  70. ): Promise<ReadOnlyRequired<VendureConfig>> {
  71. for (const plugin of config.plugins) {
  72. if (plugin.configure) {
  73. config = (await plugin.configure(config)) as ReadOnlyRequired<VendureConfig>;
  74. }
  75. }
  76. return config;
  77. }
  78. /**
  79. * Run the onBootstrap() method of any configured plugins.
  80. */
  81. export async function runPluginOnBootstrapMethods(
  82. config: ReadOnlyRequired<VendureConfig>,
  83. app: INestApplication,
  84. ): Promise<void> {
  85. function inject<T>(type: Type<T>): T {
  86. return app.get(type);
  87. }
  88. for (const plugin of config.plugins) {
  89. if (plugin.onBootstrap) {
  90. await plugin.onBootstrap(inject);
  91. const pluginName = plugin.constructor && plugin.constructor.name || '(anonymous plugin)';
  92. Logger.verbose(`Bootstrapped plugin ${pluginName}`);
  93. }
  94. }
  95. }
  96. /**
  97. * Returns an array of core entities and any additional entities defined in plugins.
  98. */
  99. async function getAllEntities(userConfig: Partial<VendureConfig>): Promise<Array<Type<any>>> {
  100. const { coreEntitiesMap } = await import('./entity/entities');
  101. const coreEntities = Object.values(coreEntitiesMap) as Array<Type<any>>;
  102. const pluginEntities = getEntitiesFromPlugins(userConfig);
  103. const allEntities: Array<Type<any>> = coreEntities;
  104. // Check to ensure that no plugins are defining entities with names
  105. // which conflict with existing entities.
  106. for (const pluginEntity of pluginEntities) {
  107. if (allEntities.find(e => e.name === pluginEntity.name)) {
  108. throw new InternalServerError(`error.entity-name-conflict`, { entityName: pluginEntity.name });
  109. } else {
  110. allEntities.push(pluginEntity);
  111. }
  112. }
  113. return [...coreEntities, ...pluginEntities];
  114. }
  115. /**
  116. * Collects all entities defined in plugins into a single array.
  117. */
  118. function getEntitiesFromPlugins(userConfig: Partial<VendureConfig>): Array<Type<any>> {
  119. if (!userConfig.plugins) {
  120. return [];
  121. }
  122. return userConfig.plugins
  123. .map(p => (p.defineEntities ? p.defineEntities() : []))
  124. .reduce((all, entities) => [...all, ...entities], []);
  125. }
  126. function logWelcomeMessage(config: VendureConfig) {
  127. let version: string;
  128. try {
  129. version = require('../package.json').version;
  130. } catch (e) {
  131. version = ' unknown';
  132. }
  133. Logger.info(`=================================================`);
  134. Logger.info(`Vendure server (v${version}) now running on port ${config.port}`);
  135. Logger.info(`Shop API: http://localhost:${config.port}/${config.shopApiPath}`);
  136. Logger.info(`Admin API: http://localhost:${config.port}/${config.adminApiPath}`);
  137. logProxyMiddlewares(config);
  138. Logger.info(`=================================================`);
  139. }