dev-mailbox.ts 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. import { LanguageCode } from '@vendure/common/lib/generated-types';
  2. import { Channel, RequestContext } from '@vendure/core';
  3. import express from 'express';
  4. import fs from 'fs-extra';
  5. import http from 'http';
  6. import path from 'path';
  7. import { EmailEventHandler } from './event-handler';
  8. import { EmailPluginDevModeOptions, EventWithContext } from './types';
  9. /**
  10. * An email inbox application that serves the contents of the dev mode `outputPath` directory.
  11. */
  12. export class DevMailbox {
  13. server: http.Server;
  14. private handleMockEventFn: (
  15. handler: EmailEventHandler<string, any>,
  16. event: EventWithContext,
  17. ) => void | undefined;
  18. serve(options: EmailPluginDevModeOptions) {
  19. const { outputPath, handlers, mailboxPort } = options;
  20. const server = express();
  21. server.get('/', (req, res) => {
  22. res.sendFile(path.join(__dirname, '../../dev-mailbox.html'));
  23. });
  24. server.get('/list', async (req, res) => {
  25. const list = await fs.readdir(outputPath);
  26. const contents = await this.getEmailList(outputPath);
  27. res.send(contents);
  28. });
  29. server.get('/types', async (req, res) => {
  30. res.send(handlers.map(h => h.type));
  31. });
  32. server.get('/generate/:type/:languageCode', async (req, res) => {
  33. const { type, languageCode } = req.params;
  34. if (this.handleMockEventFn) {
  35. const handler = handlers.find(h => h.type === type);
  36. if (!handler || !handler.mockEvent) {
  37. res.statusCode = 404;
  38. res.send({ success: false, error: `No mock event registered for type "${type}"` });
  39. return;
  40. }
  41. try {
  42. await this.handleMockEventFn(handler, {
  43. ...handler.mockEvent,
  44. ctx: this.createRequestContext(languageCode as LanguageCode),
  45. });
  46. res.send({ success: true });
  47. } catch (e) {
  48. res.statusCode = 500;
  49. res.send({ success: false, error: e.message });
  50. }
  51. return;
  52. } else {
  53. res.send({ success: false, error: `Mock email generation not set up.` });
  54. }
  55. });
  56. server.get('/item/:id', async (req, res) => {
  57. const fileName = req.params.id;
  58. const content = await this.getEmail(outputPath, fileName);
  59. res.send(content);
  60. });
  61. this.server = server.listen(mailboxPort);
  62. }
  63. handleMockEvent(handler: (handler: EmailEventHandler<string, any>, event: EventWithContext) => void) {
  64. this.handleMockEventFn = handler;
  65. }
  66. destroy() {
  67. this.server.close();
  68. }
  69. private async getEmailList(outputPath: string) {
  70. const list = await fs.readdir(outputPath);
  71. const contents: any[] = [];
  72. for (const fileName of list) {
  73. const json = await fs.readFile(path.join(outputPath, fileName), 'utf-8');
  74. const content = JSON.parse(json);
  75. contents.push({
  76. fileName,
  77. date: content.date,
  78. subject: content.subject,
  79. recipient: content.recipient,
  80. });
  81. }
  82. contents.sort((a, b) => {
  83. return +new Date(b.date) - +new Date(a.date);
  84. });
  85. return contents;
  86. }
  87. private async getEmail(outputPath: string, fileName: string) {
  88. const safeSuffix = path.normalize(fileName).replace(/^(\.\.(\/|\\|$))+/, '');
  89. const safeFilePath = path.join(outputPath, safeSuffix);
  90. const json = await fs.readFile(safeFilePath, 'utf-8');
  91. const content = JSON.parse(json);
  92. return content;
  93. }
  94. private createRequestContext(languageCode: LanguageCode): RequestContext {
  95. return new RequestContext({
  96. languageCode,
  97. apiType: 'admin',
  98. session: {} as any,
  99. isAuthorized: false,
  100. authorizedAsOwnerOnly: true,
  101. channel: new Channel(),
  102. });
  103. }
  104. }