You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

92 lines
4.0 KiB

  1. const fs = require('fs-extra');
  2. const { getFilesArray, toSystemPath } = require('../core/path');
  3. const { basename, posix } = require('path');
  4. const { v4: uuidv4 } = require('uuid');
  5. const IMPORTANCE = { 0: 'low', 1: 'normal', 2: 'high' };
  6. module.exports = {
  7. setup: function(options, name) {
  8. if (!name) throw new Error('mail.setup has no name.');
  9. this.setMailer(name, options);
  10. },
  11. send: async function(options) {
  12. let setup = this.getMailer(this.parseOptional(options.instance, 'string', 'system'));
  13. let subject = this.parseRequired(options.subject, 'string', 'mail.send: subject is required.');
  14. let fromEmail = this.parseRequired(options.fromEmail, 'string', 'mail.send: fromEmail is required.');
  15. let fromName = this.parseOptional(options.fromName, 'string', '');
  16. let toEmail = this.parseRequired(options.toEmail, 'string', 'mail.send: toEmail is required.');
  17. let toName = this.parseOptional(options.toName, 'string', '');
  18. let replyTo = this.parseOptional(options.replyTo, 'string', '');
  19. let cc = this.parseOptional(options.cc, 'string', '');
  20. let bcc = this.parseOptional(options.bcc, 'string', '');
  21. let source = this.parseOptional(options.source, 'string', 'static'); // static, file
  22. let contentType = this.parseOptional(options.contentType, 'string', 'text'); // text / html
  23. let body = this.parseOptional(options.body, 'string', '');
  24. let bodyFile = this.parseOptional(options.bodyFile, 'string', '');
  25. let embedImages = this.parseOptional(options.embedImages, 'boolean', false);
  26. let priority = IMPORTANCE[this.parseOptional(options.importance, 'number', 1)];
  27. let attachments = this.parseOptional(options.attachments, '*', []); // "/file.ext" / ["/file.ext"] / {path:"/file.ext"} / [{path:"/file.ext"}]
  28. let from = fromName ? `"${fromName}" <${fromEmail}>` : fromEmail;
  29. let to = toName ? `"${toName}" <${toEmail}>` : toEmail;
  30. let text = body;
  31. let html = null;
  32. if (source == 'file') {
  33. body = this.parse(await fs.readFile(toSystemPath(bodyFile), 'utf8'));
  34. }
  35. if (attachments) {
  36. attachments = getFilesArray(attachments).map((path) => ({ filename: basename(path), path }));
  37. }
  38. if (contentType == 'html') {
  39. html = body;
  40. if (embedImages) {
  41. let cid = {};
  42. html = html.replace(/(?:"|')([^"']+\.(jpg|png|gif))(?:"|')/gi, (m, url) => {
  43. let path = toSystemPath(url);
  44. if (fs.existsSync(path)) {
  45. if (!cid[path]) {
  46. cid[path] = uuidv4();
  47. attachments.push({
  48. filename: basename(path),
  49. path: path,
  50. cid: cid[path]
  51. });
  52. }
  53. return `"cid:${cid[path]}"`;
  54. } else {
  55. console.warn(`${path} not found`);
  56. }
  57. return `"${url}"`;
  58. });
  59. }
  60. if (this.req.get) { // we can only do this if we have a request to get our hostname
  61. const hasProxy = !!this.req.get('x-forwarded-host');
  62. const host = hasProxy ? `${this.req.protocol}://${this.req.hostname}` : this.req.get('host');
  63. html = html.replace(/(href|src)(?:\s*=\s*)(?:"|')([^"']+)(?:"|')/gi, (m, attr, url) => {
  64. if (!url.includes(':')) {
  65. url = posix.join(host, url);
  66. }
  67. return `${attr}="${url}"`;
  68. });
  69. }
  70. }
  71. const nodemailer = require('nodemailer');
  72. let transport = nodemailer.createTransport(setup);
  73. return transport.sendMail({ from, to, cc, bcc, replyTo, subject, html, text, priority, attachments });
  74. },
  75. };