@lenne.tech/nest-server 11.43.5 → 11.43.6
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/.claude/rules/configurable-features.md +1 -0
- package/FRAMEWORK-API.md +2 -2
- package/dist/core/common/interfaces/server-options.interface.d.ts +1 -0
- package/dist/core/common/services/email.service.d.ts +3 -0
- package/dist/core/common/services/email.service.js +20 -0
- package/dist/core/common/services/email.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/migration-guides/11.43.5-to-11.43.6.md +53 -0
- package/package.json +1 -1
- package/src/core/common/interfaces/server-options.interface.ts +11 -0
- package/src/core/common/services/email.service.ts +33 -1
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Migration Guide: 11.43.5 → 11.43.6
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
| Category | Details |
|
|
6
|
+
| -------------------- | ------------------------------------------------------------------------------------------------------- |
|
|
7
|
+
| **New Features** | Optional Reply-To for outgoing mail: `email.defaultReplyTo` and a per-call `replyTo` (§1) |
|
|
8
|
+
| **Breaking Changes** | None |
|
|
9
|
+
| **Migration Effort** | None. Set `email.defaultReplyTo` only if answers to your mail should go to another address (§1) |
|
|
10
|
+
|
|
11
|
+
## Quick Migration
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm add @lenne.tech/nest-server@11.43.6
|
|
15
|
+
pnpm run update # nest-server-starter based projects
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Vendor-mode projects update the core to the tag `11.43.6`: `/lt-dev:backend:update-nest-server-core --target 11.43.6`.
|
|
19
|
+
Without `--target` the updater syncs to the latest tag, which is on the 12.x line.
|
|
20
|
+
|
|
21
|
+
## 1. Reply-To for outgoing mail
|
|
22
|
+
|
|
23
|
+
`EmailService.sendMail()` can set a `Reply-To` header, so a recipient's answer goes to another address than the
|
|
24
|
+
sender, e.g. a shared support mailbox while mail goes out from `noreply@…`. Nothing changes until you set it.
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
// config.env.ts
|
|
28
|
+
email: {
|
|
29
|
+
defaultSender: { email: 'noreply@example.com', name: 'Example' },
|
|
30
|
+
defaultReplyTo: 'support@example.com', // or '"Example Support" <support@example.com>'
|
|
31
|
+
},
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
As an environment variable:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
NSC__EMAIL__DEFAULT_REPLY_TO=support@example.com
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
A call can override it, or send one mail without it:
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
await this.emailService.sendMail(to, subject, { replyTo: 'sales@example.com', htmlTemplate, templateData });
|
|
44
|
+
await this.emailService.sendMail(to, subject, { replyTo: false, htmlTemplate, templateData });
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The default applies to every mail the service sends over SMTP, Better-Auth's verification and password-reset mail
|
|
48
|
+
included. Mail sent through Brevo (`brevo.apiKey`) or Mailjet is not affected: their templates carry their own
|
|
49
|
+
reply-to. A value that is not a string is ignored with one warning rather than stopping the mail.
|
|
50
|
+
|
|
51
|
+
## Module Documentation
|
|
52
|
+
|
|
53
|
+
- Configuration reference: `email.defaultReplyTo` in `FRAMEWORK-API.md` (`IServerOptions.email`)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lenne.tech/nest-server",
|
|
3
|
-
"version": "11.43.
|
|
3
|
+
"version": "11.43.6",
|
|
4
4
|
"description": "Modern, fast, powerful Node.js web framework in TypeScript based on Nest with a GraphQL API and a connection to MongoDB (or other databases).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"node",
|
|
@@ -2510,6 +2510,17 @@ export interface IServerOptions {
|
|
|
2510
2510
|
* SMTP and template configuration for sending emails
|
|
2511
2511
|
*/
|
|
2512
2512
|
email?: {
|
|
2513
|
+
/**
|
|
2514
|
+
* Default Reply-To of every mail `EmailService.sendMail()` sends (11.43.6+): where a recipient's answer goes when it
|
|
2515
|
+
* should not reach the sender address, e.g. a shared support mailbox. An address, or `"Name" <address>`.
|
|
2516
|
+
*
|
|
2517
|
+
* Absent (the default) → no Reply-To header, exactly as before. A call overrides it with `replyTo`, and
|
|
2518
|
+
* `replyTo: false` sends that one mail without it. Set it through `NSC__EMAIL__DEFAULT_REPLY_TO`. Applies to the
|
|
2519
|
+
* SMTP path (nodemailer, `email.smtp`): Brevo and Mailjet templates carry their own reply-to.
|
|
2520
|
+
* @default undefined
|
|
2521
|
+
*/
|
|
2522
|
+
defaultReplyTo?: string;
|
|
2523
|
+
|
|
2513
2524
|
/**
|
|
2514
2525
|
* Data for default sender
|
|
2515
2526
|
*/
|
|
@@ -28,6 +28,9 @@ export class EmailService implements OnModuleDestroy {
|
|
|
28
28
|
/** Once-per-process latch for the SMTP TLS warning — a per-send warning would be noise. */
|
|
29
29
|
private smtpTlsWarningEmitted = false;
|
|
30
30
|
|
|
31
|
+
/** Once-per-process latch for a Reply-To that is not a string. */
|
|
32
|
+
private replyToWarningEmitted = false;
|
|
33
|
+
|
|
31
34
|
/**
|
|
32
35
|
* Inject services
|
|
33
36
|
*/
|
|
@@ -76,6 +79,31 @@ export class EmailService implements OnModuleDestroy {
|
|
|
76
79
|
);
|
|
77
80
|
}
|
|
78
81
|
|
|
82
|
+
/**
|
|
83
|
+
* The Reply-To of one mail (11.43.6+): the call's `replyTo`, else `email.defaultReplyTo`, else none. `false` (or an
|
|
84
|
+
* empty string) sends this mail without one. A value that is not a string is dropped with a warning instead of
|
|
85
|
+
* failing the mail: a misconfigured default must not stop password-reset or verification mail.
|
|
86
|
+
*/
|
|
87
|
+
protected resolveReplyTo(replyTo: false | string | undefined): string | undefined {
|
|
88
|
+
if (replyTo === false) {
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
const value: unknown = replyTo ?? this.configService.getFastButReadOnly('email.defaultReplyTo');
|
|
92
|
+
if (value === undefined || value === null) {
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
if (typeof value === 'string') {
|
|
96
|
+
return value.trim() || undefined;
|
|
97
|
+
}
|
|
98
|
+
if (!this.replyToWarningEmitted) {
|
|
99
|
+
this.replyToWarningEmitted = true;
|
|
100
|
+
this.emailServiceLogger.warn(
|
|
101
|
+
`Reply-To ignored: expected an address string (email.defaultReplyTo / replyTo), got ${typeof value}.`,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
|
|
79
107
|
onModuleDestroy(): void {
|
|
80
108
|
if (this.cachedTransporter) {
|
|
81
109
|
this.cachedTransporter.close();
|
|
@@ -94,6 +122,8 @@ export class EmailService implements OnModuleDestroy {
|
|
|
94
122
|
attachments?: Attachment[];
|
|
95
123
|
html?: string;
|
|
96
124
|
htmlTemplate?: string;
|
|
125
|
+
/** Reply-To of this mail; overrides `email.defaultReplyTo`, `false` sends it without one (11.43.6+) */
|
|
126
|
+
replyTo?: false | string;
|
|
97
127
|
senderEmail?: string;
|
|
98
128
|
senderName?: string;
|
|
99
129
|
smtp?: MailTransportOptions;
|
|
@@ -189,11 +219,13 @@ export class EmailService implements OnModuleDestroy {
|
|
|
189
219
|
this.cachedSmtpConfig = smtpKey;
|
|
190
220
|
}
|
|
191
221
|
|
|
192
|
-
// Send mail
|
|
222
|
+
// Send mail; without a Reply-To the header is left out entirely
|
|
223
|
+
const replyTo = this.resolveReplyTo(config.replyTo);
|
|
193
224
|
return this.cachedTransporter.sendMail({
|
|
194
225
|
attachments,
|
|
195
226
|
from: `"${senderName}" <${senderEmail}>`,
|
|
196
227
|
html,
|
|
228
|
+
...(replyTo ? { replyTo } : {}),
|
|
197
229
|
subject,
|
|
198
230
|
text,
|
|
199
231
|
to: recipients,
|