@lenne.tech/nest-server 11.1.13 → 11.2.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 (45) hide show
  1. package/dist/core/common/helpers/gridfs.helper.d.ts +42 -0
  2. package/dist/core/common/helpers/gridfs.helper.js +107 -0
  3. package/dist/core/common/helpers/gridfs.helper.js.map +1 -0
  4. package/dist/core/common/services/brevo.service.d.ts +1 -0
  5. package/dist/core/common/services/brevo.service.js +19 -18
  6. package/dist/core/common/services/brevo.service.js.map +1 -1
  7. package/dist/core/common/services/crud.service.js +1 -1
  8. package/dist/core/common/services/crud.service.js.map +1 -1
  9. package/dist/core/modules/file/core-file.controller.d.ts +2 -1
  10. package/dist/core/modules/file/core-file.controller.js +1 -1
  11. package/dist/core/modules/file/core-file.controller.js.map +1 -1
  12. package/dist/core/modules/file/core-file.service.d.ts +7 -7
  13. package/dist/core/modules/file/core-file.service.js +28 -51
  14. package/dist/core/modules/file/core-file.service.js.map +1 -1
  15. package/dist/core/modules/file/interfaces/file-upload.interface.d.ts +1 -1
  16. package/dist/core/modules/user/core-user.service.js +2 -3
  17. package/dist/core/modules/user/core-user.service.js.map +1 -1
  18. package/dist/index.d.ts +2 -0
  19. package/dist/index.js +1 -0
  20. package/dist/index.js.map +1 -1
  21. package/dist/server/modules/file/file-info.model.d.ts +7 -3
  22. package/dist/server/modules/file/file.controller.d.ts +6 -4
  23. package/dist/server/modules/file/file.controller.js +15 -4
  24. package/dist/server/modules/file/file.controller.js.map +1 -1
  25. package/dist/server/modules/file/file.resolver.js +1 -1
  26. package/dist/server/modules/file/file.resolver.js.map +1 -1
  27. package/dist/server/modules/file/multer-config.service.d.ts +0 -2
  28. package/dist/server/modules/file/multer-config.service.js +3 -22
  29. package/dist/server/modules/file/multer-config.service.js.map +1 -1
  30. package/dist/server/modules/user/user.model.d.ts +7 -3
  31. package/dist/tsconfig.build.tsbuildinfo +1 -1
  32. package/dist/types/graphql-upload.d.ts +25 -0
  33. package/package.json +50 -52
  34. package/src/core/common/helpers/gridfs.helper.ts +227 -0
  35. package/src/core/common/services/brevo.service.ts +20 -18
  36. package/src/core/common/services/crud.service.ts +3 -3
  37. package/src/core/modules/file/core-file.controller.ts +3 -2
  38. package/src/core/modules/file/core-file.service.ts +49 -60
  39. package/src/core/modules/file/interfaces/file-upload.interface.ts +2 -1
  40. package/src/core/modules/user/core-user.service.ts +3 -3
  41. package/src/index.ts +1 -0
  42. package/src/server/modules/file/file.controller.ts +25 -7
  43. package/src/server/modules/file/file.resolver.ts +1 -2
  44. package/src/server/modules/file/multer-config.service.ts +6 -21
  45. package/src/types/graphql-upload.d.ts +25 -0
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Type definitions for graphql-upload
3
+ * graphql-upload uses deep imports, so we declare types for the specific modules
4
+ */
5
+
6
+ declare module 'graphql-upload/graphqlUploadExpress.js' {
7
+ import { RequestHandler } from 'express';
8
+
9
+ interface GraphQLUploadOptions {
10
+ maxFiles?: number;
11
+ maxFileSize?: number;
12
+ }
13
+
14
+ function graphqlUploadExpress(options?: GraphQLUploadOptions): RequestHandler;
15
+
16
+ export = graphqlUploadExpress;
17
+ }
18
+
19
+ declare module 'graphql-upload/GraphQLUpload.js' {
20
+ import { GraphQLScalarType } from 'graphql';
21
+
22
+ const GraphQLUpload: GraphQLScalarType;
23
+
24
+ export = GraphQLUpload;
25
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lenne.tech/nest-server",
3
- "version": "11.1.13",
3
+ "version": "11.2.0",
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",
@@ -14,7 +14,9 @@
14
14
  "homepage": "https://github.com/lenneTech/nest-server",
15
15
  "license": "MIT",
16
16
  "scripts": {
17
- "build": "rimraf dist && nest build",
17
+ "build": "rimraf dist && nest build && npm run build:copy-types && npm run build:add-type-references",
18
+ "build:copy-types": "mkdir -p dist/types && cp src/types/*.d.ts dist/types/",
19
+ "build:add-type-references": "node scripts/add-type-references.js",
18
20
  "build:pack": "npm pack && echo 'use file:/ROOT_PATH_TO_TGZ_FILE to integrate the package'",
19
21
  "build:dev": "npm run build && yalc push --private",
20
22
  "docs": "npm run docs:ci && open http://127.0.0.1:8080/ && open ./public/index.html && compodoc -p tsconfig.json -s ",
@@ -65,43 +67,41 @@
65
67
  "node": ">= 20"
66
68
  },
67
69
  "dependencies": {
68
- "@apollo/gateway": "2.10.2",
69
- "@getbrevo/brevo": "1.0.1",
70
- "@lenne.tech/mongoose-gridfs": "1.4.2",
71
- "@lenne.tech/multer-gridfs-storage": "5.0.6",
70
+ "@apollo/gateway": "2.11.3",
71
+ "@getbrevo/brevo": "3.0.1",
72
72
  "@nestjs/apollo": "13.1.0",
73
- "@nestjs/common": "11.1.0",
74
- "@nestjs/core": "11.1.0",
73
+ "@nestjs/common": "11.1.6",
74
+ "@nestjs/core": "11.1.6",
75
75
  "@nestjs/graphql": "13.1.0",
76
- "@nestjs/jwt": "11.0.0",
76
+ "@nestjs/jwt": "11.0.1",
77
77
  "@nestjs/mongoose": "11.0.3",
78
78
  "@nestjs/passport": "11.0.5",
79
- "@nestjs/platform-express": "11.1.0",
80
- "@nestjs/schedule": "6.0.0",
79
+ "@nestjs/platform-express": "11.1.6",
80
+ "@nestjs/schedule": "6.0.1",
81
81
  "@nestjs/swagger": "11.2.0",
82
82
  "@nestjs/terminus": "11.0.0",
83
83
  "apollo-server-core": "3.13.0",
84
84
  "apollo-server-express": "3.13.0",
85
- "bcrypt": "5.1.1",
85
+ "bcrypt": "6.0.0",
86
86
  "class-transformer": "0.5.1",
87
87
  "class-validator": "0.14.2",
88
88
  "compression": "1.8.1",
89
89
  "cookie-parser": "1.4.7",
90
- "dotenv": "16.5.0",
90
+ "dotenv": "17.2.3",
91
91
  "ejs": "3.1.10",
92
92
  "graphql": "16.11.0",
93
93
  "graphql-query-complexity": "1.1.0",
94
94
  "graphql-subscriptions": "3.0.0",
95
95
  "graphql-upload": "15.0.2",
96
- "js-sha256": "0.11.0",
96
+ "js-sha256": "0.11.1",
97
97
  "json-to-graphql-query": "2.3.0",
98
98
  "light-my-request": "6.6.0",
99
99
  "lodash": "4.17.21",
100
- "mongodb": "6.16.0",
101
- "mongoose": "7.8.7",
102
- "multer": "1.4.5-lts.2",
103
- "node-mailjet": "6.0.8",
104
- "nodemailer": "7.0.3",
100
+ "mongodb": "6.20.0",
101
+ "mongoose": "8.19.1",
102
+ "multer": "2.0.2",
103
+ "node-mailjet": "6.0.9",
104
+ "nodemailer": "7.0.9",
105
105
  "nodemon": "3.1.10",
106
106
  "passport": "0.7.0",
107
107
  "passport-jwt": "4.0.1",
@@ -113,31 +113,31 @@
113
113
  },
114
114
  "devDependencies": {
115
115
  "@babel/plugin-proposal-private-methods": "7.18.6",
116
- "@compodoc/compodoc": "1.1.26",
117
- "@lenne.tech/eslint-config-ts": "2.0.1",
118
- "@nestjs/cli": "11.0.7",
119
- "@nestjs/schematics": "11.0.5",
120
- "@nestjs/testing": "11.1.0",
121
- "@swc/cli": "0.7.5",
122
- "@swc/core": "1.11.24",
123
- "@swc/jest": "0.2.38",
124
- "@types/compression": "1.7.5",
125
- "@types/cookie-parser": "1.4.8",
116
+ "@compodoc/compodoc": "1.1.31",
117
+ "@lenne.tech/eslint-config-ts": "2.1.3",
118
+ "@nestjs/cli": "11.0.10",
119
+ "@nestjs/schematics": "11.0.9",
120
+ "@nestjs/testing": "11.1.6",
121
+ "@swc/cli": "0.7.8",
122
+ "@swc/core": "1.13.5",
123
+ "@swc/jest": "0.2.39",
124
+ "@types/compression": "1.8.1",
125
+ "@types/cookie-parser": "1.4.9",
126
126
  "@types/ejs": "3.1.5",
127
127
  "@types/express": "4.17.21",
128
- "@types/jest": "29.5.14",
129
- "@types/lodash": "4.17.16",
130
- "@types/multer": "1.4.12",
131
- "@types/node": "22.15.17",
132
- "@types/nodemailer": "6.4.17",
128
+ "@types/jest": "30.0.0",
129
+ "@types/lodash": "4.17.20",
130
+ "@types/multer": "2.0.0",
131
+ "@types/node": "24.7.1",
132
+ "@types/nodemailer": "7.0.2",
133
133
  "@types/passport": "1.0.17",
134
134
  "@types/supertest": "6.0.3",
135
- "@typescript-eslint/eslint-plugin": "8.32.0",
136
- "@typescript-eslint/parser": "8.32.0",
135
+ "@typescript-eslint/eslint-plugin": "8.46.0",
136
+ "@typescript-eslint/parser": "8.46.0",
137
137
  "coffeescript": "2.7.0",
138
- "eslint": "9.32.0",
139
- "eslint-config-prettier": "10.1.5",
140
- "eslint-plugin-unused-imports": "4.1.4",
138
+ "eslint": "9.37.0",
139
+ "eslint-config-prettier": "10.1.8",
140
+ "eslint-plugin-unused-imports": "4.2.0",
141
141
  "find-file-up": "2.0.1",
142
142
  "grunt": "1.6.1",
143
143
  "grunt-bg-shell": "2.3.3",
@@ -145,28 +145,26 @@
145
145
  "grunt-contrib-watch": "1.1.0",
146
146
  "grunt-sync": "0.8.2",
147
147
  "husky": "9.1.7",
148
- "jest": "29.7.0",
148
+ "jest": "30.2.0",
149
149
  "npm-watch": "0.13.0",
150
- "pm2": "6.0.10",
151
- "prettier": "3.5.3",
152
- "pretty-quick": "4.1.1",
153
- "supertest": "7.1.0",
154
- "ts-jest": "29.3.2",
155
- "ts-loader": "9.5.2",
156
- "ts-morph": "25.0.1",
150
+ "pm2": "6.0.13",
151
+ "prettier": "3.6.2",
152
+ "pretty-quick": "4.2.2",
153
+ "supertest": "7.1.4",
154
+ "ts-jest": "29.4.5",
155
+ "ts-loader": "9.5.4",
156
+ "ts-morph": "27.0.0",
157
157
  "ts-node": "10.9.2",
158
158
  "tsconfig-paths": "4.2.0",
159
- "typescript": "5.8.3",
159
+ "typescript": "5.9.3",
160
160
  "yalc": "1.0.0-pre.53"
161
161
  },
162
162
  "overrides": {
163
- "multer-gridfs-storage": {
164
- "multer": "$multer"
165
- },
166
163
  "@lykmapipo/common": {
167
164
  "flat": "5.0.2",
168
165
  "mime": "2.6.0"
169
- }
166
+ },
167
+ "ts-morph": "27.0.0"
170
168
  },
