@flusys/nestjs-storage 1.0.0-beta → 1.0.0-rc

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 (60) hide show
  1. package/README.md +131 -13
  2. package/cjs/config/storage-config.service.js +5 -0
  3. package/cjs/config/storage.constants.js +0 -8
  4. package/cjs/controllers/file-manager.controller.js +44 -1
  5. package/cjs/controllers/folder.controller.js +44 -1
  6. package/cjs/controllers/storage-config.controller.js +44 -1
  7. package/cjs/controllers/upload.controller.js +6 -12
  8. package/cjs/dtos/file-manager.dto.js +8 -5
  9. package/cjs/dtos/storage-config.dto.js +41 -1
  10. package/cjs/dtos/upload.dto.js +7 -0
  11. package/cjs/entities/storage-config-base.entity.js +31 -2
  12. package/cjs/interfaces/index.js +0 -1
  13. package/cjs/middlewares/file-serve.middleware.js +6 -0
  14. package/cjs/providers/local-provider.js +52 -2
  15. package/cjs/providers/storage-factory.service.js +2 -2
  16. package/cjs/services/file-manager.service.js +37 -24
  17. package/cjs/services/folder.service.js +5 -8
  18. package/cjs/services/storage-provider-config.service.js +18 -35
  19. package/cjs/services/upload.service.js +39 -27
  20. package/cjs/utils/file-validator.util.js +470 -0
  21. package/cjs/utils/image-compressor.util.js +1 -3
  22. package/config/storage-config.service.d.ts +5 -2
  23. package/config/storage.constants.d.ts +0 -2
  24. package/controllers/upload.controller.d.ts +2 -6
  25. package/dtos/file-manager.dto.d.ts +2 -4
  26. package/dtos/folder.dto.d.ts +2 -4
  27. package/dtos/storage-config.dto.d.ts +9 -6
  28. package/entities/storage-config-base.entity.d.ts +2 -0
  29. package/fesm/config/storage-config.service.js +5 -0
  30. package/fesm/config/storage.constants.js +0 -2
  31. package/fesm/controllers/file-manager.controller.js +45 -2
  32. package/fesm/controllers/folder.controller.js +45 -2
  33. package/fesm/controllers/storage-config.controller.js +45 -2
  34. package/fesm/controllers/upload.controller.js +7 -13
  35. package/fesm/dtos/file-manager.dto.js +8 -5
  36. package/fesm/dtos/storage-config.dto.js +45 -11
  37. package/fesm/dtos/upload.dto.js +8 -1
  38. package/fesm/entities/index.js +1 -5
  39. package/fesm/entities/storage-config-base.entity.js +33 -7
  40. package/fesm/interfaces/index.js +0 -1
  41. package/fesm/interfaces/storage-config.interface.js +1 -3
  42. package/fesm/middlewares/file-serve.middleware.js +7 -1
  43. package/fesm/providers/local-provider.js +52 -2
  44. package/fesm/providers/storage-factory.service.js +2 -2
  45. package/fesm/services/file-manager.service.js +38 -25
  46. package/fesm/services/folder.service.js +5 -8
  47. package/fesm/services/storage-provider-config.service.js +18 -35
  48. package/fesm/services/upload.service.js +40 -28
  49. package/fesm/utils/file-validator.util.js +463 -0
  50. package/fesm/utils/image-compressor.util.js +1 -3
  51. package/interfaces/file-manager.interface.d.ts +7 -4
  52. package/interfaces/index.d.ts +0 -1
  53. package/interfaces/storage-config.interface.d.ts +2 -20
  54. package/package.json +6 -6
  55. package/providers/local-provider.d.ts +2 -0
  56. package/services/file-manager.service.d.ts +2 -2
  57. package/utils/file-validator.util.d.ts +16 -0
  58. package/cjs/interfaces/file-upload-response.interface.js +0 -4
  59. package/fesm/interfaces/file-upload-response.interface.js +0 -1
  60. package/interfaces/file-upload-response.interface.d.ts +0 -6
