email-processor.ts 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. import { InternalServerError, Logger } from '@vendure/core';
  2. import fs from 'fs-extra';
  3. import { deserializeAttachments } from './attachment-utils';
  4. import { isDevModeOptions } from './common';
  5. import { loggerCtx } from './constants';
  6. import { EmailSender } from './email-sender';
  7. import { HandlebarsMjmlGenerator } from './handlebars-mjml-generator';
  8. import { TemplateLoader } from './template-loader';
  9. import { EmailPluginOptions, EmailTransportOptions, IntermediateEmailDetails } from './types';
  10. /**
  11. * This class combines the template loading, generation, and email sending - the actual "work" of
  12. * the EmailPlugin. It is arranged this way primarily to accomodate easier testing, so that the
  13. * tests can be run without needing all the JobQueue stuff which would require a full e2e test.
  14. */
  15. export class EmailProcessor {
  16. protected templateLoader: TemplateLoader;
  17. protected emailSender: EmailSender;
  18. protected generator: HandlebarsMjmlGenerator;
  19. protected transport: EmailTransportOptions;
  20. constructor(protected options: EmailPluginOptions) {}
  21. async init() {
  22. this.templateLoader = new TemplateLoader(this.options.templatePath);
  23. this.emailSender = new EmailSender();
  24. this.generator = new HandlebarsMjmlGenerator();
  25. if (this.generator.onInit) {
  26. await this.generator.onInit.call(this.generator, this.options);
  27. }
  28. if (isDevModeOptions(this.options)) {
  29. this.transport = {
  30. type: 'file',
  31. raw: false,
  32. outputPath: this.options.outputPath,
  33. };
  34. } else {
  35. if (!this.options.transport) {
  36. throw new InternalServerError(
  37. `When devMode is not set to true, the 'transport' property must be set.`,
  38. );
  39. }
  40. this.transport = this.options.transport;
  41. }
  42. if (this.transport.type === 'file') {
  43. // ensure the configured directory exists before
  44. // we attempt to write files to it
  45. const emailPath = this.transport.outputPath;
  46. await fs.ensureDir(emailPath);
  47. }
  48. }
  49. async process(data: IntermediateEmailDetails) {
  50. try {
  51. const bodySource = await this.templateLoader.loadTemplate(data.type, data.templateFile);
  52. const generated = await this.generator.generate(
  53. data.from,
  54. data.subject,
  55. bodySource,
  56. data.templateVars,
  57. );
  58. const emailDetails = {
  59. ...generated,
  60. recipient: data.recipient,
  61. attachments: deserializeAttachments(data.attachments),
  62. };
  63. await this.emailSender.send(emailDetails, this.transport);
  64. return true;
  65. } catch (err: unknown) {
  66. if (err instanceof Error) {
  67. Logger.error(err.message, loggerCtx, err.stack);
  68. } else {
  69. Logger.error(String(err), loggerCtx);
  70. }
  71. return false;
  72. }
  73. }
  74. }