@lenne.tech/nest-server 11.38.0 → 11.39.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.
Files changed (83) hide show
  1. package/.claude/rules/configurable-features.md +23 -1
  2. package/.claude/rules/package-management.md +66 -0
  3. package/.claude/rules/testing.md +59 -5
  4. package/FRAMEWORK-API.md +1 -1
  5. package/dist/config.env.js +4 -2
  6. package/dist/config.env.js.map +1 -1
  7. package/dist/core/common/helpers/config.helper.d.ts +2 -0
  8. package/dist/core/common/helpers/config.helper.js +18 -0
  9. package/dist/core/common/helpers/config.helper.js.map +1 -1
  10. package/dist/core/common/helpers/cookies.helper.d.ts +3 -0
  11. package/dist/core/common/helpers/cookies.helper.js +9 -0
  12. package/dist/core/common/helpers/cookies.helper.js.map +1 -1
  13. package/dist/core/common/helpers/input.helper.d.ts +1 -0
  14. package/dist/core/common/helpers/input.helper.js +4 -0
  15. package/dist/core/common/helpers/input.helper.js.map +1 -1
  16. package/dist/core/common/helpers/service.helper.js +6 -1
  17. package/dist/core/common/helpers/service.helper.js.map +1 -1
  18. package/dist/core/common/interceptors/check-security.interceptor.js +1 -0
  19. package/dist/core/common/interceptors/check-security.interceptor.js.map +1 -1
  20. package/dist/core/common/interfaces/server-options.interface.d.ts +1 -0
  21. package/dist/core/common/services/email.service.d.ts +4 -1
  22. package/dist/core/common/services/email.service.js +25 -2
  23. package/dist/core/common/services/email.service.js.map +1 -1
  24. package/dist/core/common/services/module.service.js +1 -0
  25. package/dist/core/common/services/module.service.js.map +1 -1
  26. package/dist/core/modules/better-auth/better-auth.config.js +7 -0
  27. package/dist/core/modules/better-auth/better-auth.config.js.map +1 -1
  28. package/dist/core/modules/better-auth/core-better-auth-api.middleware.js +3 -1
  29. package/dist/core/modules/better-auth/core-better-auth-api.middleware.js.map +1 -1
  30. package/dist/core/modules/better-auth/core-better-auth-email-verification.service.d.ts +3 -1
  31. package/dist/core/modules/better-auth/core-better-auth-email-verification.service.js +30 -5
  32. package/dist/core/modules/better-auth/core-better-auth-email-verification.service.js.map +1 -1
  33. package/dist/core/modules/better-auth/core-better-auth-error-codes.helper.d.ts +2 -0
  34. package/dist/core/modules/better-auth/core-better-auth-error-codes.helper.js +54 -0
  35. package/dist/core/modules/better-auth/core-better-auth-error-codes.helper.js.map +1 -0
  36. package/dist/core/modules/better-auth/index.d.ts +1 -0
  37. package/dist/core/modules/better-auth/index.js +1 -0
  38. package/dist/core/modules/better-auth/index.js.map +1 -1
  39. package/dist/core/modules/error-code/error-codes.d.ts +27 -0
  40. package/dist/core/modules/error-code/error-codes.js +24 -0
  41. package/dist/core/modules/error-code/error-codes.js.map +1 -1
  42. package/dist/core/modules/hub/core-hub.service.js +1 -0
  43. package/dist/core/modules/hub/core-hub.service.js.map +1 -1
  44. package/dist/core/modules/user/core-user.model.d.ts +1 -0
  45. package/dist/core/modules/user/core-user.model.js +11 -1
  46. package/dist/core/modules/user/core-user.model.js.map +1 -1
  47. package/dist/core/modules/user/core-user.service.d.ts +9 -0
  48. package/dist/core/modules/user/core-user.service.js +96 -4
  49. package/dist/core/modules/user/core-user.service.js.map +1 -1
  50. package/dist/server/modules/error-code/error-codes.d.ts +3 -0
  51. package/dist/server/modules/user/user.model.d.ts +5 -0
  52. package/dist/server/modules/user/user.service.js +12 -6
  53. package/dist/server/modules/user/user.service.js.map +1 -1
  54. package/dist/templates/password-reset-de.ejs +12 -0
  55. package/dist/templates/password-reset-en.ejs +12 -0
  56. package/dist/templates/password-reset.ejs +1 -0
  57. package/dist/tsconfig.build.tsbuildinfo +1 -1
  58. package/docs/REQUEST-LIFECYCLE.md +14 -0
  59. package/migration-guides/11.37.x-to-11.38.x.md +18 -1
  60. package/migration-guides/11.38.x-to-11.39.x.md +456 -0
  61. package/package.json +1 -1
  62. package/src/config.env.ts +19 -3
  63. package/src/core/common/helpers/config.helper.ts +79 -0
  64. package/src/core/common/helpers/cookies.helper.ts +38 -0
  65. package/src/core/common/helpers/input.helper.ts +37 -0
  66. package/src/core/common/helpers/service.helper.ts +9 -1
  67. package/src/core/common/interceptors/check-security.interceptor.ts +1 -0
  68. package/src/core/common/interfaces/server-options.interface.ts +103 -3
  69. package/src/core/common/services/email.service.ts +46 -1
  70. package/src/core/common/services/module.service.ts +1 -0
  71. package/src/core/modules/better-auth/better-auth.config.ts +25 -0
  72. package/src/core/modules/better-auth/core-better-auth-api.middleware.ts +8 -1
  73. package/src/core/modules/better-auth/core-better-auth-email-verification.service.ts +101 -6
  74. package/src/core/modules/better-auth/core-better-auth-error-codes.helper.ts +146 -0
  75. package/src/core/modules/better-auth/index.ts +1 -0
  76. package/src/core/modules/error-code/error-codes.ts +55 -0
  77. package/src/core/modules/hub/core-hub.service.ts +1 -0
  78. package/src/core/modules/user/core-user.model.ts +28 -1
  79. package/src/core/modules/user/core-user.service.ts +267 -5
  80. package/src/server/modules/user/user.service.ts +26 -7
  81. package/src/templates/password-reset-de.ejs +12 -0
  82. package/src/templates/password-reset-en.ejs +12 -0
  83. package/src/templates/password-reset.ejs +1 -0