171
169
  "jest": {
172
170
  "collectCoverage": true,
@@ -0,0 +1,227 @@
1
+ import { mongo, Types } from 'mongoose';
2
+ import { Readable } from 'stream';
3
+
4
+ // Use Mongoose's MongoDB types to avoid BSON version conflicts
5
+ const ObjectId = Types.ObjectId;
6
+
7
+ /**
8
+ * GridFS File Info interface matching the structure from GridFS
9
+ * This is the normalized structure with contentType at root level
10
+ */
11
+ export interface GridFSFileInfo {
12
+ _id: Types.ObjectId;
13
+ chunkSize: number;
14
+ /**
15
+ * Content type of the file
16
+ * Note: Stored in metadata.contentType in MongoDB, normalized to root level by helper
17
+ */
18
+ contentType?: string;
19
+ filename: string;
20
+ length: number;
21
+ metadata?: Record<string, any> & { contentType?: string };
22
+ uploadDate: Date;
23
+ }
24
+
25
+ /**
26
+ * Options for reading files from GridFS
27
+ */
28
+ export interface GridFSReadOptions {
29
+ _id?: string | Types.ObjectId;
30
+ filename?: string;
31
+ }
32
+ /**
33
+ * Options for writing files to GridFS
34
+ */
35
+ export interface GridFSWriteOptions {
36
+ contentType?: string;
37
+ filename: string;
38
+ metadata?: Record<string, any>;
39
+ }
40
+
41
+ type GridFSBucket = mongo.GridFSBucket;
42
+
43
+ type GridFSBucketReadStream = mongo.GridFSBucketReadStream;
44
+
45
+ /**
46
+ * Raw GridFS file structure as returned by MongoDB
47
+ * This is the internal structure before normalization
48
+ */
49
+ interface RawGridFSFile {
50
+ _id: Types.ObjectId;
51
+ chunkSize: number;
52
+ filename: string;
53
+ length: number;
54
+ metadata?: Record<string, any> & { contentType?: string };
55
+ uploadDate: Date;
56
+ }
57
+
58
+ /**
59
+ * Helper class for GridFS operations using native MongoDB driver
60
+ * Provides Promise-based API for all GridFS operations
61
+ */
62
+ export class GridFSHelper {
63
+ /**
64
+ * Normalize file info to ensure contentType is accessible at root level
65
+ * MongoDB stores contentType in metadata, but our API expects it at root
66
+ */
67
+ private static normalizeFileInfo(fileInfo: RawGridFSFile): GridFSFileInfo {
68
+ return {
69
+ ...fileInfo,
70
+ contentType: fileInfo.metadata?.contentType,
71
+ };
72
+ }
73
+
74
+ /**
75
+ * Write a file to GridFS from a stream
76
+ */
77
+ static writeFileFromStream(
78
+ bucket: GridFSBucket,
79
+ stream: Readable,
80
+ options: GridFSWriteOptions,
81
+ ): Promise<GridFSFileInfo> {
82
+ return new Promise((resolve, reject) => {
83
+ // Store contentType in metadata to avoid deprecation warning
84
+ const metadata = { ...options.metadata };
85
+ if (options.contentType) {
86
+ metadata.contentType = options.contentType;
87
+ }
88
+
89
+ const uploadStream = bucket.openUploadStream(options.filename, {
90
+ metadata,
91
+ });
92
+
93
+ uploadStream.on('error', (error) => {
94
+ reject(error);
95
+ });
96
+
97
+ uploadStream.on('finish', () => {
98
+ // Fetch the file info after upload completes
99
+ bucket
100
+ .find({ _id: uploadStream.id })
101
+ .toArray()
102
+ .then((files) => {
103
+ if (files && files.length > 0) {
104
+ resolve(GridFSHelper.normalizeFileInfo(files[0]));
105
+ } else {
106
+ reject(new Error('File uploaded but metadata not found'));
107
+ }
108
+ })
109
+ .catch(reject);
110
+ });
111
+
112
+ stream.pipe(uploadStream);
113
+ });
114
+ }
115
+
116
+ /**
117
+ * Read a file from GridFS to a buffer
118
+ */
119
+ static readFileToBuffer(bucket: GridFSBucket, options: GridFSReadOptions): Promise<Buffer> {
120
+ return new Promise((resolve, reject) => {
121
+ const chunks: Buffer[] = [];
122
+ let downloadStream: GridFSBucketReadStream;
123
+
124
+ if (options._id) {
125
+ const objectId = typeof options._id === 'string' ? new ObjectId(options._id) : options._id;
126
+ downloadStream = bucket.openDownloadStream(objectId);
127
+ } else if (options.filename) {
128
+ downloadStream = bucket.openDownloadStreamByName(options.filename);
129
+ } else {
130
+ return reject(new Error('Either _id or filename must be provided'));
131
+ }
132
+
133
+ downloadStream.on('data', (chunk) => {
134
+ chunks.push(chunk);
135
+ });
136
+
137
+ downloadStream.on('error', (error) => {
138
+ reject(error);
139
+ });
140
+
141
+ downloadStream.on('end', () => {
142
+ resolve(Buffer.concat(chunks));
143
+ });
144
+ });
145
+ }
146
+
147
+ /**
148
+ * Find file metadata by ID
149
+ */
150
+ static async findFileById(bucket: GridFSBucket, id: string | Types.ObjectId): Promise<GridFSFileInfo | null> {
151
+ const objectId = typeof id === 'string' ? new ObjectId(id) : id;
152
+ const files = await bucket.find({ _id: objectId }).toArray();
153
+ return files.length > 0 ? GridFSHelper.normalizeFileInfo(files[0]) : null;
154
+ }
155
+
156
+ /**
157
+ * Find file metadata by filename
158
+ */
159
+ static async findFileByName(bucket: GridFSBucket, filename: string): Promise<GridFSFileInfo | null> {
160
+ const files = await bucket.find({ filename }).toArray();
161
+ return files.length > 0 ? GridFSHelper.normalizeFileInfo(files[0]) : null;
162
+ }
163
+
164
+ /**
165
+ * Find files with filter and options
166
+ */
167
+ static async findFiles(bucket: GridFSBucket, filter: any = {}, options: any = {}): Promise<GridFSFileInfo[]> {
168
+ const files = await bucket.find(filter, options).toArray();
169
+ return files.map(file => GridFSHelper.normalizeFileInfo(file));
170
+ }
171
+
172
+ /**
173
+ * Delete a file from GridFS
174
+ */
175
+ static async deleteFile(bucket: GridFSBucket, id: string | Types.ObjectId): Promise<void> {
176
+ const objectId = typeof id === 'string' ? new ObjectId(id) : id;
177
+ await bucket.delete(objectId);
178
+ }
179
+
180
+ /**
181
+ * Open download stream by ID
182
+ */
183
+ static openDownloadStream(bucket: GridFSBucket, id: string | Types.ObjectId): GridFSBucketReadStream {
184
+ const objectId = typeof id === 'string' ? new ObjectId(id) : id;
185
+ return bucket.openDownloadStream(objectId);
186
+ }
187
+
188
+ /**
189
+ * Open download stream by name
190
+ */
191
+ static openDownloadStreamByName(bucket: GridFSBucket, filename: string): GridFSBucketReadStream {
192
+ return bucket.openDownloadStreamByName(filename);
193
+ }
194
+
195
+ /**
196
+ * Open upload stream
197
+ */
198
+ static openUploadStream(
199
+ bucket: GridFSBucket,
200
+ filename: string,
201
+ options?: { contentType?: string },
202
+ ): mongo.GridFSBucketWriteStream {
203
+ // Store contentType in metadata to avoid deprecation warning
204
+ if (options?.contentType) {
205
+ const metadata = { contentType: options.contentType };
206
+ return bucket.openUploadStream(filename, { metadata });
207
+ }
208
+ return bucket.openUploadStream(filename, options);
209
+ }
210
+
211
+ /**
212
+ * Open upload stream with specific ID
213
+ */
214
+ static openUploadStreamWithId(
215
+ bucket: GridFSBucket,
216
+ id: Types.ObjectId,
217
+ filename: string,
218
+ options?: { contentType?: string },
219
+ ): mongo.GridFSBucketWriteStream {
220
+ // Store contentType in metadata to avoid deprecation warning
221
+ if (options?.contentType) {
222
+ const metadata = { contentType: options.contentType };
223
+ return bucket.openUploadStreamWithId(id, filename, { metadata });
224
+ }
225
+ return bucket.openUploadStreamWithId(id, filename, options);
226
+ }
227
+ }
@@ -1,4 +1,4 @@
1
- import brevo = require('@getbrevo/brevo');
1
+ import { SendSmtpEmail, TransactionalEmailsApi, TransactionalEmailsApiApiKeys } from '@getbrevo/brevo';
2
2
  import { Injectable } from '@nestjs/common';
3
3
 
4
4
  import { ConfigService } from './config.service';
@@ -9,15 +9,15 @@ import { ConfigService } from './config.service';
9
9
  @Injectable()
10
10
  export class BrevoService {
11
11
  brevoConfig: ConfigService['configFastButReadOnly']['brevo'];
12
+ private apiInstance: TransactionalEmailsApi;
12
13
 
13
14
  constructor(protected configService: ConfigService) {
14
- const defaultClient = brevo.ApiClient.instance;
15
- const apiKey = defaultClient.authentications['api-key'];
16
15
  this.brevoConfig = configService.configFastButReadOnly.brevo;
17
16
  if (!this.brevoConfig) {
18
17
  throw new Error('Brevo configuration not set!');
19
18
  }
20
- apiKey.apiKey = this.brevoConfig.apiKey;
19
+ this.apiInstance = new TransactionalEmailsApi();
20
+ this.apiInstance.setApiKey(TransactionalEmailsApiApiKeys.apiKey, this.brevoConfig.apiKey);
21
21
  }
22
22
 
23
23
  /**
@@ -37,14 +37,15 @@ export class BrevoService {
37
37
  }
38
38
 
39
39
  // Prepare data
40
- const apiInstance = new brevo.TransactionalEmailsApi();
41
- const sendSmtpEmail = new brevo.SendSmtpEmail();
42
- sendSmtpEmail.templateId = templateId;
43
- sendSmtpEmail.to = [{ email: to }];
44
- sendSmtpEmail.params = params;
40
+ const sendSmtpEmail: SendSmtpEmail = {
41
+ params,
42
+ templateId,
43
+ to: [{ email: to }],
44
+ };
45
45
 
46
46
  // Send email
47
- return await apiInstance.sendTransacEmail(sendSmtpEmail);
47
+ const result = await this.apiInstance.sendTransacEmail(sendSmtpEmail);
48
+ return result.body;
48
49
  } catch (error) {
49
50
  console.error(error);
50
51
  }
@@ -75,16 +76,17 @@ export class BrevoService {
75
76
  }
76
77
 
77
78
  // Prepare data
78
- const apiInstance = new brevo.TransactionalEmailsApi();
79
- const sendSmtpEmail = new brevo.SendSmtpEmail();
80
- sendSmtpEmail.htmlContent = html;
81
- sendSmtpEmail.params = options?.params;
82
- sendSmtpEmail.sender = this.brevoConfig.sender;
83
- sendSmtpEmail.subject = subject;
84
- sendSmtpEmail.to = [{ email: to }];
79
+ const sendSmtpEmail: SendSmtpEmail = {
80
+ htmlContent: html,
81
+ params: options?.params,
82
+ sender: this.brevoConfig.sender,
83
+ subject,
84
+ to: [{ email: to }],
85
+ };
85
86
 
86
87
  // Send email
87
- return await apiInstance.sendTransacEmail(sendSmtpEmail);
88
+ const result = await this.apiInstance.sendTransacEmail(sendSmtpEmail);
89
+ return result.body;
88
90
  } catch (error) {
89
91
  console.error(error);
90
92
  }
@@ -105,7 +105,7 @@ export abstract class CrudService<
105
105
  * Get distinct values of a property
106
106
  */
107
107
  distinct(property: string): Promise<string[]> {
108
- return this.mainDbModel.distinct(property);
108
+ return this.mainDbModel.distinct(property).exec() as Promise<string[]>;
109
109
  }
110
110
 
111
111
  /**
@@ -178,7 +178,7 @@ export abstract class CrudService<
178
178
  find = find.collation(collation);
179
179
  }
180
180
  if (serviceOptions?.select) {
181
- find = find.select(serviceOptions.select);
181
+ find = find.select(serviceOptions.select) as any;
182
182
  }
183
183
  return find.exec();
184
184
  },
@@ -401,7 +401,7 @@ export abstract class CrudService<
401
401
  find = find.collation(collation);
402
402
  }
403
403
  if (serviceOptions?.select) {
404
- find = find.select(serviceOptions.select);
404
+ find = find.select(serviceOptions.select) as any;
405
405
  }
406
406
  return find.exec();
407
407
  },
@@ -1,4 +1,5 @@
1
1
  import { BadRequestException, Controller, Get, NotFoundException, Param, Res } from '@nestjs/common';
2
+ import { Response } from 'express';
2
3
 
3
4
  import { Roles } from '../../common/decorators/roles.decorator';
4
5
  import { RoleEnum } from '../../common/enums/role.enum';
@@ -20,7 +21,7 @@ export abstract class CoreFileController {
20
21
  */
21
22
  @Get(':filename')
22
23
  @Roles(RoleEnum.S_EVERYONE)
23
- async getFile(@Param('filename') filename: string, @Res() res) {
24
+ async getFile(@Param('filename') filename: string, @Res() res: Response) {
24
25
  if (!filename) {
25
26
  throw new BadRequestException('Missing filename for download');
26
27
  }
@@ -30,7 +31,7 @@ export abstract class CoreFileController {
30
31
  throw new NotFoundException('File not found');
31
32
  }
32
33
  const filestream = await this.fileService.getFileStream(file.id);
33
- res.header('Content-Type', file.contentType);
34
+ res.header('Content-Type', file.contentType || 'application/octet-stream');
34
35
  res.header('Content-Disposition', `attachment; filename=${file.filename}`);
35
36
  return filestream.pipe(res);
36
37
  }