@flusys/nestjs-storage 7.0.2 → 8.0.0-beta.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/config/index.d.ts +3 -3
- package/controllers/file-manager.controller.d.ts +4 -4
- package/controllers/folder.controller.d.ts +2 -2
- package/controllers/index.d.ts +4 -4
- package/controllers/storage-config.controller.d.ts +2 -2
- package/controllers/upload.controller.d.ts +3 -3
- package/docs/index.d.ts +1 -1
- package/docs/storage-swagger.config.d.ts +2 -2
- package/dtos/index.d.ts +4 -4
- package/entities/file-manager-with-company.entity.d.ts +2 -2
- package/entities/file-manager.entity.d.ts +1 -1
- package/entities/folder-with-company.entity.d.ts +1 -1
- package/entities/index.d.ts +15 -15
- package/entities/storage-config-with-company.entity.d.ts +1 -1
- package/enums/index.d.ts +1 -1
- package/fesm/493.js +2768 -0
- package/fesm/537.js +1157 -0
- package/fesm/562.js +786 -0
- package/fesm/{providers/azure-provider.optional.js → chunks/190.js} +33 -30
- package/fesm/chunks/252.js +12 -0
- package/fesm/chunks/327.js +12 -0
- package/fesm/chunks/385.js +12 -0
- package/fesm/{providers/s3-provider.optional.js → chunks/591.js} +37 -34
- package/fesm/chunks/746.js +12 -0
- package/fesm/chunks/798.js +12 -0
- package/fesm/{providers/sftp-provider.optional.js → chunks/918.js} +35 -31
- package/fesm/config/index.js +119 -3
- package/fesm/controllers/index.js +16 -4
- package/fesm/docs/index.js +206 -1
- package/fesm/dtos/index.js +879 -4
- package/fesm/entities/index.js +463 -18
- package/fesm/enums/index.js +36 -1
- package/fesm/index.js +398 -12
- package/fesm/interfaces/index.js +19 -5
- package/fesm/modules/index.js +16 -1
- package/fesm/providers/index.js +364 -3
- package/fesm/runtime.js +252 -0
- package/fesm/services/index.js +16 -6
- package/fesm/utils/index.js +149 -1
- package/index.d.ts +11 -11
- package/interfaces/file-manager.interface.d.ts +1 -1
- package/interfaces/folder.interface.d.ts +1 -1
- package/interfaces/index.d.ts +5 -5
- package/interfaces/storage-config.interface.d.ts +1 -1
- package/interfaces/storage-module-options.interface.d.ts +3 -3
- package/interfaces/storage-provider.interface.d.ts +1 -1
- package/middlewares/file-serve.middleware.d.ts +4 -3
- package/middlewares/index.d.ts +1 -1
- package/modules/index.d.ts +1 -1
- package/modules/storage.module.d.ts +2 -2
- package/package.json +30 -41
- package/providers/azure-provider.optional.d.ts +2 -2
- package/providers/index.d.ts +3 -3
- package/providers/local-provider.d.ts +2 -2
- package/providers/s3-provider.optional.d.ts +2 -2
- package/providers/sftp-provider.optional.d.ts +2 -2
- package/providers/storage-factory.service.d.ts +3 -3
- package/providers/storage-provider.registry.d.ts +2 -2
- package/services/file-manager.service.d.ts +9 -8
- package/services/folder.service.d.ts +6 -6
- package/services/index.d.ts +6 -6
- package/services/storage-config.service.d.ts +3 -3
- package/services/storage-datasource.provider.d.ts +3 -3
- package/services/storage-provider-config.service.d.ts +7 -7
- package/services/upload.service.d.ts +6 -6
- package/utils/image-compressor.util.d.ts +1 -1
- package/utils/index.d.ts +1 -1
- package/cjs/config/event-actions.js +0 -34
- package/cjs/config/index.js +0 -20
- package/cjs/config/message-keys.js +0 -46
- package/cjs/config/storage.constants.js +0 -28
- package/cjs/controllers/file-manager.controller.js +0 -152
- package/cjs/controllers/file-manager.controller.spec.js +0 -135
- package/cjs/controllers/folder.controller.js +0 -102
- package/cjs/controllers/folder.controller.spec.js +0 -86
- package/cjs/controllers/index.js +0 -21
- package/cjs/controllers/storage-config.controller.js +0 -102
- package/cjs/controllers/storage-config.controller.spec.js +0 -85
- package/cjs/controllers/upload.controller.js +0 -234
- package/cjs/controllers/upload.controller.spec.js +0 -132
- package/cjs/docs/index.js +0 -11
- package/cjs/docs/storage-swagger.config.js +0 -188
- package/cjs/docs/storage-swagger.config.spec.js +0 -72
- package/cjs/dtos/file-manager.dto.js +0 -327
- package/cjs/dtos/folder.dto.js +0 -105
- package/cjs/dtos/index.js +0 -21
- package/cjs/dtos/storage-config.dto.js +0 -170
- package/cjs/dtos/upload.dto.js +0 -272
- package/cjs/entities/file-manager-with-company.entity.js +0 -61
- package/cjs/entities/file-manager.entity.js +0 -122
- package/cjs/entities/folder-with-company.entity.js +0 -55
- package/cjs/entities/folder.entity.js +0 -64
- package/cjs/entities/index.js +0 -63
- package/cjs/entities/index.spec.js +0 -18
- package/cjs/entities/storage-config-with-company.entity.js +0 -51
- package/cjs/entities/storage-config.entity.js +0 -96
- package/cjs/enums/file-location.enum.js +0 -17
- package/cjs/enums/index.js +0 -18
- package/cjs/index.js +0 -29
- package/cjs/interfaces/file-manager.interface.js +0 -4
- package/cjs/interfaces/folder.interface.js +0 -4
- package/cjs/interfaces/index.js +0 -22
- package/cjs/interfaces/storage-config.interface.js +0 -4
- package/cjs/interfaces/storage-module-options.interface.js +0 -4
- package/cjs/interfaces/storage-provider.interface.js +0 -4
- package/cjs/middlewares/file-serve.middleware.js +0 -241
- package/cjs/middlewares/file-serve.middleware.spec.js +0 -211
- package/cjs/middlewares/index.js +0 -18
- package/cjs/modules/index.js +0 -18
- package/cjs/modules/storage.module.js +0 -165
- package/cjs/modules/storage.module.spec.js +0 -184
- package/cjs/providers/azure-provider.optional.js +0 -155
- package/cjs/providers/azure-provider.optional.spec.js +0 -228
- package/cjs/providers/index.js +0 -26
- package/cjs/providers/local-provider.js +0 -230
- package/cjs/providers/local-provider.spec.js +0 -239
- package/cjs/providers/s3-provider.optional.js +0 -175
- package/cjs/providers/s3-provider.optional.spec.js +0 -243
- package/cjs/providers/sftp-provider.optional.js +0 -203
- package/cjs/providers/sftp-provider.optional.spec.js +0 -217
- package/cjs/providers/storage-factory.service.js +0 -175
- package/cjs/providers/storage-factory.service.spec.js +0 -253
- package/cjs/providers/storage-provider.registry.js +0 -41
- package/cjs/providers/storage-provider.registry.spec.js +0 -67
- package/cjs/services/file-manager.service.js +0 -387
- package/cjs/services/file-manager.service.spec.js +0 -580
- package/cjs/services/folder.service.js +0 -101
- package/cjs/services/folder.service.spec.js +0 -167
- package/cjs/services/index.js +0 -23
- package/cjs/services/storage-config.service.js +0 -129
- package/cjs/services/storage-config.service.spec.js +0 -257
- package/cjs/services/storage-datasource.provider.js +0 -137
- package/cjs/services/storage-datasource.provider.spec.js +0 -217
- package/cjs/services/storage-provider-config.service.js +0 -171
- package/cjs/services/storage-provider-config.service.spec.js +0 -265
- package/cjs/services/upload.service.js +0 -376
- package/cjs/services/upload.service.spec.js +0 -426
- package/cjs/utils/file-validator.util.js +0 -440
- package/cjs/utils/file-validator.util.spec.js +0 -259
- package/cjs/utils/image-compressor.util.js +0 -162
- package/cjs/utils/image-compressor.util.spec.js +0 -166
- package/cjs/utils/index.js +0 -18
- package/fesm/config/event-actions.js +0 -13
- package/fesm/config/message-keys.js +0 -22
- package/fesm/config/storage.constants.js +0 -7
- package/fesm/controllers/file-manager.controller.js +0 -142
- package/fesm/controllers/file-manager.controller.spec.js +0 -131
- package/fesm/controllers/folder.controller.js +0 -92
- package/fesm/controllers/folder.controller.spec.js +0 -82
- package/fesm/controllers/storage-config.controller.js +0 -92
- package/fesm/controllers/storage-config.controller.spec.js +0 -81
- package/fesm/controllers/upload.controller.js +0 -224
- package/fesm/controllers/upload.controller.spec.js +0 -128
- package/fesm/docs/storage-swagger.config.js +0 -181
- package/fesm/docs/storage-swagger.config.spec.js +0 -68
- package/fesm/dtos/file-manager.dto.js +0 -300
- package/fesm/dtos/folder.dto.js +0 -84
- package/fesm/dtos/storage-config.dto.js +0 -149
- package/fesm/dtos/upload.dto.js +0 -245
- package/fesm/entities/file-manager-with-company.entity.js +0 -51
- package/fesm/entities/file-manager.entity.js +0 -112
- package/fesm/entities/folder-with-company.entity.js +0 -45
- package/fesm/entities/folder.entity.js +0 -54
- package/fesm/entities/index.spec.js +0 -14
- package/fesm/entities/storage-config-with-company.entity.js +0 -41
- package/fesm/entities/storage-config.entity.js +0 -86
- package/fesm/enums/file-location.enum.js +0 -7
- package/fesm/interfaces/file-manager.interface.js +0 -1
- package/fesm/interfaces/folder.interface.js +0 -1
- package/fesm/interfaces/storage-config.interface.js +0 -1
- package/fesm/interfaces/storage-module-options.interface.js +0 -3
- package/fesm/interfaces/storage-provider.interface.js +0 -4
- package/fesm/middlewares/file-serve.middleware.js +0 -190
- package/fesm/middlewares/file-serve.middleware.spec.js +0 -207
- package/fesm/middlewares/index.js +0 -1
- package/fesm/modules/storage.module.js +0 -156
- package/fesm/modules/storage.module.spec.js +0 -180
- package/fesm/providers/azure-provider.optional.spec.js +0 -224
- package/fesm/providers/local-provider.js +0 -182
- package/fesm/providers/local-provider.spec.js +0 -194
- package/fesm/providers/s3-provider.optional.spec.js +0 -239
- package/fesm/providers/sftp-provider.optional.spec.js +0 -213
- package/fesm/providers/storage-factory.service.js +0 -124
- package/fesm/providers/storage-factory.service.spec.js +0 -249
- package/fesm/providers/storage-provider.registry.js +0 -34
- package/fesm/providers/storage-provider.registry.spec.js +0 -63
- package/fesm/services/file-manager.service.js +0 -336
- package/fesm/services/file-manager.service.spec.js +0 -576
- package/fesm/services/folder.service.js +0 -91
- package/fesm/services/folder.service.spec.js +0 -163
- package/fesm/services/storage-config.service.js +0 -119
- package/fesm/services/storage-config.service.spec.js +0 -253
- package/fesm/services/storage-datasource.provider.js +0 -86
- package/fesm/services/storage-datasource.provider.spec.js +0 -172
- package/fesm/services/storage-provider-config.service.js +0 -161
- package/fesm/services/storage-provider-config.service.spec.js +0 -261
- package/fesm/services/upload.service.js +0 -366
- package/fesm/services/upload.service.spec.js +0 -422
- package/fesm/utils/file-validator.util.js +0 -433
- package/fesm/utils/file-validator.util.spec.js +0 -255
- package/fesm/utils/image-compressor.util.js +0 -114
- package/fesm/utils/image-compressor.util.spec.js +0 -162
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
Object.defineProperty(exports, "ImageCompressor", {
|
|
6
|
-
enumerable: true,
|
|
7
|
-
get: function() {
|
|
8
|
-
return ImageCompressor;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
const _sharp = /*#__PURE__*/ _interop_require_wildcard(require("sharp"));
|
|
12
|
-
function _getRequireWildcardCache(nodeInterop) {
|
|
13
|
-
if (typeof WeakMap !== "function") return null;
|
|
14
|
-
var cacheBabelInterop = new WeakMap();
|
|
15
|
-
var cacheNodeInterop = new WeakMap();
|
|
16
|
-
return (_getRequireWildcardCache = function(nodeInterop) {
|
|
17
|
-
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
|
|
18
|
-
})(nodeInterop);
|
|
19
|
-
}
|
|
20
|
-
function _interop_require_wildcard(obj, nodeInterop) {
|
|
21
|
-
if (!nodeInterop && obj && obj.__esModule) {
|
|
22
|
-
return obj;
|
|
23
|
-
}
|
|
24
|
-
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
|
|
25
|
-
return {
|
|
26
|
-
default: obj
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
var cache = _getRequireWildcardCache(nodeInterop);
|
|
30
|
-
if (cache && cache.has(obj)) {
|
|
31
|
-
return cache.get(obj);
|
|
32
|
-
}
|
|
33
|
-
var newObj = {
|
|
34
|
-
__proto__: null
|
|
35
|
-
};
|
|
36
|
-
var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
|
|
37
|
-
for(var key in obj){
|
|
38
|
-
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
39
|
-
var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
|
|
40
|
-
if (desc && (desc.get || desc.set)) {
|
|
41
|
-
Object.defineProperty(newObj, key, desc);
|
|
42
|
-
} else {
|
|
43
|
-
newObj[key] = obj[key];
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
newObj.default = obj;
|
|
48
|
-
if (cache) {
|
|
49
|
-
cache.set(obj, newObj);
|
|
50
|
-
}
|
|
51
|
-
return newObj;
|
|
52
|
-
}
|
|
53
|
-
// Handle ESM/CJS interop - sharp exports differently in each module system
|
|
54
|
-
const sharp = _sharp.default || _sharp;
|
|
55
|
-
let ImageCompressor = class ImageCompressor {
|
|
56
|
-
/**
|
|
57
|
-
* Dynamically compresses an image based on its format and provided options.
|
|
58
|
-
* - Preserves original format if not overridden
|
|
59
|
-
* - Adjusts compression settings for each codec
|
|
60
|
-
*/ static async compress(buffer, mimetype, options = {}) {
|
|
61
|
-
const { maxWidth = 1280, maxHeight = 1280, quality = 85, format = 'original' } = options;
|
|
62
|
-
// Skip processing for SVG
|
|
63
|
-
if (mimetype === 'image/svg+xml') {
|
|
64
|
-
return {
|
|
65
|
-
buffer,
|
|
66
|
-
format: mimetype
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
// Initialize sharp with improved error handling
|
|
70
|
-
const image = sharp(buffer, {
|
|
71
|
-
failOnError: false,
|
|
72
|
-
density: 300
|
|
73
|
-
});
|
|
74
|
-
// Apply initial resize
|
|
75
|
-
image.resize({
|
|
76
|
-
width: maxWidth,
|
|
77
|
-
height: maxHeight,
|
|
78
|
-
fit: 'inside',
|
|
79
|
-
withoutEnlargement: true
|
|
80
|
-
});
|
|
81
|
-
// Determine target format
|
|
82
|
-
let targetFormat = format !== 'original' ? format : mimetype.split('/')[1];
|
|
83
|
-
// Format-specific optimizations
|
|
84
|
-
switch(targetFormat.toLowerCase()){
|
|
85
|
-
case 'jpeg':
|
|
86
|
-
case 'jpg':
|
|
87
|
-
image.jpeg({
|
|
88
|
-
quality,
|
|
89
|
-
mozjpeg: true,
|
|
90
|
-
chromaSubsampling: '4:4:4'
|
|
91
|
-
});
|
|
92
|
-
break;
|
|
93
|
-
case 'png':
|
|
94
|
-
image.png({
|
|
95
|
-
compressionLevel: 9,
|
|
96
|
-
adaptiveFiltering: true,
|
|
97
|
-
palette: true,
|
|
98
|
-
quality,
|
|
99
|
-
colors: 256,
|
|
100
|
-
dither: 1.0
|
|
101
|
-
});
|
|
102
|
-
break;
|
|
103
|
-
case 'webp':
|
|
104
|
-
image.webp({
|
|
105
|
-
quality,
|
|
106
|
-
smartSubsample: true,
|
|
107
|
-
effort: 6,
|
|
108
|
-
lossless: quality === 100
|
|
109
|
-
});
|
|
110
|
-
break;
|
|
111
|
-
case 'avif':
|
|
112
|
-
image.avif({
|
|
113
|
-
quality,
|
|
114
|
-
effort: 6,
|
|
115
|
-
chromaSubsampling: '4:4:4'
|
|
116
|
-
});
|
|
117
|
-
break;
|
|
118
|
-
case 'tiff':
|
|
119
|
-
image.tiff({
|
|
120
|
-
quality,
|
|
121
|
-
compression: 'lzw',
|
|
122
|
-
predictor: 'horizontal',
|
|
123
|
-
pyramid: true
|
|
124
|
-
});
|
|
125
|
-
break;
|
|
126
|
-
case 'gif':
|
|
127
|
-
image.gif({
|
|
128
|
-
colours: 256,
|
|
129
|
-
effort: 10,
|
|
130
|
-
dither: 1
|
|
131
|
-
});
|
|
132
|
-
break;
|
|
133
|
-
case 'jp2':
|
|
134
|
-
image.jp2({
|
|
135
|
-
quality,
|
|
136
|
-
lossless: quality === 100
|
|
137
|
-
});
|
|
138
|
-
break;
|
|
139
|
-
default:
|
|
140
|
-
// Fallback to WebP for best compression/quality ratio
|
|
141
|
-
targetFormat = 'webp';
|
|
142
|
-
image.webp({
|
|
143
|
-
quality,
|
|
144
|
-
smartSubsample: true,
|
|
145
|
-
effort: 6
|
|
146
|
-
});
|
|
147
|
-
break;
|
|
148
|
-
}
|
|
149
|
-
try {
|
|
150
|
-
const compressedBuffer = await image.toBuffer();
|
|
151
|
-
return {
|
|
152
|
-
buffer: compressedBuffer,
|
|
153
|
-
format: `image/${targetFormat}`
|
|
154
|
-
};
|
|
155
|
-
} catch {
|
|
156
|
-
return {
|
|
157
|
-
buffer,
|
|
158
|
-
format: mimetype
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
};
|
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
const _imagecompressorutil = require("./image-compressor.util");
|
|
6
|
-
const mockToBuffer = jest.fn();
|
|
7
|
-
const mockJpeg = jest.fn();
|
|
8
|
-
const mockPng = jest.fn();
|
|
9
|
-
const mockWebp = jest.fn();
|
|
10
|
-
const mockAvif = jest.fn();
|
|
11
|
-
const mockTiff = jest.fn();
|
|
12
|
-
const mockGif = jest.fn();
|
|
13
|
-
const mockJp2 = jest.fn();
|
|
14
|
-
const mockResize = jest.fn();
|
|
15
|
-
function buildChainableImage() {
|
|
16
|
-
const image = {};
|
|
17
|
-
image.resize = mockResize.mockReturnValue(image);
|
|
18
|
-
image.jpeg = mockJpeg.mockReturnValue(image);
|
|
19
|
-
image.png = mockPng.mockReturnValue(image);
|
|
20
|
-
image.webp = mockWebp.mockReturnValue(image);
|
|
21
|
-
image.avif = mockAvif.mockReturnValue(image);
|
|
22
|
-
image.tiff = mockTiff.mockReturnValue(image);
|
|
23
|
-
image.gif = mockGif.mockReturnValue(image);
|
|
24
|
-
image.jp2 = mockJp2.mockReturnValue(image);
|
|
25
|
-
image.toBuffer = mockToBuffer;
|
|
26
|
-
return image;
|
|
27
|
-
}
|
|
28
|
-
const mockSharpFactory = jest.fn();
|
|
29
|
-
jest.mock('sharp', ()=>{
|
|
30
|
-
const factory = (...args)=>mockSharpFactory(...args);
|
|
31
|
-
return {
|
|
32
|
-
__esModule: true,
|
|
33
|
-
default: factory
|
|
34
|
-
};
|
|
35
|
-
});
|
|
36
|
-
describe('ImageCompressor', ()=>{
|
|
37
|
-
beforeEach(()=>{
|
|
38
|
-
jest.clearAllMocks();
|
|
39
|
-
mockSharpFactory.mockImplementation(()=>buildChainableImage());
|
|
40
|
-
mockToBuffer.mockResolvedValue(Buffer.from('compressed-image-data'));
|
|
41
|
-
});
|
|
42
|
-
it('returns the buffer unchanged for image/svg+xml without invoking sharp', async ()=>{
|
|
43
|
-
const buffer = Buffer.from('<svg></svg>');
|
|
44
|
-
const result = await _imagecompressorutil.ImageCompressor.compress(buffer, 'image/svg+xml');
|
|
45
|
-
expect(result.buffer).toBe(buffer);
|
|
46
|
-
expect(result.format).toBe('image/svg+xml');
|
|
47
|
-
expect(mockSharpFactory).not.toHaveBeenCalled();
|
|
48
|
-
});
|
|
49
|
-
it('resizes using default maxWidth/maxHeight when no options are provided', async ()=>{
|
|
50
|
-
await _imagecompressorutil.ImageCompressor.compress(Buffer.from('jpeg-bytes'), 'image/jpeg');
|
|
51
|
-
expect(mockResize).toHaveBeenCalledWith(expect.objectContaining({
|
|
52
|
-
width: 1280,
|
|
53
|
-
height: 1280,
|
|
54
|
-
fit: 'inside',
|
|
55
|
-
withoutEnlargement: true
|
|
56
|
-
}));
|
|
57
|
-
});
|
|
58
|
-
it('resizes using custom maxWidth/maxHeight when provided', async ()=>{
|
|
59
|
-
await _imagecompressorutil.ImageCompressor.compress(Buffer.from('jpeg-bytes'), 'image/jpeg', {
|
|
60
|
-
maxWidth: 500,
|
|
61
|
-
maxHeight: 600
|
|
62
|
-
});
|
|
63
|
-
expect(mockResize).toHaveBeenCalledWith(expect.objectContaining({
|
|
64
|
-
width: 500,
|
|
65
|
-
height: 600
|
|
66
|
-
}));
|
|
67
|
-
});
|
|
68
|
-
it('preserves the original format (derived from mimetype) when format is "original"', async ()=>{
|
|
69
|
-
const result = await _imagecompressorutil.ImageCompressor.compress(Buffer.from('png-bytes'), 'image/png', {
|
|
70
|
-
format: 'original'
|
|
71
|
-
});
|
|
72
|
-
expect(mockPng).toHaveBeenCalled();
|
|
73
|
-
expect(result.format).toBe('image/png');
|
|
74
|
-
});
|
|
75
|
-
it('applies jpeg-specific compression options with quality', async ()=>{
|
|
76
|
-
await _imagecompressorutil.ImageCompressor.compress(Buffer.from('bytes'), 'image/jpeg', {
|
|
77
|
-
quality: 70
|
|
78
|
-
});
|
|
79
|
-
expect(mockJpeg).toHaveBeenCalledWith(expect.objectContaining({
|
|
80
|
-
quality: 70,
|
|
81
|
-
mozjpeg: true
|
|
82
|
-
}));
|
|
83
|
-
});
|
|
84
|
-
it('applies png-specific compression options', async ()=>{
|
|
85
|
-
await _imagecompressorutil.ImageCompressor.compress(Buffer.from('bytes'), 'image/png', {
|
|
86
|
-
quality: 90
|
|
87
|
-
});
|
|
88
|
-
expect(mockPng).toHaveBeenCalledWith(expect.objectContaining({
|
|
89
|
-
quality: 90,
|
|
90
|
-
colors: 256
|
|
91
|
-
}));
|
|
92
|
-
});
|
|
93
|
-
it('applies webp-specific options and sets lossless=true only when quality is 100', async ()=>{
|
|
94
|
-
await _imagecompressorutil.ImageCompressor.compress(Buffer.from('bytes'), 'image/webp', {
|
|
95
|
-
quality: 100,
|
|
96
|
-
format: 'webp'
|
|
97
|
-
});
|
|
98
|
-
expect(mockWebp).toHaveBeenCalledWith(expect.objectContaining({
|
|
99
|
-
quality: 100,
|
|
100
|
-
lossless: true
|
|
101
|
-
}));
|
|
102
|
-
});
|
|
103
|
-
it('applies webp-specific options with lossless=false for quality < 100', async ()=>{
|
|
104
|
-
await _imagecompressorutil.ImageCompressor.compress(Buffer.from('bytes'), 'image/webp', {
|
|
105
|
-
quality: 80,
|
|
106
|
-
format: 'webp'
|
|
107
|
-
});
|
|
108
|
-
expect(mockWebp).toHaveBeenCalledWith(expect.objectContaining({
|
|
109
|
-
quality: 80,
|
|
110
|
-
lossless: false
|
|
111
|
-
}));
|
|
112
|
-
});
|
|
113
|
-
it('applies avif-specific compression options', async ()=>{
|
|
114
|
-
await _imagecompressorutil.ImageCompressor.compress(Buffer.from('bytes'), 'image/avif', {
|
|
115
|
-
format: 'avif'
|
|
116
|
-
});
|
|
117
|
-
expect(mockAvif).toHaveBeenCalled();
|
|
118
|
-
});
|
|
119
|
-
it('applies tiff-specific compression options', async ()=>{
|
|
120
|
-
await _imagecompressorutil.ImageCompressor.compress(Buffer.from('bytes'), 'image/tiff', {
|
|
121
|
-
format: 'tiff'
|
|
122
|
-
});
|
|
123
|
-
expect(mockTiff).toHaveBeenCalled();
|
|
124
|
-
});
|
|
125
|
-
it('applies gif-specific compression options', async ()=>{
|
|
126
|
-
await _imagecompressorutil.ImageCompressor.compress(Buffer.from('bytes'), 'image/gif', {
|
|
127
|
-
format: 'gif'
|
|
128
|
-
});
|
|
129
|
-
expect(mockGif).toHaveBeenCalled();
|
|
130
|
-
});
|
|
131
|
-
it('applies jp2-specific compression options', async ()=>{
|
|
132
|
-
await _imagecompressorutil.ImageCompressor.compress(Buffer.from('bytes'), 'image/jp2', {
|
|
133
|
-
format: 'jp2'
|
|
134
|
-
});
|
|
135
|
-
expect(mockJp2).toHaveBeenCalled();
|
|
136
|
-
});
|
|
137
|
-
it('falls back to webp for an unrecognized target format', async ()=>{
|
|
138
|
-
const result = await _imagecompressorutil.ImageCompressor.compress(Buffer.from('bytes'), 'image/unknownformat', {
|
|
139
|
-
format: 'original'
|
|
140
|
-
});
|
|
141
|
-
expect(mockWebp).toHaveBeenCalled();
|
|
142
|
-
expect(result.format).toBe('image/webp');
|
|
143
|
-
});
|
|
144
|
-
it('returns the compressed buffer and content-type prefixed format on success', async ()=>{
|
|
145
|
-
mockToBuffer.mockResolvedValue(Buffer.from('final-bytes'));
|
|
146
|
-
const result = await _imagecompressorutil.ImageCompressor.compress(Buffer.from('bytes'), 'image/jpeg', {
|
|
147
|
-
format: 'jpeg'
|
|
148
|
-
});
|
|
149
|
-
expect(result.buffer).toEqual(Buffer.from('final-bytes'));
|
|
150
|
-
expect(result.format).toBe('image/jpeg');
|
|
151
|
-
});
|
|
152
|
-
it('falls back to the original buffer and mimetype when sharp processing throws', async ()=>{
|
|
153
|
-
const originalBuffer = Buffer.from('original-bytes');
|
|
154
|
-
mockToBuffer.mockRejectedValue(new Error('corrupt image'));
|
|
155
|
-
const result = await _imagecompressorutil.ImageCompressor.compress(originalBuffer, 'image/png');
|
|
156
|
-
expect(result.buffer).toBe(originalBuffer);
|
|
157
|
-
expect(result.format).toBe('image/png');
|
|
158
|
-
});
|
|
159
|
-
it('initializes sharp with failOnError=false and a density hint', async ()=>{
|
|
160
|
-
await _imagecompressorutil.ImageCompressor.compress(Buffer.from('bytes'), 'image/jpeg');
|
|
161
|
-
expect(mockSharpFactory).toHaveBeenCalledWith(expect.any(Buffer), expect.objectContaining({
|
|
162
|
-
failOnError: false,
|
|
163
|
-
density: 300
|
|
164
|
-
}));
|
|
165
|
-
});
|
|
166
|
-
});
|
package/cjs/utils/index.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
_export_star(require("./image-compressor.util"), exports);
|
|
6
|
-
function _export_star(from, to) {
|
|
7
|
-
Object.keys(from).forEach(function(k) {
|
|
8
|
-
if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
|
|
9
|
-
Object.defineProperty(to, k, {
|
|
10
|
-
enumerable: true,
|
|
11
|
-
get: function() {
|
|
12
|
-
return from[k];
|
|
13
|
-
}
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
});
|
|
17
|
-
return from;
|
|
18
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Domain event actions published by the storage module, alongside the CRUD
|
|
3
|
-
* lifecycle actions every ApiService emits.
|
|
4
|
-
*/ export const STORAGE_EVENT_ACTIONS = {
|
|
5
|
-
UPLOADED: 'uploaded',
|
|
6
|
-
REMOVED: 'removed'
|
|
7
|
-
};
|
|
8
|
-
/** Every entity the storage module publishes under, ApiService backed or not */ export const STORAGE_EVENT_ENTITIES = {
|
|
9
|
-
FILE: 'file_manager',
|
|
10
|
-
FOLDER: 'folder',
|
|
11
|
-
STORAGE_CONFIG: 'storageConfig'
|
|
12
|
-
};
|
|
13
|
-
export const STORAGE_EVENT_MODULE = 'storage';
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
// ==================== STORAGE MODULE MESSAGE KEYS ====================
|
|
2
|
-
export const FILE_MESSAGES = {
|
|
3
|
-
GET_ALL_SUCCESS: 'file.get.all.success',
|
|
4
|
-
DELETE_SUCCESS: 'file.delete.success',
|
|
5
|
-
NOT_FOUND: 'file.not.found'
|
|
6
|
-
};
|
|
7
|
-
export const STORAGE_CONFIG_MESSAGES = {
|
|
8
|
-
DEFAULT_DELETE_FORBIDDEN: 'storage.config.default.delete.forbidden'
|
|
9
|
-
};
|
|
10
|
-
export const FOLDER_MESSAGES = {
|
|
11
|
-
NOT_FOUND: 'folder.not.found'
|
|
12
|
-
};
|
|
13
|
-
export const UPLOAD_MESSAGES = {
|
|
14
|
-
SUCCESS: 'upload.success',
|
|
15
|
-
MANY_SUCCESS: 'upload.many.success',
|
|
16
|
-
FILE_TOO_LARGE: 'upload.file.too.large',
|
|
17
|
-
INVALID_TYPE: 'upload.invalid.type',
|
|
18
|
-
NO_FILES_PROVIDED: 'upload.no.files.provided',
|
|
19
|
-
NO_FILE_PATH: 'upload.no.file.path',
|
|
20
|
-
INVALID_FILE_PATH: 'upload.invalid.file.path',
|
|
21
|
-
CONFIG_NOT_FOUND: 'upload.config.not.found'
|
|
22
|
-
};
|
|
@@ -1,142 +0,0 @@
|
|
|
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
|
-
function _ts_decorate(decorators, target, key, desc) {
|
|
15
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
16
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
17
|
-
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
18
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
19
|
-
}
|
|
20
|
-
function _ts_metadata(k, v) {
|
|
21
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
22
|
-
}
|
|
23
|
-
function _ts_param(paramIndex, decorator) {
|
|
24
|
-
return function(target, key) {
|
|
25
|
-
decorator(target, key, paramIndex);
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
import { BadRequestException, Body, Controller, HttpCode, Inject, Post, Req, UseGuards } from '@nestjs/common';
|
|
29
|
-
import { createApiController, FILE_PERMISSIONS } from '@flusys/nestjs-shared/classes';
|
|
30
|
-
import { FILE_MESSAGES, UPLOAD_MESSAGES } from '../config';
|
|
31
|
-
import { CurrentUser } from '@flusys/nestjs-shared/decorators';
|
|
32
|
-
import { SingleResponseDto } from '@flusys/nestjs-shared/dtos';
|
|
33
|
-
import { JwtAuthGuard } from '@flusys/nestjs-shared/guards';
|
|
34
|
-
import { ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
|
|
35
|
-
import { CreateFileManagerDto, FileManagerResponseDto, GetFilesRequestDto, UpdateFileManagerDto } from '../dtos';
|
|
36
|
-
import { ApiBearerAuth, ApiBody, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
|
37
|
-
import { Request } from 'express';
|
|
38
|
-
import { FileManagerService } from '../services/file-manager.service';
|
|
39
|
-
export class FileManagerController extends createApiController(CreateFileManagerDto, UpdateFileManagerDto, FileManagerResponseDto, {
|
|
40
|
-
entityName: 'file',
|
|
41
|
-
security: {
|
|
42
|
-
insert: {
|
|
43
|
-
level: 'permission',
|
|
44
|
-
permissions: [
|
|
45
|
-
FILE_PERMISSIONS.CREATE
|
|
46
|
-
]
|
|
47
|
-
},
|
|
48
|
-
insertMany: {
|
|
49
|
-
level: 'permission',
|
|
50
|
-
permissions: [
|
|
51
|
-
FILE_PERMISSIONS.CREATE
|
|
52
|
-
]
|
|
53
|
-
},
|
|
54
|
-
getById: {
|
|
55
|
-
level: 'permission',
|
|
56
|
-
permissions: [
|
|
57
|
-
FILE_PERMISSIONS.READ
|
|
58
|
-
]
|
|
59
|
-
},
|
|
60
|
-
getAll: {
|
|
61
|
-
level: 'permission',
|
|
62
|
-
permissions: [
|
|
63
|
-
FILE_PERMISSIONS.READ
|
|
64
|
-
]
|
|
65
|
-
},
|
|
66
|
-
update: {
|
|
67
|
-
level: 'permission',
|
|
68
|
-
permissions: [
|
|
69
|
-
FILE_PERMISSIONS.UPDATE
|
|
70
|
-
]
|
|
71
|
-
},
|
|
72
|
-
updateMany: {
|
|
73
|
-
level: 'permission',
|
|
74
|
-
permissions: [
|
|
75
|
-
FILE_PERMISSIONS.UPDATE
|
|
76
|
-
]
|
|
77
|
-
},
|
|
78
|
-
delete: {
|
|
79
|
-
level: 'permission',
|
|
80
|
-
permissions: [
|
|
81
|
-
FILE_PERMISSIONS.DELETE
|
|
82
|
-
]
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}) {
|
|
86
|
-
async getFiles(dto, req, user) {
|
|
87
|
-
if (!dto || !dto.length) {
|
|
88
|
-
throw new BadRequestException({
|
|
89
|
-
message: 'No files provided',
|
|
90
|
-
messageKey: UPLOAD_MESSAGES.NO_FILES_PROVIDED
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
// Fetch files from service
|
|
94
|
-
const files = await this.fileService.getFiles(dto, req.protocol, req.get('host') || req.hostname, user);
|
|
95
|
-
return {
|
|
96
|
-
success: true,
|
|
97
|
-
message: 'Files retrieved successfully',
|
|
98
|
-
messageKey: FILE_MESSAGES.GET_ALL_SUCCESS,
|
|
99
|
-
data: files
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
constructor(fileService){
|
|
103
|
-
super(fileService), _define_property(this, "fileService", void 0), this.fileService = fileService;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
_ts_decorate([
|
|
107
|
-
Post('get-files'),
|
|
108
|
-
UseGuards(JwtAuthGuard),
|
|
109
|
-
ApiBearerAuth(),
|
|
110
|
-
HttpCode(200),
|
|
111
|
-
ApiBody({
|
|
112
|
-
type: GetFilesRequestDto,
|
|
113
|
-
isArray: true
|
|
114
|
-
}),
|
|
115
|
-
ApiOperation({
|
|
116
|
-
summary: 'Get multiple files by IDs with authentication (local or S3)'
|
|
117
|
-
}),
|
|
118
|
-
ApiResponse({
|
|
119
|
-
status: 200,
|
|
120
|
-
description: 'Files retrieved successfully',
|
|
121
|
-
type: SingleResponseDto
|
|
122
|
-
}),
|
|
123
|
-
_ts_param(0, Body()),
|
|
124
|
-
_ts_param(1, Req()),
|
|
125
|
-
_ts_param(2, CurrentUser()),
|
|
126
|
-
_ts_metadata("design:type", Function),
|
|
127
|
-
_ts_metadata("design:paramtypes", [
|
|
128
|
-
typeof Array === "undefined" ? Object : Array,
|
|
129
|
-
typeof Request === "undefined" ? Object : Request,
|
|
130
|
-
typeof ILoggedUserInfo === "undefined" ? Object : ILoggedUserInfo
|
|
131
|
-
]),
|
|
132
|
-
_ts_metadata("design:returntype", Promise)
|
|
133
|
-
], FileManagerController.prototype, "getFiles", null);
|
|
134
|
-
FileManagerController = _ts_decorate([
|
|
135
|
-
ApiTags('Files'),
|
|
136
|
-
Controller('storage/file-manager'),
|
|
137
|
-
_ts_param(0, Inject(FileManagerService)),
|
|
138
|
-
_ts_metadata("design:type", Function),
|
|
139
|
-
_ts_metadata("design:paramtypes", [
|
|
140
|
-
typeof FileManagerService === "undefined" ? Object : FileManagerService
|
|
141
|
-
])
|
|
142
|
-
], FileManagerController);
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import { BadRequestException } from '@nestjs/common';
|
|
2
|
-
import { buildMockUser } from '@test-utils/mocks/logged-user.mock';
|
|
3
|
-
import { FileManagerController } from './file-manager.controller';
|
|
4
|
-
function buildFile(overrides = {}) {
|
|
5
|
-
return {
|
|
6
|
-
id: 'file-1',
|
|
7
|
-
name: 'report.pdf',
|
|
8
|
-
key: 'files/report.pdf',
|
|
9
|
-
size: 1024,
|
|
10
|
-
contentType: 'application/pdf',
|
|
11
|
-
isActive: true,
|
|
12
|
-
readOnly: false,
|
|
13
|
-
createdAt: new Date(),
|
|
14
|
-
updatedAt: new Date(),
|
|
15
|
-
...overrides
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
describe('FileManagerController', ()=>{
|
|
19
|
-
let controller;
|
|
20
|
-
let mockFileService;
|
|
21
|
-
beforeEach(()=>{
|
|
22
|
-
mockFileService = {
|
|
23
|
-
insert: jest.fn(),
|
|
24
|
-
findById: jest.fn(),
|
|
25
|
-
getAll: jest.fn(),
|
|
26
|
-
update: jest.fn(),
|
|
27
|
-
delete: jest.fn(),
|
|
28
|
-
getFiles: jest.fn()
|
|
29
|
-
};
|
|
30
|
-
controller = new FileManagerController(mockFileService);
|
|
31
|
-
});
|
|
32
|
-
describe('insert', ()=>{
|
|
33
|
-
it('creates a file record and returns the mapped response', async ()=>{
|
|
34
|
-
const user = buildMockUser();
|
|
35
|
-
mockFileService.insert.mockResolvedValue(buildFile());
|
|
36
|
-
const result = await controller.insert({
|
|
37
|
-
name: 'report.pdf'
|
|
38
|
-
}, user);
|
|
39
|
-
expect(mockFileService.insert).toHaveBeenCalledWith({
|
|
40
|
-
name: 'report.pdf'
|
|
41
|
-
}, user);
|
|
42
|
-
expect(result.success).toBe(true);
|
|
43
|
-
expect(result.data).toEqual(expect.objectContaining({
|
|
44
|
-
id: 'file-1'
|
|
45
|
-
}));
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
describe('getAll', ()=>{
|
|
49
|
-
it('paginates files and maps the response list', async ()=>{
|
|
50
|
-
mockFileService.getAll.mockResolvedValue({
|
|
51
|
-
data: [
|
|
52
|
-
buildFile()
|
|
53
|
-
],
|
|
54
|
-
total: 1
|
|
55
|
-
});
|
|
56
|
-
const result = await controller.getAll({}, buildMockUser(), '');
|
|
57
|
-
expect(mockFileService.getAll).toHaveBeenCalledWith('', {}, buildMockUser());
|
|
58
|
-
expect(result.data).toHaveLength(1);
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
describe('getFiles', ()=>{
|
|
62
|
-
it('resolves signed/public URLs for the requested files using the request protocol and host', async ()=>{
|
|
63
|
-
const user = buildMockUser();
|
|
64
|
-
const req = {
|
|
65
|
-
protocol: 'https',
|
|
66
|
-
hostname: 'app.example.com',
|
|
67
|
-
get: jest.fn().mockReturnValue('app.example.com')
|
|
68
|
-
};
|
|
69
|
-
mockFileService.getFiles.mockResolvedValue([
|
|
70
|
-
{
|
|
71
|
-
id: 'file-1',
|
|
72
|
-
url: 'https://app.example.com/storage/upload/file/report.pdf'
|
|
73
|
-
}
|
|
74
|
-
]);
|
|
75
|
-
const result = await controller.getFiles([
|
|
76
|
-
{
|
|
77
|
-
id: 'file-1'
|
|
78
|
-
}
|
|
79
|
-
], req, user);
|
|
80
|
-
expect(mockFileService.getFiles).toHaveBeenCalledWith([
|
|
81
|
-
{
|
|
82
|
-
id: 'file-1'
|
|
83
|
-
}
|
|
84
|
-
], 'https', 'app.example.com', user);
|
|
85
|
-
expect(result.success).toBe(true);
|
|
86
|
-
expect(result.data).toEqual([
|
|
87
|
-
{
|
|
88
|
-
id: 'file-1',
|
|
89
|
-
url: 'https://app.example.com/storage/upload/file/report.pdf'
|
|
90
|
-
}
|
|
91
|
-
]);
|
|
92
|
-
});
|
|
93
|
-
it('falls back to req.hostname when the host header is absent', async ()=>{
|
|
94
|
-
const user = buildMockUser();
|
|
95
|
-
const req = {
|
|
96
|
-
protocol: 'http',
|
|
97
|
-
hostname: 'localhost',
|
|
98
|
-
get: jest.fn().mockReturnValue(undefined)
|
|
99
|
-
};
|
|
100
|
-
mockFileService.getFiles.mockResolvedValue([]);
|
|
101
|
-
await controller.getFiles([
|
|
102
|
-
{
|
|
103
|
-
id: 'file-1'
|
|
104
|
-
}
|
|
105
|
-
], req, user);
|
|
106
|
-
expect(mockFileService.getFiles).toHaveBeenCalledWith([
|
|
107
|
-
{
|
|
108
|
-
id: 'file-1'
|
|
109
|
-
}
|
|
110
|
-
], 'http', 'localhost', user);
|
|
111
|
-
});
|
|
112
|
-
it('rejects an empty file id list without calling the service', async ()=>{
|
|
113
|
-
const user = buildMockUser();
|
|
114
|
-
const req = {
|
|
115
|
-
protocol: 'https',
|
|
116
|
-
get: jest.fn()
|
|
117
|
-
};
|
|
118
|
-
await expect(controller.getFiles([], req, user)).rejects.toThrow(BadRequestException);
|
|
119
|
-
expect(mockFileService.getFiles).not.toHaveBeenCalled();
|
|
120
|
-
});
|
|
121
|
-
it('rejects a null/undefined dto without calling the service', async ()=>{
|
|
122
|
-
const user = buildMockUser();
|
|
123
|
-
const req = {
|
|
124
|
-
protocol: 'https',
|
|
125
|
-
get: jest.fn()
|
|
126
|
-
};
|
|
127
|
-
await expect(controller.getFiles(undefined, req, user)).rejects.toThrow(BadRequestException);
|
|
128
|
-
expect(mockFileService.getFiles).not.toHaveBeenCalled();
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
});
|