domma-cms 0.110.0 → 0.111.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "domma-cms",
3
- "version": "0.110.0",
3
+ "version": "0.111.0",
4
4
  "description": "File-based CMS powered by Domma and Fastify. Run npx domma-cms my-site to create a new project.",
5
5
  "type": "module",
6
6
  "main": "server/server.js",
@@ -104,23 +104,46 @@ export async function createTransport(smtp) {
104
104
  });
105
105
  }
106
106
 
107
+ /**
108
+ * The From header as nodemailer's {name, address} object, never a string: a
109
+ * string is re-parsed as an address list, so a display name holding `"`, `<`
110
+ * or a comma could name a different sender - and some callers pass a name a
111
+ * site user typed. nodemailer encodes an object's name as a phrase.
112
+ *
113
+ * @param {string} name
114
+ * @param {string} address
115
+ * @returns {{name: string, address: string}|string}
116
+ */
117
+ export function fromHeader(name, address) {
118
+ const clean = String(name ?? '').replace(/[\r\n]+/g, ' ').trim();
119
+ return clean ? {name: clean, address} : address;
120
+ }
121
+
107
122
  /**
108
123
  * Send a generic transactional email.
109
124
  *
125
+ * `replyTo`, `headers` and `attachments` are passed to nodemailer only when
126
+ * given - an inline image is an attachment with a `cid` the html refers to
127
+ * as `<img src="cid:...">`.
128
+ *
110
129
  * @param {import('nodemailer').Transporter} transport
111
- * @param {{ from: string, fromName: string, to: string, subject: string, html: string, text: string }} opts
130
+ * @param {{ from: string, fromName: string, to: string, subject: string, html: string, text: string, replyTo?: string, headers?: Record<string, string>, attachments?: Array<object> }} opts
112
131
  * @returns {Promise<void>}
113
132
  * @throws {Error} If sending the email fails.
114
133
  */
115
- export async function sendEmail(transport, {from, fromName, to, subject, html, text}) {
134
+ export async function sendEmail(transport, {from, fromName, to, subject, html, text, replyTo, headers, attachments}) {
116
135
  try {
117
- const info = await transport.sendMail({
118
- from: `"${fromName}" <${from}>`,
136
+ const message = {
137
+ from: fromHeader(fromName, from),
119
138
  to,
120
139
  subject,
121
140
  text,
122
141
  html
123
- });
142
+ };
143
+ if (replyTo) message.replyTo = replyTo;
144
+ if (headers && Object.keys(headers).length) message.headers = headers;
145
+ if (Array.isArray(attachments) && attachments.length) message.attachments = attachments;
146
+ const info = await transport.sendMail(message);
124
147
  recordSendResult(true, info.messageId);
125
148
 
126
149
  const previewUrl = nodemailer.getTestMessageUrl(info);
@@ -176,7 +199,7 @@ export async function sendFormEmail(transport, { from, fromName, to, subject, fo
176
199
 
177
200
  try {
178
201
  const info = await transport.sendMail({
179
- from: `"${fromName}" <${from}>`,
202
+ from: fromHeader(fromName, from),
180
203
  to,
181
204
  subject,
182
205
  text,