@lenne.tech/nest-server 11.5.0 → 11.6.1

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": "@lenne.tech/nest-server",
3
- "version": "11.5.0",
3
+ "version": "11.6.1",
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",
@@ -77,24 +77,22 @@
77
77
  "node": ">= 20"
78
78
  },
79
79
  "dependencies": {
80
- "@apollo/gateway": "2.12.0",
81
80
  "@getbrevo/brevo": "3.0.1",
82
81
  "@nestjs/apollo": "13.1.0",
83
- "@nestjs/common": "11.1.8",
84
- "@nestjs/core": "11.1.8",
85
- "@nestjs/graphql": "13.1.0",
86
- "@nestjs/jwt": "11.0.1",
87
- "@nestjs/mongoose": "11.0.3",
82
+ "@nestjs/common": "11.1.9",
83
+ "@nestjs/core": "11.1.9",
84
+ "@nestjs/graphql": "13.2.0",
85
+ "@nestjs/jwt": "11.0.2",
86
+ "@nestjs/mongoose": "11.0.4",
88
87
  "@nestjs/passport": "11.0.5",
89
- "@nestjs/platform-express": "11.1.8",
90
- "@nestjs/schedule": "6.0.1",
91
- "@nestjs/swagger": "11.2.1",
88
+ "@nestjs/platform-express": "11.1.9",
89
+ "@nestjs/schedule": "6.1.0",
90
+ "@nestjs/swagger": "11.2.3",
92
91
  "@nestjs/terminus": "11.0.0",
93
92
  "apollo-server-core": "3.13.0",
94
- "apollo-server-express": "3.13.0",
95
93
  "bcrypt": "6.0.0",
96
94
  "class-transformer": "0.5.1",
97
- "class-validator": "0.14.2",
95
+ "class-validator": "0.14.3",
98
96
  "compression": "1.8.1",
99
97
  "cookie-parser": "1.4.7",
100
98
  "dotenv": "17.2.3",
@@ -110,41 +108,38 @@
110
108
  "mongoose": "8.19.3",
111
109
  "multer": "2.0.2",
112
110
  "node-mailjet": "6.0.11",
113
- "nodemailer": "7.0.10",
111
+ "nodemailer": "7.0.11",
114
112
  "passport": "0.7.0",
115
113
  "passport-jwt": "4.0.1",
116
114
  "reflect-metadata": "0.2.2",
117
115
  "rfdc": "1.4.1",
118
116
  "rxjs": "7.8.2",
117
+ "ts-jest": "29.4.6",
119
118
  "yuml-diagram": "1.2.0"
120
119
  },
121
120
  "devDependencies": {
122
- "@babel/plugin-proposal-private-methods": "7.18.6",
123
121
  "@compodoc/compodoc": "1.1.32",
124
122
  "@lenne.tech/eslint-config-ts": "2.1.4",
125
- "@nestjs/cli": "11.0.10",
123
+ "@nestjs/cli": "11.0.14",
126
124
  "@nestjs/schematics": "11.0.9",
127
- "@nestjs/testing": "11.1.8",
125
+ "@nestjs/testing": "11.1.9",
128
126
  "@swc/cli": "0.7.9",
129
- "@swc/core": "1.15.0",
130
- "@swc/jest": "0.2.39",
127
+ "@swc/core": "1.15.3",
131
128
  "@types/compression": "1.8.1",
132
129
  "@types/cookie-parser": "1.4.10",
133
130
  "@types/ejs": "3.1.5",
134
131
  "@types/express": "4.17.21",
135
- "@types/jest": "30.0.0",
136
- "@types/lodash": "4.17.20",
132
+ "@types/lodash": "4.17.21",
137
133
  "@types/multer": "2.0.0",
138
- "@types/node": "24.10.0",
139
- "@types/nodemailer": "7.0.3",
134
+ "@types/node": "25.0.1",
135
+ "@types/nodemailer": "7.0.4",
140
136
  "@types/passport": "1.0.17",
141
137
  "@types/supertest": "6.0.3",
142
- "@typescript-eslint/eslint-plugin": "8.46.3",
143
- "@typescript-eslint/parser": "8.46.3",
144
- "@vitest/coverage-v8": "4.0.9",
145
- "@vitest/ui": "4.0.9",
138
+ "@typescript-eslint/eslint-plugin": "8.49.0",
139
+ "@typescript-eslint/parser": "8.49.0",
140
+ "@vitest/coverage-v8": "4.0.15",
141
+ "@vitest/ui": "4.0.15",
146
142
  "ansi-colors": "4.1.3",
147
- "coffeescript": "2.7.0",
148
143
  "eslint": "9.39.1",
149
144
  "eslint-config-prettier": "10.1.8",
150
145
  "eslint-plugin-unused-imports": "4.3.0",
@@ -155,25 +150,23 @@
155
150
  "grunt-contrib-watch": "1.1.0",
156
151
  "grunt-sync": "0.8.2",
157
152
  "husky": "9.1.7",
158
- "jest": "30.2.0",
159
- "nodemon": "3.1.10",
153
+ "nodemon": "3.1.11",
160
154
  "npm-watch": "0.13.0",
161
- "pm2": "6.0.13",
162
- "prettier": "3.6.2",
155
+ "pm2": "6.0.14",
156
+ "prettier": "3.7.4",
163
157
  "pretty-quick": "4.2.2",
164
- "rimraf": "6.1.0",
158
+ "rimraf": "6.1.2",
165
159
  "supertest": "7.1.4",
166
- "ts-jest": "29.4.5",
167
160
  "ts-loader": "9.5.4",
168
161
  "ts-morph": "27.0.2",
169
162
  "ts-node": "10.9.2",
170
163
  "tsconfig-paths": "4.2.0",
171
164
  "typescript": "5.9.3",
172
- "unplugin-swc": "1.5.8",
173
- "vite": "7.2.2",
165
+ "unplugin-swc": "1.5.9",
166
+ "vite": "7.2.7",
174
167
  "vite-plugin-node": "7.0.0",
175
168
  "vite-tsconfig-paths": "5.1.4",
176
- "vitest": "4.0.9",
169
+ "vitest": "4.0.15",
177
170
  "yalc": "1.0.0-pre.53"
178
171
  },
