@lenne.tech/nest-server 11.1.14 → 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.
- package/dist/core/common/helpers/gridfs.helper.d.ts +42 -0
- package/dist/core/common/helpers/gridfs.helper.js +107 -0
- package/dist/core/common/helpers/gridfs.helper.js.map +1 -0
- package/dist/core/common/services/brevo.service.d.ts +1 -0
- package/dist/core/common/services/brevo.service.js +19 -18
- package/dist/core/common/services/brevo.service.js.map +1 -1
- package/dist/core/common/services/crud.service.js +1 -1
- package/dist/core/common/services/crud.service.js.map +1 -1
- package/dist/core/modules/file/core-file.controller.d.ts +2 -1
- package/dist/core/modules/file/core-file.controller.js +1 -1
- package/dist/core/modules/file/core-file.controller.js.map +1 -1
- package/dist/core/modules/file/core-file.service.d.ts +7 -7
- package/dist/core/modules/file/core-file.service.js +28 -51
- package/dist/core/modules/file/core-file.service.js.map +1 -1
- package/dist/core/modules/file/interfaces/file-upload.interface.d.ts +1 -1
- package/dist/core/modules/user/core-user.service.js +2 -3
- package/dist/core/modules/user/core-user.service.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/server/modules/file/file-info.model.d.ts +7 -3
- package/dist/server/modules/file/file.controller.d.ts +6 -4
- package/dist/server/modules/file/file.controller.js +15 -4
- package/dist/server/modules/file/file.controller.js.map +1 -1
- package/dist/server/modules/file/file.resolver.js +1 -1
- package/dist/server/modules/file/file.resolver.js.map +1 -1
- package/dist/server/modules/file/multer-config.service.d.ts +0 -2
- package/dist/server/modules/file/multer-config.service.js +3 -22
- package/dist/server/modules/file/multer-config.service.js.map +1 -1
- package/dist/server/modules/user/user.model.d.ts +7 -3
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types/graphql-upload.d.ts +25 -0
- package/package.json +21 -23
- package/src/core/common/helpers/gridfs.helper.ts +227 -0
- package/src/core/common/services/brevo.service.ts +20 -18
- package/src/core/common/services/crud.service.ts +3 -3
- package/src/core/modules/file/core-file.controller.ts +3 -2
- package/src/core/modules/file/core-file.service.ts +49 -60
- package/src/core/modules/file/interfaces/file-upload.interface.ts +2 -1
- package/src/core/modules/user/core-user.service.ts +3 -3
- package/src/index.ts +1 -0
- package/src/server/modules/file/file.controller.ts +25 -7
- package/src/server/modules/file/file.resolver.ts +1 -2
- package/src/server/modules/file/multer-config.service.ts +6 -21
- package/src/types/graphql-upload.d.ts +25 -0
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { createBucket, MongoGridFSOptions, MongooseGridFS } from '@lenne.tech/mongoose-gridfs';
|
|
2
1
|
import { NotFoundException } from '@nestjs/common';
|
|
3
|
-
import {
|
|
4
|
-
import mongoose, { Connection, Types } from 'mongoose';
|
|
2
|
+
import mongoose, { Connection, mongo, Types } from 'mongoose';
|
|
5
3
|
|
|
6
4
|
import { FilterArgs } from '../../common/args/filter.args';
|
|
7
5
|
import { getObjectIds, getStringIds } from '../../common/helpers/db.helper';
|
|
8
6
|
import { convertFilterArgsToQuery } from '../../common/helpers/filter.helper';
|
|
7
|
+
import { GridFSHelper } from '../../common/helpers/gridfs.helper';
|
|
9
8
|
import { check } from '../../common/helpers/input.helper';
|
|
10
9
|
import { prepareOutput } from '../../common/helpers/service.helper';
|
|
11
10
|
import { MaybePromise } from '../../common/types/maybe-promise.type';
|
|
@@ -22,7 +21,8 @@ export type FileInputCheckType = 'file' | 'filename' | 'files' | 'filterArgs' |
|
|
|
22
21
|
* Abstract core file service
|
|
23
22
|
*/
|
|
24
23
|
export abstract class CoreFileService {
|
|
25
|
-
|
|
24
|
+
// Use the native MongoDB driver's types (accessed via Mongoose's exports) to avoid BSON version conflicts
|
|
25
|
+
files: mongo.GridFSBucket;
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* Include MongoDB connection and create File bucket
|
|
@@ -31,7 +31,8 @@ export abstract class CoreFileService {
|
|
|
31
31
|
protected readonly connection: Connection,
|
|
32
32
|
bucketName = 'fs',
|
|
33
33
|
) {
|
|
34
|
-
|
|
34
|
+
// Use the native MongoDB driver's GridFSBucket via Mongoose's mongo export to avoid BSON version conflicts
|
|
35
|
+
this.files = new mongo.GridFSBucket(connection.db, { bucketName });
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
/**
|
|
@@ -41,15 +42,13 @@ export abstract class CoreFileService {
|
|
|
41
42
|
if (!(await this.checkRights(file, { ...serviceOptions, checkInputType: 'file' }))) {
|
|
42
43
|
return null;
|
|
43
44
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
this.files.writeFile(options, readStream, (error, fileInfo) => {
|
|
50
|
-
error ? reject(error) : resolve(this.prepareOutput(fileInfo, serviceOptions));
|
|
51
|
-
});
|
|
45
|
+
const { createReadStream, filename, mimetype } = await file;
|
|
46
|
+
const readStream = createReadStream();
|
|
47
|
+
const fileInfo = await GridFSHelper.writeFileFromStream(this.files, readStream, {
|
|
48
|
+
contentType: mimetype,
|
|
49
|
+
filename,
|
|
52
50
|
});
|
|
51
|
+
return this.prepareOutput(fileInfo as unknown as CoreFileInfo, serviceOptions);
|
|
53
52
|
}
|
|
54
53
|
|
|
55
54
|
/**
|
|
@@ -71,7 +70,11 @@ export abstract class CoreFileService {
|
|
|
71
70
|
*/
|
|
72
71
|
async duplicateByName(name: string, newName: string): Promise<any> {
|
|
73
72
|
return new Promise(async (resolve) => {
|
|
74
|
-
resolve(
|
|
73
|
+
resolve(
|
|
74
|
+
GridFSHelper.openDownloadStreamByName(this.files, name).pipe(
|
|
75
|
+
GridFSHelper.openUploadStream(this.files, newName),
|
|
76
|
+
),
|
|
77
|
+
);
|
|
75
78
|
});
|
|
76
79
|
}
|
|
77
80
|
|
|
@@ -82,10 +85,10 @@ export abstract class CoreFileService {
|
|
|
82
85
|
const objectId = getObjectIds(id);
|
|
83
86
|
const file = await this.getFileInfo(objectId);
|
|
84
87
|
return new Promise((resolve, reject) => {
|
|
85
|
-
const downloadStream = this.files
|
|
88
|
+
const downloadStream = GridFSHelper.openDownloadStream(this.files, objectId);
|
|
86
89
|
|
|
87
90
|
const newFileId = new mongoose.Types.ObjectId();
|
|
88
|
-
const uploadStream = this.files
|
|
91
|
+
const uploadStream = GridFSHelper.openUploadStreamWithId(this.files, newFileId, file.filename, {
|
|
89
92
|
contentType: file.contentType,
|
|
90
93
|
});
|
|
91
94
|
|
|
@@ -112,16 +115,9 @@ export abstract class CoreFileService {
|
|
|
112
115
|
if (!(await this.checkRights(filterArgs, { ...serviceOptions, checkInputType: 'filterArgs' }))) {
|
|
113
116
|
return null;
|
|
114
117
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (!cursor) {
|
|
119
|
-
throw new NotFoundException('File collection not found');
|
|
120
|
-
}
|
|
121
|
-
cursor.toArray((error, docs) => {
|
|
122
|
-
error ? reject(error) : resolve(this.prepareOutput(docs, serviceOptions));
|
|
123
|
-
});
|
|
124
|
-
});
|
|
118
|
+
const filterQuery = convertFilterArgsToQuery(filterArgs);
|
|
119
|
+
const docs = await GridFSHelper.findFiles(this.files, filterQuery[0], filterQuery[1]);
|
|
120
|
+
return this.prepareOutput(docs as unknown as CoreFileInfo[], serviceOptions);
|
|
125
121
|
}
|
|
126
122
|
|
|
127
123
|
/**
|
|
@@ -131,11 +127,8 @@ export abstract class CoreFileService {
|
|
|
131
127
|
if (!(await this.checkRights(id, { ...serviceOptions, checkInputType: 'id' }))) {
|
|
132
128
|
return null;
|
|
133
129
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
error ? reject(error) : resolve(this.prepareOutput(fileInfo, serviceOptions));
|
|
137
|
-
});
|
|
138
|
-
});
|
|
130
|
+
const fileInfo = await GridFSHelper.findFileById(this.files, getObjectIds(id));
|
|
131
|
+
return this.prepareOutput(fileInfo as unknown as CoreFileInfo, serviceOptions);
|
|
139
132
|
}
|
|
140
133
|
|
|
141
134
|
/**
|
|
@@ -145,11 +138,8 @@ export abstract class CoreFileService {
|
|
|
145
138
|
if (!(await this.checkRights(filename, { ...serviceOptions, checkInputType: 'filename' }))) {
|
|
146
139
|
return null;
|
|
147
140
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
error ? reject(error) : resolve(this.prepareOutput(fileInfo, serviceOptions));
|
|
151
|
-
});
|
|
152
|
-
});
|
|
141
|
+
const fileInfo = await GridFSHelper.findFileByName(this.files, filename);
|
|
142
|
+
return this.prepareOutput(fileInfo as unknown as CoreFileInfo, serviceOptions);
|
|
153
143
|
}
|
|
154
144
|
|
|
155
145
|
/**
|
|
@@ -159,7 +149,7 @@ export abstract class CoreFileService {
|
|
|
159
149
|
if (!(await this.checkRights(id, { ...serviceOptions, checkInputType: 'id' }))) {
|
|
160
150
|
return null;
|
|
161
151
|
}
|
|
162
|
-
return this.files
|
|
152
|
+
return GridFSHelper.openDownloadStream(this.files, getObjectIds(id)) as mongo.GridFSBucketReadStream;
|
|
163
153
|
}
|
|
164
154
|
|
|
165
155
|
/**
|
|
@@ -168,11 +158,11 @@ export abstract class CoreFileService {
|
|
|
168
158
|
async getFileStreamByName(
|
|
169
159
|
filename: string,
|
|
170
160
|
serviceOptions?: FileServiceOptions,
|
|
171
|
-
): Promise<
|
|
161
|
+
): Promise<mongo.GridFSBucketReadStream> {
|
|
172
162
|
if (!(await this.checkRights(filename, { ...serviceOptions, checkInputType: 'filename' }))) {
|
|
173
163
|
return null;
|
|
174
164
|
}
|
|
175
|
-
return this.files
|
|
165
|
+
return GridFSHelper.openDownloadStreamByName(this.files, filename);
|
|
176
166
|
}
|
|
177
167
|
|
|
178
168
|
/**
|
|
@@ -182,39 +172,30 @@ export abstract class CoreFileService {
|
|
|
182
172
|
if (!(await this.checkRights(id, { ...serviceOptions, checkInputType: 'id' }))) {
|
|
183
173
|
return null;
|
|
184
174
|
}
|
|
185
|
-
return await
|
|
186
|
-
this.files.readFile({ _id: getObjectIds(id) }, (error, buffer) => {
|
|
187
|
-
error ? reject(error) : resolve(buffer);
|
|
188
|
-
});
|
|
189
|
-
});
|
|
175
|
+
return await GridFSHelper.readFileToBuffer(this.files, { _id: getObjectIds(id) });
|
|
190
176
|
}
|
|
191
177
|
|
|
192
178
|
/**
|
|
193
|
-
* Get file buffer (for small files) via
|
|
179
|
+
* Get file buffer (for small files) via filename
|
|
194
180
|
*/
|
|
195
181
|
async getBufferByName(filename: string, serviceOptions?: FileServiceOptions): Promise<Buffer> {
|
|
196
182
|
if (!(await this.checkRights(filename, { ...serviceOptions, checkInputType: 'filename' }))) {
|
|
197
183
|
return null;
|
|
198
184
|
}
|
|
199
|
-
return await
|
|
200
|
-
this.files.readFile({ filename }, (error, buffer) => {
|
|
201
|
-
error ? reject(error) : resolve(buffer);
|
|
202
|
-
});
|
|
203
|
-
});
|
|
185
|
+
return await GridFSHelper.readFileToBuffer(this.files, { filename });
|
|
204
186
|
}
|
|
205
187
|
|
|
206
188
|
/**
|
|
207
|
-
* Delete file
|
|
189
|
+
* Delete file
|
|
208
190
|
*/
|
|
209
191
|
async deleteFile(id: string | Types.ObjectId, serviceOptions?: FileServiceOptions): Promise<CoreFileInfo> {
|
|
210
192
|
if (!(await this.checkRights(id, { ...serviceOptions, checkInputType: 'id' }))) {
|
|
211
193
|
return null;
|
|
212
194
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
});
|
|
195
|
+
const objectId = getObjectIds(id);
|
|
196
|
+
const fileInfo = await this.getFileInfo(objectId, serviceOptions);
|
|
197
|
+
await GridFSHelper.deleteFile(this.files, objectId);
|
|
198
|
+
return fileInfo;
|
|
218
199
|
}
|
|
219
200
|
|
|
220
201
|
/**
|
|
@@ -247,15 +228,23 @@ export abstract class CoreFileService {
|
|
|
247
228
|
}
|
|
248
229
|
|
|
249
230
|
/**
|
|
250
|
-
* Prepare output before return
|
|
231
|
+
* Prepare output before return - single file
|
|
232
|
+
* Accepts both GridFSFileInfo (from GridFS operations) and CoreFileInfo
|
|
233
|
+
* They are structurally compatible (duck typing), so we use type assertion
|
|
251
234
|
*/
|
|
252
|
-
protected async prepareOutput(fileInfo: CoreFileInfo
|
|
235
|
+
protected async prepareOutput(fileInfo: CoreFileInfo, options?: FileServiceOptions): Promise<CoreFileInfo>;
|
|
236
|
+
protected async prepareOutput(fileInfo: null, options?: FileServiceOptions): Promise<null>;
|
|
237
|
+
protected async prepareOutput(fileInfo: CoreFileInfo[], options?: FileServiceOptions): Promise<CoreFileInfo[]>;
|
|
238
|
+
protected async prepareOutput(
|
|
239
|
+
fileInfo: CoreFileInfo | CoreFileInfo[] | null,
|
|
240
|
+
options?: FileServiceOptions,
|
|
241
|
+
): Promise<CoreFileInfo | CoreFileInfo[] | null> {
|
|
253
242
|
if (!fileInfo) {
|
|
254
243
|
return fileInfo;
|
|
255
244
|
}
|
|
256
245
|
this.setId(fileInfo);
|
|
257
|
-
|
|
258
|
-
return check(
|
|
246
|
+
const prepared = await prepareOutput(fileInfo, { targetModel: CoreFileInfo });
|
|
247
|
+
return check(prepared, options?.currentUser, { roles: options?.roles });
|
|
259
248
|
}
|
|
260
249
|
|
|
261
250
|
/**
|
|
@@ -115,9 +115,9 @@ export abstract class CoreUserService<
|
|
|
115
115
|
return this.process(
|
|
116
116
|
async () => {
|
|
117
117
|
// Update and return user
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
118
|
+
await this.mainDbModel.updateOne({ _id: dbObject.id }, { verified: true, verifiedAt: new Date() }).exec();
|
|
119
|
+
// Return the updated user
|
|
120
|
+
return await this.mainDbModel.findById(dbObject.id).exec();
|
|
121
121
|
},
|
|
122
122
|
{ dbObject, serviceOptions },
|
|
123
123
|
);
|
package/src/index.ts
CHANGED
|
@@ -34,6 +34,7 @@ export * from './core/common/helpers/decorator.helper';
|
|
|
34
34
|
export * from './core/common/helpers/file.helper';
|
|
35
35
|
export * from './core/common/helpers/filter.helper';
|
|
36
36
|
export * from './core/common/helpers/graphql.helper';
|
|
37
|
+
export * from './core/common/helpers/gridfs.helper';
|
|
37
38
|
export * from './core/common/helpers/input.helper';
|
|
38
39
|
export * from './core/common/helpers/model.helper';
|
|
39
40
|
export * from './core/common/helpers/scim.helper';
|
|
@@ -11,13 +11,17 @@ import {
|
|
|
11
11
|
UseInterceptors,
|
|
12
12
|
} from '@nestjs/common';
|
|
13
13
|
import { FileInterceptor } from '@nestjs/platform-express';
|
|
14
|
+
import { Response } from 'express';
|
|
15
|
+
import { Readable } from 'stream';
|
|
14
16
|
|
|
15
17
|
import { Roles } from '../../../core/common/decorators/roles.decorator';
|
|
16
18
|
import { RoleEnum } from '../../../core/common/enums/role.enum';
|
|
19
|
+
import { CoreFileInfo } from '../../../core/modules/file/core-file-info.model';
|
|
20
|
+
import { FileUpload } from '../../../core/modules/file/interfaces/file-upload.interface';
|
|
17
21
|
import { FileService } from './file.service';
|
|
18
22
|
|
|
19
23
|
/**
|
|
20
|
-
* File controller
|
|
24
|
+
* File controller
|
|
21
25
|
*/
|
|
22
26
|
@Controller('files')
|
|
23
27
|
@Roles(RoleEnum.ADMIN)
|
|
@@ -28,13 +32,26 @@ export class FileController {
|
|
|
28
32
|
constructor(private readonly fileService: FileService) {}
|
|
29
33
|
|
|
30
34
|
/**
|
|
31
|
-
* Upload file
|
|
35
|
+
* Upload file via HTTP
|
|
32
36
|
*/
|
|
33
37
|
@Post('upload')
|
|
34
38
|
@Roles(RoleEnum.ADMIN)
|
|
35
39
|
@UseInterceptors(FileInterceptor('file'))
|
|
36
|
-
uploadFile(@UploadedFile() file: Express.Multer.File): any {
|
|
37
|
-
|
|
40
|
+
async uploadFile(@UploadedFile() file: Express.Multer.File): Promise<any> {
|
|
41
|
+
if (!file) {
|
|
42
|
+
throw new BadRequestException('No file provided');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Convert Multer file to FileUpload interface
|
|
46
|
+
const fileUpload: FileUpload = {
|
|
47
|
+
capacitor: null, // Not used when creating from buffer
|
|
48
|
+
createReadStream: () => Readable.from(file.buffer),
|
|
49
|
+
filename: file.originalname,
|
|
50
|
+
mimetype: file.mimetype,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// Save to GridFS using FileService
|
|
54
|
+
return await this.fileService.createFile(fileUpload);
|
|
38
55
|
}
|
|
39
56
|
|
|
40
57
|
/**
|
|
@@ -42,23 +59,24 @@ export class FileController {
|
|
|
42
59
|
*/
|
|
43
60
|
@Get(':id')
|
|
44
61
|
@Roles(RoleEnum.ADMIN)
|
|
45
|
-
async getFile(@Param('id') id: string, @Res() res) {
|
|
62
|
+
async getFile(@Param('id') id: string, @Res() res: Response) {
|
|
46
63
|
if (!id) {
|
|
47
64
|
throw new BadRequestException('Missing ID');
|
|
48
65
|
}
|
|
49
66
|
|
|
50
|
-
let file;
|
|
67
|
+
let file: CoreFileInfo | null;
|
|
51
68
|
try {
|
|
52
69
|
file = await this.fileService.getFileInfo(id);
|
|
53
70
|
} catch (e) {
|
|
54
71
|
console.error(e);
|
|
72
|
+
file = null;
|
|
55
73
|
}
|
|
56
74
|
|
|
57
75
|
if (!file) {
|
|
58
76
|
throw new NotFoundException('File not found');
|
|
59
77
|
}
|
|
60
78
|
const filestream = await this.fileService.getFileStream(id);
|
|
61
|
-
res.header('Content-Type', file.contentType);
|
|
79
|
+
res.header('Content-Type', file.contentType || 'application/octet-stream');
|
|
62
80
|
res.header('Content-Disposition', `attachment; filename=${file.filename}`);
|
|
63
81
|
res.header('Cache-Control', 'max-age=31536000');
|
|
64
82
|
return filestream.pipe(res);
|
|
@@ -64,8 +64,7 @@ export class FileResolver {
|
|
|
64
64
|
// Save files in filesystem
|
|
65
65
|
const promises: Promise<any>[] = [];
|
|
66
66
|
for (const file of files) {
|
|
67
|
-
|
|
68
|
-
const { createReadStream, encoding, filename, mimetype } = await file;
|
|
67
|
+
const { createReadStream, filename } = await file;
|
|
69
68
|
await fs.promises.mkdir('./uploads', { recursive: true });
|
|
70
69
|
promises.push(
|
|
71
70
|
new Promise((resolve, reject) =>
|
|
@@ -1,31 +1,16 @@
|
|
|
1
|
-
import { GridFsStorage } from '@lenne.tech/multer-gridfs-storage';
|
|
2
1
|
import { Injectable } from '@nestjs/common';
|
|
3
2
|
import { MulterModuleOptions, MulterOptionsFactory } from '@nestjs/platform-express';
|
|
3
|
+
import { memoryStorage } from 'multer';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Multer configuration service using MemoryStorage
|
|
7
|
+
* Files are stored in memory as Buffer objects and then manually saved to GridFS
|
|
8
|
+
*/
|
|
7
9
|
@Injectable()
|
|
8
10
|
export class GridFsMulterConfigService implements MulterOptionsFactory {
|
|
9
|
-
gridFsStorage: any;
|
|
10
|
-
|
|
11
|
-
constructor() {
|
|
12
|
-
this.gridFsStorage = new GridFsStorage({
|
|
13
|
-
file: (req, file) => {
|
|
14
|
-
return new Promise((resolve) => {
|
|
15
|
-
const filename = file.originalname.trim();
|
|
16
|
-
const fileInfo = {
|
|
17
|
-
filename,
|
|
18
|
-
};
|
|
19
|
-
resolve(fileInfo);
|
|
20
|
-
});
|
|
21
|
-
},
|
|
22
|
-
url: envConfig.mongoose.uri,
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
|
|
26
11
|
createMulterOptions(): MulterModuleOptions {
|
|
27
12
|
return {
|
|
28
|
-
storage:
|
|
13
|
+
storage: memoryStorage(),
|
|
29
14
|
};
|
|
30
15
|
}
|
|
31
16
|
}
|
|
@@ -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
|
+
}
|