email-processor.ts 3.2 KB

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