@@ -0,0 +1,463 @@
1
+ function _define_property(obj, key, value) {
2
+ if (key in obj) {
3
+ Object.defineProperty(obj, key, {
4
+ value: value,
5
+ enumerable: true,
6
+ configurable: true,
7
+ writable: true
8
+ });
9
+ } else {
10
+ obj[key] = value;
11
+ }
12
+ return obj;
13
+ }
14
+ import { Logger } from '@nestjs/common';
15
+ /**
16
+ * Magic byte signatures for common file types.
17
+ * Each entry maps a hex signature pattern to its MIME type.
18
+ */ const MAGIC_BYTES = [
19
+ // Images
20
+ {
21
+ signature: [
22
+ 0xff,
23
+ 0xd8,
24
+ 0xff
25
+ ],
26
+ offset: 0,
27
+ mimeType: 'image/jpeg'
28
+ },
29
+ {
30
+ signature: [
31
+ 0x89,
32
+ 0x50,
33
+ 0x4e,
34
+ 0x47,
35
+ 0x0d,
36
+ 0x0a,
37
+ 0x1a,
38
+ 0x0a
39
+ ],
40
+ offset: 0,
41
+ mimeType: 'image/png'
42
+ },
43
+ {
44
+ signature: [
45
+ 0x47,
46
+ 0x49,
47
+ 0x46,
48
+ 0x38,
49
+ 0x37,
50
+ 0x61
51
+ ],
52
+ offset: 0,
53
+ mimeType: 'image/gif'
54
+ },
55
+ {
56
+ signature: [
57
+ 0x47,
58
+ 0x49,
59
+ 0x46,
60
+ 0x38,
61
+ 0x39,
62
+ 0x61
63
+ ],
64
+ offset: 0,
65
+ mimeType: 'image/gif'
66
+ },
67
+ {
68
+ signature: [
69
+ 0x42,
70
+ 0x4d
71
+ ],
72
+ offset: 0,
73
+ mimeType: 'image/bmp'
74
+ },
75
+ {
76
+ signature: [
77
+ 0x52,
78
+ 0x49,
79
+ 0x46,
80
+ 0x46
81
+ ],
82
+ offset: 0,
83
+ mimeType: 'image/webp'
84
+ },
85
+ {
86
+ signature: [
87
+ 0x00,
88
+ 0x00,
89
+ 0x01,
90
+ 0x00
91
+ ],
92
+ offset: 0,
93
+ mimeType: 'image/x-icon'
94
+ },
95
+ {
96
+ signature: [
97
+ 0x00,
98
+ 0x00,
99
+ 0x02,
100
+ 0x00
101
+ ],
102
+ offset: 0,
103
+ mimeType: 'image/x-icon'
104
+ },
105
+ // Documents
106
+ {
107
+ signature: [
108
+ 0x25,
109
+ 0x50,
110
+ 0x44,
111
+ 0x46
112
+ ],
113
+ offset: 0,
114
+ mimeType: 'application/pdf'
115
+ },
116
+ {
117
+ signature: [
118
+ 0x50,
119
+ 0x4b,
120
+ 0x03,
121
+ 0x04
122
+ ],
123
+ offset: 0,
124
+ mimeType: 'application/zip'
125
+ },
126
+ // Audio
127
+ {
128
+ signature: [
129
+ 0x49,
130
+ 0x44,
131
+ 0x33
132
+ ],
133
+ offset: 0,
134
+ mimeType: 'audio/mpeg'
135
+ },
136
+ {
137
+ signature: [
138
+ 0xff,
139
+ 0xfb
140
+ ],
141
+ offset: 0,
142
+ mimeType: 'audio/mpeg'
143
+ },
144
+ {
145
+ signature: [
146
+ 0xff,
147
+ 0xfa
148
+ ],
149
+ offset: 0,
150
+ mimeType: 'audio/mpeg'
151
+ },
152
+ {
153
+ signature: [
154
+ 0x4f,
155
+ 0x67,
156
+ 0x67,
157
+ 0x53
158
+ ],
159
+ offset: 0,
160
+ mimeType: 'audio/ogg'
161
+ },
162
+ {
163
+ signature: [
164
+ 0x66,
165
+ 0x4c,
166
+ 0x61,
167
+ 0x43
168
+ ],
169
+ offset: 0,
170
+ mimeType: 'audio/flac'
171
+ },
172
+ // Video
173
+ {
174
+ signature: [
175
+ 0x00,
176
+ 0x00,
177
+ 0x00,
178
+ 0x1c,
179
+ 0x66,
180
+ 0x74,
181
+ 0x79,
182
+ 0x70
183
+ ],
184
+ offset: 0,
185
+ mimeType: 'video/mp4'
186
+ },
187
+ {
188
+ signature: [
189
+ 0x00,
190
+ 0x00,
191
+ 0x00,
192
+ 0x20,
193
+ 0x66,
194
+ 0x74,
195
+ 0x79,
196
+ 0x70
197
+ ],
198
+ offset: 0,
199
+ mimeType: 'video/mp4'
200
+ },
201
+ {
202
+ signature: [
203
+ 0x1a,
204
+ 0x45,
205
+ 0xdf,
206
+ 0xa3
207
+ ],
208
+ offset: 0,
209
+ mimeType: 'video/webm'
210
+ },
211
+ {
212
+ signature: [
213
+ 0x52,
214
+ 0x49,
215
+ 0x46,
216
+ 0x46
217
+ ],
218
+ offset: 0,
219
+ mimeType: 'video/avi'
220
+ },
221
+ // Archives
222
+ {
223
+ signature: [
224
+ 0x1f,
225
+ 0x8b
226
+ ],
227
+ offset: 0,
228
+ mimeType: 'application/gzip'
229
+ },
230
+ {
231
+ signature: [
232
+ 0x37,
233
+ 0x7a,
234
+ 0xbc,
235
+ 0xaf,
236
+ 0x27,
237
+ 0x1c
238
+ ],
239
+ offset: 0,
240
+ mimeType: 'application/x-7z-compressed'
241
+ },
242
+ {
243
+ signature: [
244
+ 0x52,
245
+ 0x61,
246
+ 0x72,
247
+ 0x21,
248
+ 0x1a,
249
+ 0x07
250
+ ],
251
+ offset: 0,
252
+ mimeType: 'application/x-rar-compressed'
253
+ }
254
+ ];
255
+ /**
256
+ * MIME type aliases - types that are equivalent.
257
+ */ const MIME_ALIASES = {
258
+ 'image/jpeg': [
259
+ 'image/jpg'
260
+ ],
261
+ 'image/jpg': [
262
+ 'image/jpeg'
263
+ ],
264
+ 'video/mp4': [
265
+ 'video/quicktime'
266
+ ],
267
+ 'application/zip': [
268
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
269
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
270
+ 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
271
+ ]
272
+ };
273
+ /**
274
+ * File types that are text-based and don't have magic bytes.
275
+ * SECURITY NOTE: Dangerous types (HTML, JS, SVG) are excluded as they can contain scripts.
276
+ * These types require explicit allowlisting and additional content scanning.
277
+ */ const TEXT_BASED_TYPES = [
278
+ 'text/plain',
279
+ 'text/csv',
280
+ 'text/markdown',
281
+ 'application/json',
282
+ 'application/xml',
283
+ 'application/typescript',
284
+ 'text/css'
285
+ ];
286
+ /**
287
+ * Dangerous text-based types that can execute scripts.
288
+ * These bypass magic-bytes validation but require explicit allowlisting.
289
+ */ const DANGEROUS_TEXT_TYPES = [
290
+ 'text/html',
291
+ 'application/javascript',
292
+ 'text/javascript',
293
+ 'image/svg+xml',
294
+ 'application/xhtml+xml'
295
+ ];
296
+ /**
297
+ * Utility class for validating file content using magic bytes.
298
+ * Prevents file type spoofing by checking actual file content.
299
+ */ export class FileValidator {
300
+ /**
301
+ * Detect file type from buffer using magic bytes.
302
+ * @param buffer - File buffer to analyze
303
+ * @returns Detected MIME type or null if unknown
304
+ */ static detectFileType(buffer) {
305
+ for (const { signature, offset, mimeType } of MAGIC_BYTES){
306
+ if (buffer.length < offset + signature.length) continue;
307
+ let matches = true;
308
+ for(let i = 0; i < signature.length; i++){
309
+ if (buffer[offset + i] !== signature[i]) {
310
+ matches = false;
311
+ break;
312
+ }
313
+ }
314
+ if (matches) {
315
+ return mimeType;
316
+ }
317
+ }
318
+ return null;
319
+ }
320
+ /**
321
+ * Check if a MIME type is text-based (doesn't have magic bytes).
322
+ */ static isTextBasedType(mimeType) {
323
+ return TEXT_BASED_TYPES.some((t)=>mimeType.startsWith(t) || mimeType === t);
324
+ }
325
+ /**
326
+ * Check if a MIME type is a dangerous text type that can execute scripts.
327
+ */ static isDangerousTextType(mimeType) {
328
+ return DANGEROUS_TEXT_TYPES.some((t)=>mimeType === t);
329
+ }
330
+ /**
331
+ * Check if two MIME types are compatible (exact match or aliases).
332
+ */ static mimeTypesMatch(detected, declared) {
333
+ // Exact match
334
+ if (detected === declared) return true;
335
+ // Check aliases
336
+ const aliases = MIME_ALIASES[detected];
337
+ if (aliases?.includes(declared)) return true;
338
+ // Check reverse aliases
339
+ const reverseAliases = MIME_ALIASES[declared];
340
+ if (reverseAliases?.includes(detected)) return true;
341
+ // Check if both are in same category (e.g., both images)
342
+ const detectedCategory = detected.split('/')[0];
343
+ const declaredCategory = declared.split('/')[0];
344
+ // For ZIP-based formats, allow any ZIP-detected file if declared is a ZIP variant
345
+ if (detected === 'application/zip') {
346
+ const zipVariants = [
347
+ 'application/vnd.openxmlformats-officedocument',
348
+ 'application/x-zip',
349
+ 'application/x-compressed'
350
+ ];
351
+ if (zipVariants.some((v)=>declared.startsWith(v))) {
352
+ return true;
353
+ }
354
+ }
355
+ return detectedCategory === declaredCategory;
356
+ }
357
+ /**
358
+ * Check if a MIME type is in the allowed list.
359
+ */ static isTypeAllowed(mimeType, allowedTypes) {
360
+ // Wildcard allows all
361
+ if (allowedTypes.includes('*/*')) return true;
362
+ return allowedTypes.some((allowed)=>{
363
+ // Category wildcard (e.g., "image/*")
364
+ if (allowed.endsWith('/*')) {
365
+ const category = allowed.slice(0, -2);
366
+ return mimeType.startsWith(category);
367
+ }
368
+ return allowed === mimeType;
369
+ });
370
+ }
371
+ /**
372
+ * Validate file content matches declared MIME type using magic bytes.
373
+ * @param buffer - File buffer
374
+ * @param declaredMimeType - MIME type declared by client
375
+ * @param allowedTypes - List of allowed MIME types/patterns
376
+ * @returns Validation result
377
+ */ static validateFileContent(buffer, declaredMimeType, allowedTypes = [
378
+ '*/*'
379
+ ]) {
380
+ try {
381
+ // Detect actual file type from magic bytes
382
+ const detectedType = this.detectFileType(buffer);
383
+ // If no type detected, check if it's a text-based type
384
+ if (!detectedType) {
385
+ // Check for dangerous text types first (HTML, JS, SVG)
386
+ if (this.isDangerousTextType(declaredMimeType)) {
387
+ // Only allow dangerous types if explicitly in allowedTypes (not via wildcard)
388
+ const explicitlyAllowed = allowedTypes.some((t)=>t === declaredMimeType && t !== '*/*' && !t.endsWith('/*'));
389
+ if (!explicitlyAllowed) {
390
+ this.logger.warn(`Blocked dangerous file type: ${declaredMimeType} - requires explicit allowlisting`);
391
+ return {
392
+ valid: false,
393
+ detectedType: declaredMimeType,
394
+ declaredType: declaredMimeType,
395
+ message: `File type "${declaredMimeType}" is potentially dangerous and not explicitly allowed`
396
+ };
397
+ }
398
+ this.logger.warn(`Allowing explicitly permitted dangerous file type: ${declaredMimeType}`);
399
+ }
400
+ if (this.isTextBasedType(declaredMimeType)) {
401
+ // Safe text-based files don't have magic bytes, trust the declared type
402
+ const isAllowed = this.isTypeAllowed(declaredMimeType, allowedTypes);
403
+ return {
404
+ valid: isAllowed,
405
+ detectedType: declaredMimeType,
406
+ declaredType: declaredMimeType,
407
+ message: isAllowed ? undefined : `File type "${declaredMimeType}" is not allowed`
408
+ };
409
+ }
410
+ // For binary files without recognized signatures, be cautious
411
+ this.logger.warn(`Unable to detect file type for declared type: ${declaredMimeType}`);
412
+ return {
413
+ valid: false,
414
+ declaredType: declaredMimeType,
415
+ message: 'Unable to verify file type. File may be corrupted or unsupported.'
416
+ };
417
+ }
418
+ // Check if detected type matches declared type
419
+ if (!this.mimeTypesMatch(detectedType, declaredMimeType)) {
420
+ this.logger.warn(`MIME type mismatch: declared=${declaredMimeType}, detected=${detectedType}`);
421
+ return {
422
+ valid: false,
423
+ detectedType,
424
+ declaredType: declaredMimeType,
425
+ message: `File content does not match declared type. Detected: ${detectedType}, Declared: ${declaredMimeType}`
426
+ };
427
+ }
428
+ // Check if detected type is allowed
429
+ if (!this.isTypeAllowed(detectedType, allowedTypes)) {
430
+ return {
431
+ valid: false,
432
+ detectedType,
433
+ declaredType: declaredMimeType,
434
+ message: `File type "${detectedType}" is not allowed`
435
+ };
436
+ }
437
+ return {
438
+ valid: true,
439
+ detectedType,
440
+ declaredType: declaredMimeType
441
+ };
442
+ } catch (error) {
443
+ this.logger.error('File validation error:', error);
444
+ return {
445
+ valid: false,
446
+ message: 'File validation failed'
447
+ };
448
+ }
449
+ }
450
+ /**
451
+ * Sanitize filename to prevent path traversal and special character issues.
452
+ * @param filename - Original filename
453
+ * @returns Sanitized filename
454
+ */ static sanitizeFilename(filename) {
455
+ return filename// Remove path components (prevent traversal)
456
+ .replace(/^.*[\\\/]/, '')// Remove null bytes
457
+ .replace(/\0/g, '')// Replace multiple dots with single
458
+ .replace(/\.{2,}/g, '.')// Remove special characters except allowed ones
459
+ .replace(/[^a-zA-Z0-9._-]/g, '_')// Limit length
460
+ .substring(0, 255);
461
+ }
462
+ }
463
+ _define_property(FileValidator, "logger", new Logger(FileValidator.name));
@@ -107,9 +107,7 @@ const sharp = sharpModule.default || sharpModule;
107
107
  buffer: data,
