@flusys/nestjs-storage 9.1.2 → 9.2.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/README.md +13 -0
- package/controllers/file-manager.controller.d.ts +1 -0
- package/controllers/folder.controller.d.ts +1 -0
- package/controllers/storage-config.controller.d.ts +1 -0
- package/fesm/493.js +416 -251
- package/fesm/537.js +25 -21
- package/fesm/562.js +184 -172
- package/fesm/chunks/190.js +6 -6
- package/fesm/chunks/252.js +1 -1
- package/fesm/chunks/327.js +1 -1
- package/fesm/chunks/385.js +1 -1
- package/fesm/chunks/591.js +11 -11
- package/fesm/chunks/746.js +1 -1
- package/fesm/chunks/798.js +1 -1
- package/fesm/chunks/918.js +19 -7
- package/fesm/config/index.js +8 -8
- package/fesm/controllers/index.js +1 -1
- package/fesm/docs/index.js +11 -15
- package/fesm/dtos/index.js +24 -20
- package/fesm/entities/index.js +6 -6
- package/fesm/enums/index.js +4 -4
- package/fesm/index.js +30 -34
- package/fesm/interfaces/index.js +2 -2
- package/fesm/modules/index.js +1 -1
- package/fesm/providers/index.js +123 -129
- package/fesm/services/index.js +1 -1
- package/fesm/utils/index.js +88 -84
- package/middlewares/file-serve.middleware.d.ts +3 -1
- package/package.json +3 -3
- package/providers/local-provider.d.ts +2 -0
- package/providers/sftp-provider.optional.d.ts +1 -0
- package/providers/storage-factory.service.d.ts +4 -2
- package/services/file-manager.service.d.ts +5 -6
- package/services/folder.service.d.ts +5 -5
- package/services/storage-provider-config.service.d.ts +5 -2
- package/services/upload.service.d.ts +3 -1
- package/utils/company-ownership.util.d.ts +3 -0
- package/utils/file-validator.util.d.ts +1 -0
package/fesm/providers/index.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import * as __rspack_external_sharp from "sharp";
|
|
2
|
-
import * as __rspack_external_path from "path";
|
|
3
2
|
import * as __rspack_external_uuid from "uuid";
|
|
4
3
|
import * as __rspack_external_fs_promises_e1851c9c from "fs/promises";
|
|
5
4
|
export const __rspack_esm_id = 467;
|
|
6
5
|
export const __rspack_esm_ids = [467];
|
|
7
6
|
export const __webpack_modules__ = {
|
|
8
|
-
|
|
9
|
-
/* import */ var _storage_factory_service_js__rspack_import_0 = __webpack_require__(
|
|
10
|
-
/* import */ var _storage_provider_registry_js__rspack_import_1 = __webpack_require__(
|
|
7
|
+
1922(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
8
|
+
/* import */ var _storage_factory_service_js__rspack_import_0 = __webpack_require__(2192);
|
|
9
|
+
/* import */ var _storage_provider_registry_js__rspack_import_1 = __webpack_require__(9264);
|
|
11
10
|
/* import */ var _local_provider_js__rspack_import_2 = __webpack_require__(632);
|
|
12
11
|
// Core exports
|
|
13
12
|
|
|
@@ -35,15 +34,15 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
35
34
|
});
|
|
36
35
|
|
|
37
36
|
// EXTERNAL MODULE: external "@nestjs/common"
|
|
38
|
-
var common_ = __webpack_require__(
|
|
37
|
+
var common_ = __webpack_require__(6157);
|
|
39
38
|
// EXTERNAL MODULE: external "@flusys/nestjs-shared/constants"
|
|
40
|
-
var constants_ = __webpack_require__(
|
|
39
|
+
var constants_ = __webpack_require__(9140);
|
|
41
40
|
// EXTERNAL MODULE: ./projects/nestjs-storage/src/utils/image-compressor.util.ts + 1 modules
|
|
42
|
-
var image_compressor_util = __webpack_require__(
|
|
41
|
+
var image_compressor_util = __webpack_require__(1872);
|
|
43
42
|
;// CONCATENATED MODULE: external "fs/promises"
|
|
44
43
|
|
|
45
44
|
// EXTERNAL MODULE: external "path"
|
|
46
|
-
var external_path_ = __webpack_require__(
|
|
45
|
+
var external_path_ = __webpack_require__(2521);
|
|
47
46
|
// EXTERNAL MODULE: external "uuid"
|
|
48
47
|
var external_uuid_ = __webpack_require__(37);
|
|
49
48
|
;// CONCATENATED MODULE: ./projects/nestjs-storage/src/providers/local-provider.ts
|
|
@@ -71,20 +70,26 @@ var external_uuid_ = __webpack_require__(37);
|
|
|
71
70
|
basePath = '';
|
|
72
71
|
baseUrl = '';
|
|
73
72
|
relativeBasePath = '';
|
|
73
|
+
isWithinBase(targetPath) {
|
|
74
|
+
const normalizedBasePath = external_path_.resolve(this.basePath);
|
|
75
|
+
const normalizedTargetPath = external_path_.resolve(targetPath);
|
|
76
|
+
return normalizedTargetPath.startsWith(normalizedBasePath + external_path_.sep) || normalizedTargetPath === normalizedBasePath;
|
|
77
|
+
}
|
|
74
78
|
/**
|
|
75
79
|
* SECURITY: Validates that a target path does not escape the base directory
|
|
76
80
|
* Prevents path traversal attacks using ../ sequences
|
|
77
81
|
* @throws Error if path traversal is detected
|
|
78
82
|
*/ validatePathWithinBase(targetPath) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (!normalizedTargetPath.startsWith(normalizedBasePath + external_path_.sep) && normalizedTargetPath !== normalizedBasePath) {
|
|
82
|
-
throw new common_.BadRequestException({
|
|
83
|
-
message: 'Invalid path: Path traversal attempt detected',
|
|
84
|
-
messageKey: constants_.SYSTEM_MESSAGES.PATH_TRAVERSAL_DETECTED
|
|
85
|
-
});
|
|
83
|
+
if (!this.isWithinBase(targetPath)) {
|
|
84
|
+
this.throwTraversal();
|
|
86
85
|
}
|
|
87
86
|
}
|
|
87
|
+
throwTraversal() {
|
|
88
|
+
throw new common_.BadRequestException({
|
|
89
|
+
message: 'Invalid path: Path traversal attempt detected',
|
|
90
|
+
messageKey: constants_.SYSTEM_MESSAGES.PATH_TRAVERSAL_DETECTED
|
|
91
|
+
});
|
|
92
|
+
}
|
|
88
93
|
/**
|
|
89
94
|
* SECURITY: Validates file key format to prevent malicious input
|
|
90
95
|
* @throws Error if key contains suspicious patterns
|
|
@@ -159,36 +164,27 @@ var external_uuid_ = __webpack_require__(37);
|
|
|
159
164
|
return Promise.all(files.map((file)=>this.uploadFile(file, options)));
|
|
160
165
|
}
|
|
161
166
|
async deleteFile(key) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
this.
|
|
169
|
-
|
|
167
|
+
// SECURITY: Validate key format first
|
|
168
|
+
this.validateKeyFormat(key);
|
|
169
|
+
// New keys include the basePath and resolve from cwd; old keys are relative to basePath.
|
|
170
|
+
// Only candidates inside the base directory are ever touched.
|
|
171
|
+
const candidates = [
|
|
172
|
+
external_path_.resolve(key),
|
|
173
|
+
external_path_.join(this.basePath, key)
|
|
174
|
+
].filter((candidate)=>this.isWithinBase(candidate) && external_path_.resolve(candidate) !== this.basePath);
|
|
175
|
+
if (!candidates.length) this.throwTraversal();
|
|
176
|
+
for (const candidate of candidates){
|
|
170
177
|
try {
|
|
171
|
-
await __rspack_external_fs_promises_e1851c9c.access(
|
|
178
|
+
await __rspack_external_fs_promises_e1851c9c.access(candidate);
|
|
172
179
|
} catch {
|
|
173
|
-
|
|
174
|
-
const fallbackPath = external_path_.join(this.basePath, key);
|
|
175
|
-
// SECURITY: Validate fallback path as well
|
|
176
|
-
this.validatePathWithinBase(fallbackPath);
|
|
177
|
-
try {
|
|
178
|
-
await __rspack_external_fs_promises_e1851c9c.access(fallbackPath);
|
|
179
|
-
filePath = fallbackPath;
|
|
180
|
-
} catch {
|
|
181
|
-
// File doesn't exist at either location - silent return
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
180
|
+
continue;
|
|
184
181
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
throw error;
|
|
182
|
+
try {
|
|
183
|
+
await __rspack_external_fs_promises_e1851c9c.unlink(candidate);
|
|
184
|
+
} catch {
|
|
185
|
+
// Silent failure - the file may have been removed concurrently
|
|
190
186
|
}
|
|
191
|
-
|
|
187
|
+
return;
|
|
192
188
|
}
|
|
193
189
|
}
|
|
194
190
|
async deleteMultipleFiles(keys) {
|
|
@@ -217,7 +213,7 @@ var external_uuid_ = __webpack_require__(37);
|
|
|
217
213
|
|
|
218
214
|
|
|
219
215
|
},
|
|
220
|
-
|
|
216
|
+
1872(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
221
217
|
|
|
222
218
|
// EXPORTS
|
|
223
219
|
__webpack_require__.d(__webpack_exports__, {
|
|
@@ -230,6 +226,10 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
230
226
|
|
|
231
227
|
// Handle ESM/CJS interop - sharp exports differently in each module system
|
|
232
228
|
const sharp = __rspack_external_sharp["default"] || __rspack_external_sharp;
|
|
229
|
+
const MULTI_FRAME_FORMATS = [
|
|
230
|
+
'gif',
|
|
231
|
+
'webp'
|
|
232
|
+
];
|
|
233
233
|
/**
|
|
234
234
|
* Image Compression Utility
|
|
235
235
|
* Handles image compression and format conversion using sharp
|
|
@@ -247,87 +247,87 @@ const sharp = __rspack_external_sharp["default"] || __rspack_external_sharp;
|
|
|
247
247
|
format: mimetype
|
|
248
248
|
};
|
|
249
249
|
}
|
|
250
|
-
|
|
251
|
-
const image = sharp(buffer, {
|
|
252
|
-
failOnError: false,
|
|
253
|
-
density: 300
|
|
254
|
-
});
|
|
255
|
-
// Apply initial resize
|
|
256
|
-
image.resize({
|
|
257
|
-
width: maxWidth,
|
|
258
|
-
height: maxHeight,
|
|
259
|
-
fit: 'inside',
|
|
260
|
-
withoutEnlargement: true
|
|
261
|
-
});
|
|
262
|
-
// Determine target format
|
|
263
|
-
let targetFormat = format !== 'original' ? format : mimetype.split('/')[1];
|
|
264
|
-
// Format-specific optimizations
|
|
265
|
-
switch(targetFormat.toLowerCase()){
|
|
266
|
-
case 'jpeg':
|
|
267
|
-
case 'jpg':
|
|
268
|
-
image.jpeg({
|
|
269
|
-
quality,
|
|
270
|
-
mozjpeg: true,
|
|
271
|
-
chromaSubsampling: '4:4:4'
|
|
272
|
-
});
|
|
273
|
-
break;
|
|
274
|
-
case 'png':
|
|
275
|
-
image.png({
|
|
276
|
-
compressionLevel: 9,
|
|
277
|
-
adaptiveFiltering: true,
|
|
278
|
-
palette: true,
|
|
279
|
-
quality,
|
|
280
|
-
colors: 256,
|
|
281
|
-
dither: 1.0
|
|
282
|
-
});
|
|
283
|
-
break;
|
|
284
|
-
case 'webp':
|
|
285
|
-
image.webp({
|
|
286
|
-
quality,
|
|
287
|
-
smartSubsample: true,
|
|
288
|
-
effort: 6,
|
|
289
|
-
lossless: quality === 100
|
|
290
|
-
});
|
|
291
|
-
break;
|
|
292
|
-
case 'avif':
|
|
293
|
-
image.avif({
|
|
294
|
-
quality,
|
|
295
|
-
effort: 6,
|
|
296
|
-
chromaSubsampling: '4:4:4'
|
|
297
|
-
});
|
|
298
|
-
break;
|
|
299
|
-
case 'tiff':
|
|
300
|
-
image.tiff({
|
|
301
|
-
quality,
|
|
302
|
-
compression: 'lzw',
|
|
303
|
-
predictor: 'horizontal',
|
|
304
|
-
pyramid: true
|
|
305
|
-
});
|
|
306
|
-
break;
|
|
307
|
-
case 'gif':
|
|
308
|
-
image.gif({
|
|
309
|
-
colours: 256,
|
|
310
|
-
effort: 10,
|
|
311
|
-
dither: 1
|
|
312
|
-
});
|
|
313
|
-
break;
|
|
314
|
-
case 'jp2':
|
|
315
|
-
image.jp2({
|
|
316
|
-
quality,
|
|
317
|
-
lossless: quality === 100
|
|
318
|
-
});
|
|
319
|
-
break;
|
|
320
|
-
default:
|
|
321
|
-
// Fallback to WebP for best compression/quality ratio
|
|
322
|
-
targetFormat = 'webp';
|
|
323
|
-
image.webp({
|
|
324
|
-
quality,
|
|
325
|
-
smartSubsample: true,
|
|
326
|
-
effort: 6
|
|
327
|
-
});
|
|
328
|
-
break;
|
|
329
|
-
}
|
|
250
|
+
let targetFormat = (format !== 'original' ? format : mimetype.split('/')[1]).toLowerCase();
|
|
330
251
|
try {
|
|
252
|
+
// Read every frame only when the output can hold them - a multi-page JPEG/PNG would be one tall strip
|
|
253
|
+
const image = sharp(buffer, {
|
|
254
|
+
failOnError: false,
|
|
255
|
+
density: 300,
|
|
256
|
+
animated: MULTI_FRAME_FORMATS.includes(targetFormat)
|
|
257
|
+
});
|
|
258
|
+
// Apply initial resize
|
|
259
|
+
image.resize({
|
|
260
|
+
width: maxWidth,
|
|
261
|
+
height: maxHeight,
|
|
262
|
+
fit: 'inside',
|
|
263
|
+
withoutEnlargement: true
|
|
264
|
+
});
|
|
265
|
+
// Format-specific optimizations
|
|
266
|
+
switch(targetFormat){
|
|
267
|
+
case 'jpeg':
|
|
268
|
+
case 'jpg':
|
|
269
|
+
image.jpeg({
|
|
270
|
+
quality,
|
|
271
|
+
mozjpeg: true,
|
|
272
|
+
chromaSubsampling: '4:4:4'
|
|
273
|
+
});
|
|
274
|
+
break;
|
|
275
|
+
case 'png':
|
|
276
|
+
image.png({
|
|
277
|
+
compressionLevel: 9,
|
|
278
|
+
adaptiveFiltering: true,
|
|
279
|
+
palette: true,
|
|
280
|
+
quality,
|
|
281
|
+
colors: 256,
|
|
282
|
+
dither: 1.0
|
|
283
|
+
});
|
|
284
|
+
break;
|
|
285
|
+
case 'webp':
|
|
286
|
+
image.webp({
|
|
287
|
+
quality,
|
|
288
|
+
smartSubsample: true,
|
|
289
|
+
effort: 6,
|
|
290
|
+
lossless: quality === 100
|
|
291
|
+
});
|
|
292
|
+
break;
|
|
293
|
+
case 'avif':
|
|
294
|
+
image.avif({
|
|
295
|
+
quality,
|
|
296
|
+
effort: 6,
|
|
297
|
+
chromaSubsampling: '4:4:4'
|
|
298
|
+
});
|
|
299
|
+
break;
|
|
300
|
+
case 'tiff':
|
|
301
|
+
image.tiff({
|
|
302
|
+
quality,
|
|
303
|
+
compression: 'lzw',
|
|
304
|
+
predictor: 'horizontal',
|
|
305
|
+
pyramid: true
|
|
306
|
+
});
|
|
307
|
+
break;
|
|
308
|
+
case 'gif':
|
|
309
|
+
image.gif({
|
|
310
|
+
colours: 256,
|
|
311
|
+
effort: 10,
|
|
312
|
+
dither: 1
|
|
313
|
+
});
|
|
314
|
+
break;
|
|
315
|
+
case 'jp2':
|
|
316
|
+
image.jp2({
|
|
317
|
+
quality,
|
|
318
|
+
lossless: quality === 100
|
|
319
|
+
});
|
|
320
|
+
break;
|
|
321
|
+
default:
|
|
322
|
+
// Fallback to WebP for best compression/quality ratio
|
|
323
|
+
targetFormat = 'webp';
|
|
324
|
+
image.webp({
|
|
325
|
+
quality,
|
|
326
|
+
smartSubsample: true,
|
|
327
|
+
effort: 6
|
|
328
|
+
});
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
331
|
const compressedBuffer = await image.toBuffer();
|
|
332
332
|
return {
|
|
333
333
|
buffer: compressedBuffer,
|
|
@@ -343,12 +343,6 @@ const sharp = __rspack_external_sharp["default"] || __rspack_external_sharp;
|
|
|
343
343
|
}
|
|
344
344
|
|
|
345
345
|
|
|
346
|
-
},
|
|
347
|
-
521(module) {
|
|
348
|
-
|
|
349
|
-
module.exports = __rspack_external_path;
|
|
350
|
-
|
|
351
|
-
|
|
352
346
|
},
|
|
353
347
|
37(module) {
|
|
354
348
|
|
|
@@ -364,7 +358,7 @@ import * as __rspack_chunk_1 from '../493.js';
|
|
|
364
358
|
__webpack_require__.C(__rspack_chunk_1);
|
|
365
359
|
import * as __rspack_chunk_2 from './index.js';
|
|
366
360
|
__webpack_require__.C(__rspack_chunk_2);
|
|
367
|
-
var __webpack_exports__ = __webpack_exec__(
|
|
361
|
+
var __webpack_exports__ = __webpack_exec__(1922);var __webpack_exports__LocalProvider = __webpack_exports__.TZ;
|
|
368
362
|
var __webpack_exports__StorageFactoryService = __webpack_exports__.Lk;
|
|
369
363
|
var __webpack_exports__StorageProviderRegistry = __webpack_exports__.CG;
|
|
370
364
|
export { __webpack_exports__LocalProvider as LocalProvider, __webpack_exports__StorageFactoryService as StorageFactoryService, __webpack_exports__StorageProviderRegistry as StorageProviderRegistry };
|
package/fesm/services/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import * as __rspack_chunk_1 from '../493.js';
|
|
|
7
7
|
__webpack_require__.C(__rspack_chunk_1);
|
|
8
8
|
import * as __rspack_chunk_2 from './index.js';
|
|
9
9
|
__webpack_require__.C(__rspack_chunk_2);
|
|
10
|
-
var __webpack_exports__ = __webpack_exec__(
|
|
10
|
+
var __webpack_exports__ = __webpack_exec__(4148);var __webpack_exports__FileManagerService = __webpack_exports__.DL;
|
|
11
11
|
var __webpack_exports__FolderService = __webpack_exports__.kh;
|
|
12
12
|
var __webpack_exports__StorageConfigService = __webpack_exports__.tP;
|
|
13
13
|
var __webpack_exports__StorageDataSourceProvider = __webpack_exports__.M0;
|
package/fesm/utils/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import * as __rspack_external_sharp from "sharp";
|
|
|
2
2
|
export const __rspack_esm_id = 152;
|
|
3
3
|
export const __rspack_esm_ids = [152];
|
|
4
4
|
export const __webpack_modules__ = {
|
|
5
|
-
|
|
5
|
+
1872(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
6
6
|
|
|
7
7
|
// EXPORTS
|
|
8
8
|
__webpack_require__.d(__webpack_exports__, {
|
|
@@ -15,6 +15,10 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
15
15
|
|
|
16
16
|
// Handle ESM/CJS interop - sharp exports differently in each module system
|
|
17
17
|
const sharp = __rspack_external_sharp["default"] || __rspack_external_sharp;
|
|
18
|
+
const MULTI_FRAME_FORMATS = [
|
|
19
|
+
'gif',
|
|
20
|
+
'webp'
|
|
21
|
+
];
|
|
18
22
|
/**
|
|
19
23
|
* Image Compression Utility
|
|
20
24
|
* Handles image compression and format conversion using sharp
|
|
@@ -32,87 +36,87 @@ const sharp = __rspack_external_sharp["default"] || __rspack_external_sharp;
|
|
|
32
36
|
format: mimetype
|
|
33
37
|
};
|
|
34
38
|
}
|
|
35
|
-
|
|
36
|
-
const image = sharp(buffer, {
|
|
37
|
-
failOnError: false,
|
|
38
|
-
density: 300
|
|
39
|
-
});
|
|
40
|
-
// Apply initial resize
|
|
41
|
-
image.resize({
|
|
42
|
-
width: maxWidth,
|
|
43
|
-
height: maxHeight,
|
|
44
|
-
fit: 'inside',
|
|
45
|
-
withoutEnlargement: true
|
|
46
|
-
});
|
|
47
|
-
// Determine target format
|
|
48
|
-
let targetFormat = format !== 'original' ? format : mimetype.split('/')[1];
|
|
49
|
-
// Format-specific optimizations
|
|
50
|
-
switch(targetFormat.toLowerCase()){
|
|
51
|
-
case 'jpeg':
|
|
52
|
-
case 'jpg':
|
|
53
|
-
image.jpeg({
|
|
54
|
-
quality,
|
|
55
|
-
mozjpeg: true,
|
|
56
|
-
chromaSubsampling: '4:4:4'
|
|
57
|
-
});
|
|
58
|
-
break;
|
|
59
|
-
case 'png':
|
|
60
|
-
image.png({
|
|
61
|
-
compressionLevel: 9,
|
|
62
|
-
adaptiveFiltering: true,
|
|
63
|
-
palette: true,
|
|
64
|
-
quality,
|
|
65
|
-
colors: 256,
|
|
66
|
-
dither: 1.0
|
|
67
|
-
});
|
|
68
|
-
break;
|
|
69
|
-
case 'webp':
|
|
70
|
-
image.webp({
|
|
71
|
-
quality,
|
|
72
|
-
smartSubsample: true,
|
|
73
|
-
effort: 6,
|
|
74
|
-
lossless: quality === 100
|
|
75
|
-
});
|
|
76
|
-
break;
|
|
77
|
-
case 'avif':
|
|
78
|
-
image.avif({
|
|
79
|
-
quality,
|
|
80
|
-
effort: 6,
|
|
81
|
-
chromaSubsampling: '4:4:4'
|
|
82
|
-
});
|
|
83
|
-
break;
|
|
84
|
-
case 'tiff':
|
|
85
|
-
image.tiff({
|
|
86
|
-
quality,
|
|
87
|
-
compression: 'lzw',
|
|
88
|
-
predictor: 'horizontal',
|
|
89
|
-
pyramid: true
|
|
90
|
-
});
|
|
91
|
-
break;
|
|
92
|
-
case 'gif':
|
|
93
|
-
image.gif({
|
|
94
|
-
colours: 256,
|
|
95
|
-
effort: 10,
|
|
96
|
-
dither: 1
|
|
97
|
-
});
|
|
98
|
-
break;
|
|
99
|
-
case 'jp2':
|
|
100
|
-
image.jp2({
|
|
101
|
-
quality,
|
|
102
|
-
lossless: quality === 100
|
|
103
|
-
});
|
|
104
|
-
break;
|
|
105
|
-
default:
|
|
106
|
-
// Fallback to WebP for best compression/quality ratio
|
|
107
|
-
targetFormat = 'webp';
|
|
108
|
-
image.webp({
|
|
109
|
-
quality,
|
|
110
|
-
smartSubsample: true,
|
|
111
|
-
effort: 6
|
|
112
|
-
});
|
|
113
|
-
break;
|
|
114
|
-
}
|
|
39
|
+
let targetFormat = (format !== 'original' ? format : mimetype.split('/')[1]).toLowerCase();
|
|
115
40
|
try {
|
|
41
|
+
// Read every frame only when the output can hold them - a multi-page JPEG/PNG would be one tall strip
|
|
42
|
+
const image = sharp(buffer, {
|
|
43
|
+
failOnError: false,
|
|
44
|
+
density: 300,
|
|
45
|
+
animated: MULTI_FRAME_FORMATS.includes(targetFormat)
|
|
46
|
+
});
|
|
47
|
+
// Apply initial resize
|
|
48
|
+
image.resize({
|
|
49
|
+
width: maxWidth,
|
|
50
|
+
height: maxHeight,
|
|
51
|
+
fit: 'inside',
|
|
52
|
+
withoutEnlargement: true
|
|
53
|
+
});
|
|
54
|
+
// Format-specific optimizations
|
|
55
|
+
switch(targetFormat){
|
|
56
|
+
case 'jpeg':
|
|
57
|
+
case 'jpg':
|
|
58
|
+
image.jpeg({
|
|
59
|
+
quality,
|
|
60
|
+
mozjpeg: true,
|
|
61
|
+
chromaSubsampling: '4:4:4'
|
|
62
|
+
});
|
|
63
|
+
break;
|
|
64
|
+
case 'png':
|
|
65
|
+
image.png({
|
|
66
|
+
compressionLevel: 9,
|
|
67
|
+
adaptiveFiltering: true,
|
|
68
|
+
palette: true,
|
|
69
|
+
quality,
|
|
70
|
+
colors: 256,
|
|
71
|
+
dither: 1.0
|
|
72
|
+
});
|
|
73
|
+
break;
|
|
74
|
+
case 'webp':
|
|
75
|
+
image.webp({
|
|
76
|
+
quality,
|
|
77
|
+
smartSubsample: true,
|
|
78
|
+
effort: 6,
|
|
79
|
+
lossless: quality === 100
|
|
80
|
+
});
|
|
81
|
+
break;
|
|
82
|
+
case 'avif':
|
|
83
|
+
image.avif({
|
|
84
|
+
quality,
|
|
85
|
+
effort: 6,
|
|
86
|
+
chromaSubsampling: '4:4:4'
|
|
87
|
+
});
|
|
88
|
+
break;
|
|
89
|
+
case 'tiff':
|
|
90
|
+
image.tiff({
|
|
91
|
+
quality,
|
|
92
|
+
compression: 'lzw',
|
|
93
|
+
predictor: 'horizontal',
|
|
94
|
+
pyramid: true
|
|
95
|
+
});
|
|
96
|
+
break;
|
|
97
|
+
case 'gif':
|
|
98
|
+
image.gif({
|
|
99
|
+
colours: 256,
|
|
100
|
+
effort: 10,
|
|
101
|
+
dither: 1
|
|
102
|
+
});
|
|
103
|
+
break;
|
|
104
|
+
case 'jp2':
|
|
105
|
+
image.jp2({
|
|
106
|
+
quality,
|
|
107
|
+
lossless: quality === 100
|
|
108
|
+
});
|
|
109
|
+
break;
|
|
110
|
+
default:
|
|
111
|
+
// Fallback to WebP for best compression/quality ratio
|
|
112
|
+
targetFormat = 'webp';
|
|
113
|
+
image.webp({
|
|
114
|
+
quality,
|
|
115
|
+
smartSubsample: true,
|
|
116
|
+
effort: 6
|
|
117
|
+
});
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
116
120
|
const compressedBuffer = await image.toBuffer();
|
|
117
121
|
return {
|
|
118
122
|
buffer: compressedBuffer,
|
|
@@ -129,8 +133,8 @@ const sharp = __rspack_external_sharp["default"] || __rspack_external_sharp;
|
|
|
129
133
|
|
|
130
134
|
|
|
131
135
|
},
|
|
132
|
-
|
|
133
|
-
/* import */ var _image_compressor_util_js__rspack_import_0 = __webpack_require__(
|
|
136
|
+
8857(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
137
|
+
/* import */ var _image_compressor_util_js__rspack_import_0 = __webpack_require__(1872);
|
|
134
138
|
|
|
135
139
|
|
|
136
140
|
__webpack_require__.d(__webpack_exports__, {
|
|
@@ -145,5 +149,5 @@ import { __webpack_require__ } from '../runtime.js';
|
|
|
145
149
|
var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId); }
|
|
146
150
|
import * as __rspack_chunk_1 from './index.js';
|
|
147
151
|
__webpack_require__.C(__rspack_chunk_1);
|
|
148
|
-
var __webpack_exports__ = __webpack_exec__(
|
|
152
|
+
var __webpack_exports__ = __webpack_exec__(8857);var __webpack_exports__ImageCompressor = __webpack_exports__.J;
|
|
149
153
|
export { __webpack_exports__ImageCompressor as ImageCompressor };
|
|
@@ -9,7 +9,9 @@ export declare class FileServeMiddleware implements NestMiddleware {
|
|
|
9
9
|
private serveFile;
|
|
10
10
|
private extractFilePath;
|
|
11
11
|
private resolveFilePath;
|
|
12
|
-
private
|
|
12
|
+
private findInStorageRoot;
|
|
13
|
+
private isWithin;
|
|
14
|
+
private fallbackPaths;
|
|
13
15
|
private getMimeType;
|
|
14
16
|
private setResponseHeaders;
|
|
15
17
|
private streamFile;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flusys/nestjs-storage",
|
|
3
|
-
"version": "9.1
|
|
3
|
+
"version": "9.2.1",
|
|
4
4
|
"description": "Modular storage package with optional AWS S3, Azure Blob, and SFTP providers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./fesm/index.js",
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
}
|
|
119
119
|
},
|
|
120
120
|
"dependencies": {
|
|
121
|
-
"@flusys/nestjs-core": "9.1
|
|
122
|
-
"@flusys/nestjs-shared": "9.1
|
|
121
|
+
"@flusys/nestjs-core": "9.2.1",
|
|
122
|
+
"@flusys/nestjs-shared": "9.2.1"
|
|
123
123
|
}
|
|
124
124
|
}
|
|
@@ -4,7 +4,9 @@ export declare class LocalProvider implements IStorageProvider {
|
|
|
4
4
|
private basePath;
|
|
5
5
|
private baseUrl;
|
|
6
6
|
private relativeBasePath;
|
|
7
|
+
private isWithinBase;
|
|
7
8
|
private validatePathWithinBase;
|
|
9
|
+
private throwTraversal;
|
|
8
10
|
private validateKeyFormat;
|
|
9
11
|
initialize(config: {
|
|
10
12
|
basePath?: string;
|
|
@@ -17,6 +17,7 @@ export declare class SftpProvider implements IStorageProvider {
|
|
|
17
17
|
uploadFile(file: Express.Multer.File, options: UploadOptionsDto): Promise<IUploadedFileInfo>;
|
|
18
18
|
uploadMultipleFiles(files: Express.Multer.File[], options: UploadOptionsDto): Promise<IUploadedFileInfo[]>;
|
|
19
19
|
deleteFile(key: string): Promise<void>;
|
|
20
|
+
private resolveWithinBasePath;
|
|
20
21
|
deleteMultipleFiles(keys: string[]): Promise<void>;
|
|
21
22
|
generatePresignedUrl(key: string, _expiresInSeconds?: number): Promise<string>;
|
|
22
23
|
healthCheck(): Promise<boolean>;
|
|
@@ -7,9 +7,11 @@ export declare class StorageFactoryService implements OnModuleDestroy {
|
|
|
7
7
|
constructor(configService: StorageConfigService);
|
|
8
8
|
createProvider(config: IStorageProviderConfig): Promise<IStorageProvider>;
|
|
9
9
|
getProvider(providerName: string): Promise<IStorageProvider>;
|
|
10
|
-
|
|
11
|
-
clearCache(): void;
|
|
10
|
+
clearCache(): Promise<void>;
|
|
12
11
|
onModuleDestroy(): Promise<void>;
|
|
12
|
+
private instantiate;
|
|
13
|
+
private evict;
|
|
14
|
+
private closeEntry;
|
|
13
15
|
private generateCacheKey;
|
|
14
16
|
private initializeProvider;
|
|
15
17
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ApiService, HybridCache } from '@flusys/nestjs-shared/classes';
|
|
2
|
-
import { DeleteDto
|
|
2
|
+
import { DeleteDto } from '@flusys/nestjs-shared/dtos';
|
|
3
3
|
import { type ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
|
|
4
4
|
import { SharedPermissionCacheService, UtilsService } from '@flusys/nestjs-shared/modules';
|
|
5
5
|
import { type QueryRunner, SelectQueryBuilder } from 'typeorm';
|
|
@@ -17,8 +17,9 @@ export declare class FileManagerService extends ApiService<CreateFileManagerDto,
|
|
|
17
17
|
private readonly dataSourceProvider;
|
|
18
18
|
private readonly permissionCacheService;
|
|
19
19
|
constructor(cacheManager: HybridCache, utilsService: UtilsService, uploadService: UploadService, storageConfig: StorageConfigService, dataSourceProvider: StorageDataSourceProvider, permissionCacheService: SharedPermissionCacheService);
|
|
20
|
+
protected cacheDependencies(): string[];
|
|
20
21
|
convertSingleDtoToEntity(dto: CreateFileManagerDto | UpdateFileManagerDto, user: ILoggedUserInfo | null): Promise<FileManagerBase>;
|
|
21
|
-
getSelectQuery(query: SelectQueryBuilder<FileManagerBase>,
|
|
22
|
+
getSelectQuery(query: SelectQueryBuilder<FileManagerBase>, user: ILoggedUserInfo | null, select?: string[]): Promise<{
|
|
22
23
|
query: SelectQueryBuilder<FileManager>;
|
|
23
24
|
isRaw: boolean;
|
|
24
25
|
}>;
|
|
@@ -26,13 +27,11 @@ export declare class FileManagerService extends ApiService<CreateFileManagerDto,
|
|
|
26
27
|
query: SelectQueryBuilder<FileManagerBase>;
|
|
27
28
|
isRaw: boolean;
|
|
28
29
|
}>;
|
|
29
|
-
protected
|
|
30
|
-
query: SelectQueryBuilder<FileManagerBase>;
|
|
31
|
-
isRaw: boolean;
|
|
32
|
-
}>;
|
|
30
|
+
protected beforeUpdateOperation(dto: UpdateFileManagerDto | UpdateFileManagerDto[], user: ILoggedUserInfo | null): Promise<void>;
|
|
33
31
|
beforeDeleteOperation(dto: DeleteDto, user: ILoggedUserInfo | null, _qr: QueryRunner): Promise<void>;
|
|
34
32
|
getFiles(dtos: GetFilesRequestDto[], protocol: string, host: string, user?: ILoggedUserInfo): Promise<FilesResponseDto[]>;
|
|
35
33
|
private buildWhereWithCompany;
|
|
34
|
+
private assertInUserCompany;
|
|
36
35
|
private getStorageConfigRepository;
|
|
37
36
|
private getStorageConfigBasePath;
|
|
38
37
|
private validateFolder;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { HybridCache, ApiService } from '@flusys/nestjs-shared/classes';
|
|
2
|
+
import { DeleteDto } from '@flusys/nestjs-shared/dtos';
|
|
2
3
|
import { type ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
|
|
3
4
|
import { UtilsService } from '@flusys/nestjs-shared/modules';
|
|
4
5
|
import { SelectQueryBuilder } from 'typeorm';
|
|
@@ -13,12 +14,11 @@ export declare class FolderService extends ApiService<CreateFolderDto, UpdateFol
|
|
|
13
14
|
private readonly storageConfig;
|
|
14
15
|
constructor(cacheManager: HybridCache, utilsService: UtilsService, storageConfig: StorageConfigService, dataSourceProvider: StorageDataSourceProvider);
|
|
15
16
|
convertSingleDtoToEntity(dto: CreateFolderDto | UpdateFolderDto, user: ILoggedUserInfo | null): Promise<FolderBase>;
|
|
16
|
-
getSelectQuery(query: SelectQueryBuilder<FolderBase>,
|
|
17
|
-
query: SelectQueryBuilder<Folder>;
|
|
18
|
-
isRaw: boolean;
|
|
19
|
-
}>;
|
|
20
|
-
protected getExtraManipulateQuery(query: SelectQueryBuilder<FolderBase>, filterDto: any, user: ILoggedUserInfo | null): Promise<{
|
|
17
|
+
getSelectQuery(query: SelectQueryBuilder<FolderBase>, user: ILoggedUserInfo | null, select?: string[]): Promise<{
|
|
21
18
|
query: SelectQueryBuilder<Folder>;
|
|
22
19
|
isRaw: boolean;
|
|
23
20
|
}>;
|
|
21
|
+
protected beforeUpdateOperation(dto: UpdateFolderDto | UpdateFolderDto[], user: ILoggedUserInfo | null): Promise<void>;
|
|
22
|
+
protected beforeDeleteOperation(dto: DeleteDto, user: ILoggedUserInfo | null): Promise<void>;
|
|
23
|
+
private assertInUserCompany;
|
|
24
24
|
}
|