@@ -91,21 +91,40 @@ export class UserService extends CoreUserService<User, UserInput, UserCreateInpu
91
91
  * Better-Auth makes on the IAM path.
92
92
  */
93
93
  async sendPasswordResetMail(email: string, serviceOptions?: ServiceOptions): Promise<null | User> {
94
- // Set password reset token
95
- const user = await super.setPasswordResetTokenForEmail(email, serviceOptions);
94
+ // `createPasswordResetToken`, not `setPasswordResetTokenForEmail`: the latter returns the user
95
+ // through `process()`, where the security interceptor strips `passwordResetToken`. Reading the
96
+ // token off that object is what mailed the word `undefined` to a real recipient.
97
+ const created = await this.createPasswordResetToken(email, serviceOptions);
96
98
 
97
- if (!user) {
99
+ if (!created) {
98
100
  // Unknown address, enumeration protection on. Answer exactly as for a known one.
99
101
  return null;
100
102
  }
101
103
 
104
+ // `buildPasswordResetLink`, not string concatenation: `email.passwordResetLink` has no default,
105
+ // and this config file does not set one, so concatenating produced `undefined/<token>`.
106
+ const link = this.buildPasswordResetLink(created.token);
107
+ if (!link) {
108
+ // Sending a mail whose link cannot work is worse than sending none. The recipient has no
109
+ // second way in, and a dead link gives them nothing to act on — while no mail at least reads
110
+ // as "try again". The address is still answered exactly as a known one.
111
+ this.userServiceLogger.error(
112
+ 'Password reset mail not sent: no reset link could be built. Set `email.passwordResetLink` or `appUrl`.',
113
+ );
114
+ return created.user;
115
+ }
116
+
102
117
  // Deliberately NOT awaited — see the note above.
103
118
  void this.emailService
104
- .sendMail(user.email, 'Password reset', {
119
+ .sendMail(created.user.email, 'Password reset', {
105
120
  htmlTemplate: 'password-reset',
106
121
  templateData: {
107
- link: `${this.configService.configFastButReadOnly.email.passwordResetLink}/${user.passwordResetToken}`,
108
- name: user.username,
122
+ link,
123
+ // The mail is the only place the recipient can learn there IS a deadline. Without it an
124
+ // expired link is indistinguishable from a broken one — the same experience this whole
125
+ // area was repaired for.
126
+ linkExpiresInMinutes: this.passwordResetTokenExpiryMinutes(),
127
+ name: created.user.username,
109
128
  },
110
129
  })
111
130
  .catch((error: unknown) => {
@@ -115,7 +134,7 @@ export class UserService extends CoreUserService<User, UserInput, UserCreateInpu
115
134
  });
116
135
 
117
136
  // Return user
118
- return user;
137
+ return created.user;
119
138
  }
120
139
 
121
140
  /**
@@ -50,6 +50,18 @@
50
50
  Falls die Schaltfläche nicht funktioniert, kopiere diesen Link in deinen Browser:<br>
51
51
  <a href="<%= link %>" style="color:#1d4ed8; word-break:break-all; overflow-wrap:break-word;"><%= link %></a>
52
52
  </p>
53
+ <%
54
+ /* Optional, like `appName`: a caller that omits it must not turn a password-reset
55
+ mail into a 500. Rendered only for a real, positive value — a deployment that
56
+ switched the expiry off (`0`) has no deadline to announce. */
57
+ const expiryMinutes = (typeof linkExpiresInMinutes !== 'undefined' && Number(linkExpiresInMinutes) > 0)
58
+ ? Number(linkExpiresInMinutes) : 0;
59
+ -%>
60
+ <% if (expiryMinutes) { %>
61
+ <p style="margin:16px 0 0 0; font-size:13px; line-height:20px; color:#737373;">
62
+ Der Link ist <%= expiryMinutes %> Minuten lang gültig. Danach kannst du einfach einen neuen anfordern.
63
+ </p>
64
+ <% } %>
53
65
  </td>
54
66
  </tr>
55
67
  <tr>
@@ -50,6 +50,18 @@
50
50
  If the button doesn't work, copy and paste this link into your browser:<br>
51
51
  <a href="<%= link %>" style="color:#1d4ed8; word-break:break-all; overflow-wrap:break-word;"><%= link %></a>
52
52
  </p>
53
+ <%
54
+ /* Optional, like `appName`: a caller that omits it must not turn a password-reset
55
+ mail into a 500. Rendered only for a real, positive value — a deployment that
56
+ switched the expiry off (`0`) has no deadline to announce. */
57
+ const expiryMinutes = (typeof linkExpiresInMinutes !== 'undefined' && Number(linkExpiresInMinutes) > 0)
58
+ ? Number(linkExpiresInMinutes) : 0;
59
+ -%>
60
+ <% if (expiryMinutes) { %>
61
+ <p style="margin:16px 0 0 0; font-size:13px; line-height:20px; color:#737373;">
62
+ This link is valid for <%= expiryMinutes %> minutes. After that, just request a new one.
63
+ </p>
64
+ <% } %>
53
65
  </td>
54
66
  </tr>
55
67
  <tr>
@@ -1,3 +1,4 @@
1
1
  <h1>Hello <%= name %>,</h1>
2
2
  <p>you requested a link for setting a new password. Here it is.</p><br />
3
3
  <a href="<%= link %>">Reset password</a>
4
+ <% if (typeof linkExpiresInMinutes !== 'undefined' && Number(linkExpiresInMinutes) > 0) { %><br /><p>This link is valid for <%= Number(linkExpiresInMinutes) %> minutes. After that, just request a new one.</p><% } %>