179
172
  "overrides": {
@@ -31,12 +31,13 @@ export class CheckSecurityInterceptor implements NestInterceptor {
31
31
  // Get current user
32
32
  const user = getContextData(context)?.currentUser || null;
33
33
 
34
- // Set force mode for sign in and sign up
34
+ // Set force mode for sign in and sign up (both GraphQL and REST)
35
35
  let force = false;
36
36
  if (!user) {
37
37
  // Here the name is used and not the class itself, because the concrete class is located in the respective project.
38
- // In case of an override it is better to use the concrete class directly (context.getClass() instead of context.getClasss()?.name).
39
- if (context.getClass()?.name === 'AuthResolver') {
38
+ // In case of an override it is better to use the concrete class directly (context.getClass() instead of context.getClass()?.name).
39
+ const className = context.getClass()?.name;
40
+ if (className === 'AuthResolver' || className === 'AuthController') {
40
41
  force = true;
41
42
  }
42
43
  }
@@ -76,7 +77,7 @@ export class CheckSecurityInterceptor implements NestInterceptor {
76
77
 
77
78
  // Check if data is writeable (e.g. objects from direct access to json files via http are not writable)
78
79
  if (data && typeof data === 'object') {
79
- const writeable = !Object.keys(data).find(key => !Object.getOwnPropertyDescriptor(data, key).writable);
80
+ const writeable = !Object.keys(data).find((key) => !Object.getOwnPropertyDescriptor(data, key).writable);
80
81
  if (!writeable) {
81
82
  return data;
82
83
  }
@@ -88,7 +89,7 @@ export class CheckSecurityInterceptor implements NestInterceptor {
88
89
  (item) => {
89
90
  if (!item || typeof item !== 'object' || typeof item.securityCheck !== 'function') {
90
91
  if (Array.isArray(item)) {
91
- return item.filter(i => i !== undefined);
92
+ return item.filter((i) => i !== undefined);
92
93
  }
93
94
  return item;
94
95
  }
@@ -15,6 +15,18 @@ import { ICoreAuthUser } from '../interfaces/core-auth-user.interface';
15
15
  import { JwtPayload } from '../interfaces/jwt-payload.interface';
16
16
  import { CoreAuthUserService } from './core-auth-user.service';
17
17
 
18
+ /**
19
+ * Options for getResult method
20
+ */
21
+ export interface GetResultOptions {
22
+ /** Current refresh token (for renewal) */
23
+ currentRefreshToken?: string;
24
+ /** Additional data (deviceId, deviceDescription, etc.) */
25
+ data?: { [key: string]: any; deviceId?: string };
26
+ /** Service options including currentUser for securityCheck */
27
+ serviceOptions?: ServiceOptions;
28
+ }
29
+
18
30
  /**
19
31
  * CoreAuthService to handle user authentication
20
32
  */
@@ -71,16 +83,17 @@ export class CoreAuthService {
71
83
  /**
72
84
  * Refresh tokens
73
85
  */
74
- async refreshTokens(user: ICoreAuthUser, currentRefreshToken: string) {
86
+ async refreshTokens(user: ICoreAuthUser, currentRefreshToken: string, serviceOptions?: ServiceOptions) {
75
87
  // Create new tokens
76
88
  const { deviceDescription, deviceId } = this.decodeJwt(currentRefreshToken);
77
89
  const tokens = await this.createTokens(user.id, { deviceDescription, deviceId });
78
90
  tokens.refreshToken = await this.updateRefreshToken(user, currentRefreshToken, tokens.refreshToken);
79
91
 
80
- // Return
81
- return CoreAuthModel.map({
82
- ...tokens,
83
- user: await this.userService.prepareOutput(user),
92
+ // Return with currentUser set so securityCheck knows user is requesting own data
93
+ return this.getResult(user, {
94
+ currentRefreshToken,
95
+ data: { deviceDescription, deviceId },
96
+ serviceOptions: { ...serviceOptions, currentUser: user },
84
97
  });
85
98
  }
86
99
 
@@ -114,8 +127,11 @@ export class CoreAuthService {
114
127
  throw new UnauthorizedException('Wrong password');
115
128
  }
116
129
 
117
- // Return tokens and user
118
- return this.getResult(user, { deviceDescription, deviceId });
130
+ // Return tokens and user with currentUser set so securityCheck knows user is requesting own data
131
+ return this.getResult(user, {
132
+ data: { deviceDescription, deviceId },
133
+ serviceOptions: { ...serviceOptions, currentUser: user },
134
+ });
119
135
  }
120
136
 
121
137
  /**
@@ -138,8 +154,11 @@ export class CoreAuthService {
138
154
  // Set device ID
139
155
  const { deviceDescription, deviceId } = input;
140
156
 
141
- // Return tokens and user
142
- return this.getResult(user, { deviceDescription, deviceId });
157
+ // Return tokens and user with currentUser set so securityCheck knows user is requesting own data
158
+ return this.getResult(user, {
159
+ data: { deviceDescription, deviceId },
160
+ serviceOptions: { ...serviceOptions, currentUser: user },
161
+ });
143
162
  } catch (err) {
144
163
  if (err?.message === 'Unprocessable Entity') {
145
164
  throw new BadRequestException('Email address already in use');
@@ -171,12 +190,16 @@ export class CoreAuthService {
171
190
 
172
191
  /**
173
192
  * Rest result with user and tokens
193
+ *
194
+ * @param user - The authenticated user
195
+ * @param options - Optional configuration for result generation
196
+ * @param options.data - Additional data (deviceId, deviceDescription, etc.)
197
+ * @param options.currentRefreshToken - Current refresh token (for renewal)
198
+ * @param options.serviceOptions - Service options including currentUser for securityCheck
174
199
  */
175
- protected async getResult(
176
- user: ICoreAuthUser,
177
- data?: { [key: string]: any; deviceId?: string },
178
- currentRefreshToken?: string,
179
- ) {
200
+ protected async getResult(user: ICoreAuthUser, options?: GetResultOptions) {
201
+ const { currentRefreshToken, data, serviceOptions } = options || {};
202
+
180
203
  // Create new tokens
181
204
  const tokens = await this.createTokens(user.id, data);
182
205
 
@@ -184,9 +207,10 @@ export class CoreAuthService {
184
207
  tokens.refreshToken = await this.updateRefreshToken(user, currentRefreshToken, tokens.refreshToken, data);
185
208
 
186
209
  // Return tokens and user
210
+ // Pass serviceOptions to prepareOutput so currentUser is available for securityCheck
187
211
  return CoreAuthModel.map({
188
212
  ...tokens,
189
- user: await this.userService.prepareOutput(user),
213
+ user: await this.userService.prepareOutput(user, serviceOptions),
190
214
  });
191
215
  }
192
216
 
@@ -199,10 +223,10 @@ export class CoreAuthService {
199
223
  path += '.refresh';
200
224
  }
201
225
  return (
202
- this.configService.getFastButReadOnly(`${path}.signInOptions.secret`)
203
- || this.configService.getFastButReadOnly(`${path}.signInOptions.secretOrPrivateKey`)
204
- || this.configService.getFastButReadOnly(`${path}.secret`)
205
- || this.configService.getFastButReadOnly(`${path}.secretOrPrivateKey`)
226
+ this.configService.getFastButReadOnly(`${path}.signInOptions.secret`) ||
227
+ this.configService.getFastButReadOnly(`${path}.signInOptions.secretOrPrivateKey`) ||
228
+ this.configService.getFastButReadOnly(`${path}.secret`) ||
229
+ this.configService.getFastButReadOnly(`${path}.secretOrPrivateKey`)
206
230
  );
207
231
  }
208
232