-
Notifications
You must be signed in to change notification settings - Fork 306
Expand file tree
/
Copy pathemail-service.mjs
More file actions
76 lines (67 loc) · 2.45 KB
/
email-service.mjs
File metadata and controls
76 lines (67 loc) · 2.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import nodemailer from 'nodemailer'
import path from 'path'
import debugModule from '../debug.mjs'
import { pathToFileURL } from 'url'
const debug = debugModule.email
class EmailService {
constructor (templatePath, config) {
this.mailer = nodemailer.createTransport(config)
this.sender = this.initSender(config)
this.templatePath = templatePath
}
initSender (config) {
let sender
if (config.sender) {
sender = config.sender
} else {
sender = `no-reply@${config.host}`
}
return sender
}
sendMail (email) {
email.from = email.from || this.sender
debug('Sending email to ' + email.to)
return this.mailer.sendMail(email)
}
sendWithTemplate (templateName, data) {
return Promise.resolve()
.then(async () => {
const renderedEmail = await this.emailFromTemplate(templateName, data)
return this.sendMail(renderedEmail)
})
}
async emailFromTemplate (templateName, data) {
const template = await this.readTemplate(templateName)
const renderFn = template.render ?? (typeof template.default === 'function' ? template.default : template.default?.render)
if (!renderFn) throw new Error('Template does not expose a render function: ' + templateName)
return Object.assign({}, renderFn(data), data)
}
async readTemplate (templateName) {
// Accept legacy `.js` templateName and prefer `.mjs`
let name = templateName
if (name.endsWith('.js')) name = name.replace(/\.js$/, '.mjs')
const templateFile = this.templatePathFor(name)
// Try dynamic import for ESM templates first
try {
const moduleUrl = pathToFileURL(templateFile).href
const mod = await import(moduleUrl)
return mod
} catch (err) {
// Fallback: if consumer passed a CommonJS template name (no .mjs), try requiring it
try {
const { createRequire } = await import('module')
const require = createRequire(import.meta.url)
// If templateName originally had .js, attempt that too
const cjsTemplateFile = this.templatePathFor(templateName)
const required = require(cjsTemplateFile)
return required
} catch (err2) {
throw new Error('Cannot find email template: ' + templateFile, { cause: err2 })
}
}
}
templatePathFor (templateName) {
return path.join(this.templatePath, templateName)
}
}
export default EmailService