plugin.ts 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. import { AdminUiConfig } from '@vendure/common/lib/shared-types';
  2. import { createProxyHandler, InjectorFn, VendureConfig, VendurePlugin } from '@vendure/core';
  3. import express from 'express';
  4. import fs from 'fs-extra';
  5. import { Server } from 'http';
  6. import path from 'path';
  7. /**
  8. * @description
  9. * Configuration options for the {@link AdminUiPlugin}.
  10. *
  11. * @docsCategory AdminUiPlugin
  12. */
  13. export interface AdminUiOptions {
  14. /**
  15. * @description
  16. * The hostname of the server serving the static admin ui files.
  17. *
  18. * @default 'localhost'
  19. */
  20. hostname?: string;
  21. /**
  22. * @description
  23. * The port on which the server will listen.
  24. */
  25. port: number;
  26. /**
  27. * @description
  28. * The hostname of the Vendure server which the admin ui will be making API calls
  29. * to. If set to "auto", the admin ui app will determine the hostname from the
  30. * current location (i.e. `window.location.hostname`).
  31. *
  32. * @default 'auto'
  33. */
  34. apiHost?: string | 'auto';
  35. /**
  36. * @description
  37. * The port of the Vendure server which the admin ui will be making API calls
  38. * to. If set to "auto", the admin ui app will determine the port from the
  39. * current location (i.e. `window.location.port`).
  40. *
  41. * @default 'auto'
  42. */
  43. apiPort?: number | 'auto';
  44. }
  45. /**
  46. * @description
  47. * This plugin starts a static server for the Admin UI app, and proxies it via the `/admin/` path of the main Vendure server.
  48. *
  49. * The Admin UI allows you to administer all aspects of your store, from inventory management to order tracking. It is the tool used by
  50. * store administrators on a day-to-day basis for the management of the store.
  51. *
  52. * ## Installation
  53. *
  54. * `yarn add @vendure/admin-ui-plugin`
  55. *
  56. * or
  57. *
  58. * `npm install @vendure/admin-ui-plugin`
  59. *
  60. * @example
  61. * ```ts
  62. * import { AdminUiPlugin } from '@vendure/admin-ui-plugin';
  63. *
  64. * const config: VendureConfig = {
  65. * // Add an instance of the plugin to the plugins array
  66. * plugins: [
  67. * new AdminUiPlugin({ port: 3002 }),
  68. * ],
  69. * };
  70. * ```
  71. *
  72. * @docsCategory AdminUiPlugin
  73. */
  74. export class AdminUiPlugin implements VendurePlugin {
  75. private server: Server;
  76. constructor(private options: AdminUiOptions) {}
  77. /** @internal */
  78. async configure(config: Required<VendureConfig>): Promise<Required<VendureConfig>> {
  79. const route = 'admin';
  80. config.middleware.push({
  81. handler: createProxyHandler({ ...this.options, route, label: 'Admin UI' }),
  82. route,
  83. });
  84. const { adminApiPath } = config;
  85. const { apiHost, apiPort } = this.options;
  86. await this.overwriteAdminUiConfig(apiHost || 'auto', apiPort || 'auto', adminApiPath);
  87. return config;
  88. }
  89. /** @internal */
  90. onBootstrap(inject: InjectorFn): void | Promise<void> {
  91. const adminUiPath = this.getAdminUiPath();
  92. const assetServer = express();
  93. assetServer.use(express.static(adminUiPath));
  94. assetServer.use((req, res) => {
  95. res.sendFile(path.join(adminUiPath, 'index.html'));
  96. });
  97. this.server = assetServer.listen(this.options.port);
  98. }
  99. /** @internal */
  100. onClose(): Promise<void> {
  101. return new Promise(resolve => this.server.close(() => resolve()));
  102. }
  103. /**
  104. * Overwrites the parts of the admin-ui app's `vendure-ui-config.json` file relating to connecting to
  105. * the server admin API.
  106. */
  107. private async overwriteAdminUiConfig(host: string | 'auto', port: number | 'auto', adminApiPath: string) {
  108. const adminUiConfigPath = path.join(this.getAdminUiPath(), 'vendure-ui-config.json');
  109. const adminUiConfig = await fs.readFile(adminUiConfigPath, 'utf-8');
  110. const config: AdminUiConfig = JSON.parse(adminUiConfig);
  111. config.apiHost = host || 'http://localhost';
  112. config.apiPort = port;
  113. config.adminApiPath = adminApiPath;
  114. await fs.writeFile(adminUiConfigPath, JSON.stringify(config, null, 2));
  115. }
  116. private getAdminUiPath(): string {
  117. // attempt to read from the path location on a production npm install
  118. const prodPath = path.join(__dirname, '../admin-ui');
  119. if (fs.existsSync(path.join(prodPath, 'index.html'))) {
  120. return prodPath;
  121. }
  122. // attempt to read from the path on a development install
  123. const devPath = path.join(__dirname, '../lib/admin-ui');
  124. if (fs.existsSync(path.join(devPath, 'index.html'))) {
  125. return devPath;
  126. }
  127. throw new Error(`AdminUiPlugin: admin-ui app not found`);
  128. }
  129. }