UNPKG

4.2 kBJavaScriptView Raw
1'use strict'
2
3const nodemailer = require('nodemailer')
4const path = require('path')
5const debug = require('../debug').email
6
7/**
8 * Models a Nodemailer-based email sending service.
9 *
10 * @see https://nodemailer.com/about/
11 */
12class EmailService {
13 /**
14 * @constructor
15 *
16 * @param templatePath {string} Path to the email templates directory
17 *
18 * @param config {Object} Nodemailer configuration object
19 * @see https://nodemailer.com/smtp/
20 *
21 * Transport SMTP config options:
22 * @param config.host {string} e.g. 'smtp.gmail.com'
23 * @param config.port {string} e.g. '465'
24 * @param config.secure {boolean} Whether to use TLS when connecting to server
25 *
26 * Transport authentication config options:
27 * @param config.auth {Object}
28 * @param config.auth.user {string} Smtp username (e.g. 'alice@gmail.com')
29 * @param config.auth.pass {string} Smtp password
30 *
31 * Optional default Sender / `from:` address:
32 * @param [config.sender] {string} e.g. 'Solid Server <no-reply@databox.me>'
33 */
34 constructor (templatePath, config) {
35 this.mailer = nodemailer.createTransport(config)
36
37 this.sender = this.initSender(config)
38
39 this.templatePath = templatePath
40 }
41
42 /**
43 * Returns the default Sender address based on config.
44 *
45 * Note that if using Gmail for SMTP transport, Gmail ignores the sender
46 * `from:` address and uses the SMTP username instead (`auth.user`).
47 *
48 * @param config {Object}
49 *
50 * The sender is derived from either:
51 * @param [config.sender] {string} e.g. 'Solid Server <no-reply@databox.me>'
52 *
53 * or, if explicit sender is not passed in, uses:
54 * @param [config.host] {string} SMTP host from transport config
55 *
56 * @return {string} Sender `from:` address
57 */
58 initSender (config) {
59 let sender
60
61 if (config.sender) {
62 sender = config.sender
63 } else {
64 sender = `no-reply@${config.host}`
65 }
66
67 return sender
68 }
69
70 /**
71 * Sends an email (passes it through to nodemailer).
72 *
73 * @param email {Object}
74 *
75 * @return {Promise<EmailResponse>}
76 */
77 sendMail (email) {
78 email.from = email.from || this.sender
79
80 debug('Sending email to ' + email.to)
81 return this.mailer.sendMail(email)
82 }
83
84 /**
85 * Sends an email using a saved email template.
86 * Usage:
87 *
88 * ```
89 * let data = { webid: 'https://example.com/alice#me', ... }
90 *
91 * emailService.sendWithTemplate('welcome', data)
92 * .then(response => {
93 * // email sent using the 'welcome' template
94 * })
95 * ```
96 *
97 * @param templateName {string} Name of a template file in the email-templates
98 * dir, no extension necessary.
99 *
100 * @param data {Object} Key/value hashmap of data for an email template.
101 *
102 * @return {Promise<EmailResponse>}
103 */
104 sendWithTemplate (templateName, data) {
105 return Promise.resolve()
106 .then(() => {
107 let renderedEmail = this.emailFromTemplate(templateName, data)
108
109 return this.sendMail(renderedEmail)
110 })
111 }
112
113 /**
114 * Returns an email from a rendered template.
115 *
116 * @param templateName {string}
117 * @param data {Object} Key/value hashmap of data for an email template.
118 *
119 * @return {Object} Rendered email object from template
120 */
121 emailFromTemplate (templateName, data) {
122 let template = this.readTemplate(templateName)
123
124 return Object.assign({}, template.render(data), data)
125 }
126
127 /**
128 * Reads (requires) and returns the contents of an email template file, for
129 * a given template name.
130 *
131 * @param templateName {string}
132 *
133 * @throws {Error} If the template could not be found
134 *
135 * @return {Object}
136 */
137 readTemplate (templateName) {
138 let templateFile = this.templatePathFor(templateName)
139 let template
140
141 try {
142 template = require(templateFile)
143 } catch (error) {
144 throw new Error('Cannot find email template: ' + templateFile)
145 }
146
147 return template
148 }
149
150 /**
151 * Returns a template file path for a given template name.
152 *
153 * @param templateName {string}
154 *
155 * @return {string}
156 */
157 templatePathFor (templateName) {
158 return path.join(this.templatePath, templateName)
159 }
160}
161
162module.exports = EmailService