108
108
  format: `image/${targetFormat}`
109
109
  };
110
- } catch (error) {
111
- // Fallback to original if processing fails
112
- console.warn(`Image processing failed: ${error instanceof Error ? error.message : String(error)}`);
110
+ } catch {
113
111
  return {
114
112
  buffer,
115
113
  format: mimetype
@@ -3,12 +3,15 @@ export interface IFileManager extends IIdentity {
3
3
  name?: string;
4
4
  key?: string;
5
5
  url?: string;
6
- folder?: number;
7
- size?: number;
8
- expiresAt?: number;
6
+ folder?: {
7
+ id: string;
8
+ name?: string;
9
+ } | null;
10
+ size?: string;
11
+ expiresAt?: number | null;
9
12
  contentType?: string;
10
13
  location?: string;
11
- storageConfigId?: string;
14
+ storageConfigId?: string | null;
12
15
  providerName?: string;
13
16
  isPrivate?: boolean;
14
17
  companyId?: string | null;
@@ -1,5 +1,4 @@
1
1
  export * from './file-manager.interface';
2
- export * from './file-upload-response.interface';
3
2
  export * from './folder.interface';
4
3
  export * from './storage-config.interface';
5
4
  export * from './storage-module-options.interface';
@@ -4,25 +4,7 @@ export interface IStorageConfig extends IIdentity {
4
4
  name: string;
5
5
  storage: FileLocationEnum;
6
6
  config: Record<string, any>;
7
+ isActive: boolean;
8
+ isDefault: boolean;
7
9
  companyId?: string | null;
8
10
  }
9
- export interface IAwsS3Config {
10
- region: string;
11
- bucket: string;
12
- accessKeyId: string;
13
- secretAccessKey: string;
14
- endpoint?: string;
15
- }
16
- export interface IAzureBlobConfig {
17
- accountName: string;
18
- accountKey: string;
19
- containerName: string;
20
- }
21
- export interface ISftpConfig {
22
- host: string;
23
- port: number;
24
- username: string;
25
- password?: string;
26
- privateKey?: string;
27
- basePath: string;
28
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flusys/nestjs-storage",
3
- "version": "1.0.0-beta",
3
+ "version": "1.0.0-rc",
4
4
  "description": "Modular storage package with optional AWS S3, Azure Blob, and SFTP providers",
5
5
  "main": "cjs/index.js",
6
6
  "module": "fesm/index.js",
@@ -94,15 +94,15 @@
94
94
  "class-transformer": "^0.5.0",
95
95
  "class-validator": "^0.14.0",
96
96
  "typeorm": "^0.3.0",
97
- "multer": "^1.4.0",
98
- "sharp": "^0.33.0",
97
+ "multer": "^2.0.0",
98
+ "sharp": "^0.34.0",
99
99
  "mime-types": "^2.1.0",
100
100
  "uuid": "^9.0.0 || ^11.0.0",
101
101
  "@aws-sdk/client-s3": "^3.400.0",
102
102
  "@aws-sdk/lib-storage": "^3.400.0",
103
103
  "@aws-sdk/s3-request-presigner": "^3.400.0",
104
104
  "@azure/storage-blob": "^12.15.0",
105
- "ssh2-sftp-client": "^10.0.0"
105
+ "ssh2-sftp-client": "^12.0.0"
106
106
  },
107
107
  "peerDependenciesMeta": {
108
108
  "sharp": {
@@ -128,7 +128,7 @@
128
128
  }
129
129
  },
130
130
  "dependencies": {
131
- "@flusys/nestjs-core": "1.0.0-beta",
132
- "@flusys/nestjs-shared": "1.0.0-beta"
131
+ "@flusys/nestjs-core": "1.0.0-rc",
132
+ "@flusys/nestjs-shared": "1.0.0-rc"
133
133
  }
134
134
  }
@@ -5,6 +5,8 @@ export declare class LocalProvider implements IStorageProvider {
5
5
  private basePath;
6
6
  private baseUrl;
7
7
  private relativeBasePath;
8
+ private validatePathWithinBase;
9
+ private validateKeyFormat;
8
10
  initialize(config: {
9
11
  basePath?: string;
10
12
  baseUrl?: string;
@@ -1,8 +1,8 @@
1
- import { RequestScopedApiService, HybridCache } from '@flusys/nestjs-shared/classes';
1
+ import { HybridCache, RequestScopedApiService } from '@flusys/nestjs-shared/classes';
2
2
  import { DeleteDto, FilterAndPaginationDto } from '@flusys/nestjs-shared/dtos';
3
3
  import { ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
4
4
  import { UtilsService } from '@flusys/nestjs-shared/modules';
5
- import { QueryRunner, EntityTarget, Repository, SelectQueryBuilder } from 'typeorm';
5
+ import { EntityTarget, QueryRunner, Repository, SelectQueryBuilder } from 'typeorm';
6
6
  import { StorageConfigService } from '../config';
7
7
  import { CreateFileManagerDto, FilesResponseDto, GetFilesRequestDto, UpdateFileManagerDto } from '../dtos';
8
8
  import { FileManagerBase } from '../entities';
@@ -0,0 +1,16 @@
1
+ export interface FileValidationResult {
2
+ valid: boolean;
3
+ detectedType?: string;
4
+ declaredType?: string;
5
+ message?: string;
6
+ }
7
+ export declare class FileValidator {
8
+ private static logger;
9
+ static detectFileType(buffer: Buffer): string | null;
10
+ static isTextBasedType(mimeType: string): boolean;
11
+ static isDangerousTextType(mimeType: string): boolean;
12
+ static mimeTypesMatch(detected: string, declared: string): boolean;
13
+ static isTypeAllowed(mimeType: string, allowedTypes: string[]): boolean;
14
+ static validateFileContent(buffer: Buffer, declaredMimeType: string, allowedTypes?: string[]): FileValidationResult;
15
+ static sanitizeFilename(filename: string): string;
16
+ }
@@ -1,4 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
@@ -1 +0,0 @@
1
- export { };
@@ -1,6 +0,0 @@
1
- export interface IFileUploadResponse {
2
- name: string;
3
- contentType: string;
4
- size: number;
5
- key: string;
6
- }