@flusys/nestjs-storage 7.1.0 → 8.0.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/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
package/fesm/562.js
ADDED
|
@@ -0,0 +1,786 @@
|
|
|
1
|
+
import * as __rspack_external_sharp from "sharp";
|
|
2
|
+
import * as __rspack_external_path from "path";
|
|
3
|
+
import * as __rspack_external_uuid from "uuid";
|
|
4
|
+
import * as __rspack_external_fs from "fs";
|
|
5
|
+
import * as __rspack_external_mime_types_1e2a4730 from "mime-types";
|
|
6
|
+
import * as __rspack_external_passport from "passport";
|
|
7
|
+
import * as __rspack_external_fs_promises_e1851c9c from "fs/promises";
|
|
8
|
+
export const __rspack_esm_id = 562;
|
|
9
|
+
export const __rspack_esm_ids = [562];
|
|
10
|
+
export const __webpack_modules__ = {
|
|
11
|
+
969(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
12
|
+
|
|
13
|
+
// EXPORTS
|
|
14
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
15
|
+
u: () => (/* reexport */ FileServeMiddleware)
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// EXTERNAL MODULE: external "@nestjs/common"
|
|
19
|
+
var common_ = __webpack_require__(157);
|
|
20
|
+
;// CONCATENATED MODULE: external "fs"
|
|
21
|
+
|
|
22
|
+
// EXTERNAL MODULE: external "path"
|
|
23
|
+
var external_path_ = __webpack_require__(521);
|
|
24
|
+
;// CONCATENATED MODULE: external "mime-types"
|
|
25
|
+
|
|
26
|
+
;// CONCATENATED MODULE: external "passport"
|
|
27
|
+
|
|
28
|
+
// EXTERNAL MODULE: external "@flusys/nestjs-shared/constants"
|
|
29
|
+
var constants_ = __webpack_require__(140);
|
|
30
|
+
// EXTERNAL MODULE: ./projects/nestjs-storage/src/config/message-keys.ts
|
|
31
|
+
var message_keys = __webpack_require__(450);
|
|
32
|
+
// EXTERNAL MODULE: ./projects/nestjs-storage/src/services/upload.service.ts + 1 modules
|
|
33
|
+
var upload_service = __webpack_require__(18);
|
|
34
|
+
;// CONCATENATED MODULE: ./projects/nestjs-storage/src/middlewares/file-serve.middleware.ts
|
|
35
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
36
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
37
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
38
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
39
|
+
} else {
|
|
40
|
+
for(var i = decorators.length - 1; i >= 0; i--){
|
|
41
|
+
if (d = decorators[i]) {
|
|
42
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
47
|
+
}
|
|
48
|
+
function _ts_metadata(metadataKey, metadataValue) {
|
|
49
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
|
|
50
|
+
return Reflect.metadata(metadataKey, metadataValue);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function _ts_param(paramIndex, decorator) {
|
|
54
|
+
return function(target, key) {
|
|
55
|
+
decorator(target, key, paramIndex);
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
/** MIME type prefixes that can be displayed inline in browser */ const VIEWABLE_TYPE_PREFIXES = [
|
|
67
|
+
'image/',
|
|
68
|
+
'video/',
|
|
69
|
+
'audio/',
|
|
70
|
+
'text/',
|
|
71
|
+
'application/pdf',
|
|
72
|
+
'application/json',
|
|
73
|
+
'application/xml'
|
|
74
|
+
];
|
|
75
|
+
class FileServeMiddleware {
|
|
76
|
+
uploadService;
|
|
77
|
+
uploadDir = process.cwd();
|
|
78
|
+
constructor(uploadService){
|
|
79
|
+
this.uploadService = uploadService;
|
|
80
|
+
}
|
|
81
|
+
async use(req, res, next) {
|
|
82
|
+
await new Promise((resolvePromise)=>{
|
|
83
|
+
__rspack_external_passport["default"].authenticate('jwt', {
|
|
84
|
+
session: false
|
|
85
|
+
}, (err, user)=>{
|
|
86
|
+
if (err || !user) {
|
|
87
|
+
this.sendErrorResponse(res, new common_.UnauthorizedException({
|
|
88
|
+
message: 'Authentication required',
|
|
89
|
+
messageKey: constants_.AUTH_MESSAGES.TOKEN_INVALID
|
|
90
|
+
}));
|
|
91
|
+
resolvePromise();
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
req.user = user;
|
|
95
|
+
this.serveFile(req, res).finally(resolvePromise);
|
|
96
|
+
})(req, res, next);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
async serveFile(req, res) {
|
|
100
|
+
try {
|
|
101
|
+
const normalizedPath = this.extractFilePath(req);
|
|
102
|
+
const fullPath = await this.resolveFilePath(normalizedPath);
|
|
103
|
+
const stats = (0,__rspack_external_fs.statSync)(fullPath);
|
|
104
|
+
if (!stats.isFile()) {
|
|
105
|
+
throw new common_.NotFoundException({
|
|
106
|
+
message: `Not a file: ${normalizedPath}`,
|
|
107
|
+
messageKey: message_keys/* .FILE_MESSAGES.NOT_FOUND */.uA.NOT_FOUND,
|
|
108
|
+
messageVariables: {
|
|
109
|
+
path: normalizedPath
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
const mimeType = this.getMimeType(fullPath);
|
|
114
|
+
this.setResponseHeaders(res, stats.size, mimeType, normalizedPath);
|
|
115
|
+
this.streamFile(res, fullPath, normalizedPath);
|
|
116
|
+
} catch (error) {
|
|
117
|
+
this.sendErrorResponse(res, error);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/** Extract and validate file path from request URL */ extractFilePath(req) {
|
|
121
|
+
const urlPath = req.path || req.url;
|
|
122
|
+
const match = urlPath.match(/\/storage\/upload\/file\/(.+)/);
|
|
123
|
+
const normalizedPath = match ? match[1] : '';
|
|
124
|
+
if (!normalizedPath) {
|
|
125
|
+
throw new common_.BadRequestException({
|
|
126
|
+
message: 'File path is required',
|
|
127
|
+
messageKey: message_keys/* .UPLOAD_MESSAGES.NO_FILE_PATH */.dS.NO_FILE_PATH
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
return normalizedPath;
|
|
131
|
+
}
|
|
132
|
+
/** Resolve file path using multiple fallback strategies */ async resolveFilePath(normalizedPath) {
|
|
133
|
+
// Strategy 1: Path relative to CWD (new format: key includes basePath)
|
|
134
|
+
let fullPath = (0,external_path_.join)(this.uploadDir, normalizedPath);
|
|
135
|
+
const resolvedPath = (0,external_path_.resolve)(fullPath);
|
|
136
|
+
// Prevent path traversal attacks (boundary-safe: avoids matching sibling dirs like uploadDir + '-evil')
|
|
137
|
+
if (resolvedPath !== this.uploadDir && !resolvedPath.startsWith(this.uploadDir + external_path_.sep)) {
|
|
138
|
+
throw new common_.BadRequestException({
|
|
139
|
+
message: 'Invalid file path',
|
|
140
|
+
messageKey: message_keys/* .UPLOAD_MESSAGES.INVALID_FILE_PATH */.dS.INVALID_FILE_PATH
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
if ((0,__rspack_external_fs.existsSync)(fullPath)) {
|
|
144
|
+
return fullPath;
|
|
145
|
+
}
|
|
146
|
+
// Try fallback strategies with basePath
|
|
147
|
+
const basePath = await this.uploadService.getLocalStorageBasePath();
|
|
148
|
+
if (basePath) {
|
|
149
|
+
const fallbackPath = this.tryFallbackPaths(normalizedPath, basePath);
|
|
150
|
+
if (fallbackPath) {
|
|
151
|
+
return fallbackPath;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
throw new common_.NotFoundException({
|
|
155
|
+
message: `File not found: ${normalizedPath}`,
|
|
156
|
+
messageKey: message_keys/* .FILE_MESSAGES.NOT_FOUND */.uA.NOT_FOUND,
|
|
157
|
+
messageVariables: {
|
|
158
|
+
path: normalizedPath
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
/** Try basePath-based fallback strategies */ tryFallbackPaths(normalizedPath, basePath) {
|
|
163
|
+
const normalizedBasePath = basePath.replace(/^\.\//, '').replace(/\/$/, '');
|
|
164
|
+
const pathStartsWithBase = normalizedPath.startsWith(normalizedBasePath + '/') || normalizedPath.startsWith(normalizedBasePath);
|
|
165
|
+
if (pathStartsWithBase) {
|
|
166
|
+
// Strategy 2: basePath + remaining path
|
|
167
|
+
const remainingPath = normalizedPath.substring(normalizedBasePath.length).replace(/^\//, '');
|
|
168
|
+
const fallbackPath = (0,external_path_.join)(basePath, remainingPath);
|
|
169
|
+
if ((0,__rspack_external_fs.existsSync)(fallbackPath)) return fallbackPath;
|
|
170
|
+
} else {
|
|
171
|
+
// Strategy 3: Old format - prepend basePath
|
|
172
|
+
const fallbackPath = (0,external_path_.join)(basePath, normalizedPath);
|
|
173
|
+
if ((0,__rspack_external_fs.existsSync)(fallbackPath)) return fallbackPath;
|
|
174
|
+
}
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
/** Get MIME type with SVG special handling */ getMimeType(fullPath) {
|
|
178
|
+
if (fullPath.toLowerCase().endsWith('.svg')) {
|
|
179
|
+
return 'image/svg+xml';
|
|
180
|
+
}
|
|
181
|
+
return __rspack_external_mime_types_1e2a4730.lookup(fullPath) || 'application/octet-stream';
|
|
182
|
+
}
|
|
183
|
+
/** Set response headers for file serving */ setResponseHeaders(res, size, mimeType, normalizedPath) {
|
|
184
|
+
const isViewable = VIEWABLE_TYPE_PREFIXES.some((type)=>mimeType.startsWith(type));
|
|
185
|
+
const filename = normalizedPath.split('/').pop() || 'download';
|
|
186
|
+
const contentDisposition = isViewable ? 'inline' : `attachment; filename="${encodeURIComponent(filename)}"`;
|
|
187
|
+
res.set({
|
|
188
|
+
'Content-Type': mimeType,
|
|
189
|
+
'Content-Length': size,
|
|
190
|
+
'Content-Disposition': contentDisposition,
|
|
191
|
+
'Cache-Control': 'public, max-age=3600',
|
|
192
|
+
'Accept-Ranges': 'bytes',
|
|
193
|
+
'Cross-Origin-Resource-Policy': 'cross-origin',
|
|
194
|
+
'Access-Control-Allow-Origin': '*',
|
|
195
|
+
'X-Content-Type-Options': 'nosniff'
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
/** Stream file to response with error handling */ streamFile(res, fullPath, normalizedPath) {
|
|
199
|
+
const stream = (0,__rspack_external_fs.createReadStream)(fullPath);
|
|
200
|
+
stream.pipe(res);
|
|
201
|
+
stream.on('error', ()=>{
|
|
202
|
+
if (!res.headersSent) {
|
|
203
|
+
this.sendErrorResponse(res, new common_.NotFoundException({
|
|
204
|
+
message: `Failed to serve file: ${normalizedPath}`,
|
|
205
|
+
messageKey: message_keys/* .FILE_MESSAGES.NOT_FOUND */.uA.NOT_FOUND,
|
|
206
|
+
messageVariables: {
|
|
207
|
+
path: normalizedPath
|
|
208
|
+
}
|
|
209
|
+
}));
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
res.on('close', ()=>stream.destroy());
|
|
213
|
+
}
|
|
214
|
+
/** Send consistent error response */ sendErrorResponse(res, error) {
|
|
215
|
+
if (res.headersSent) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
const isHttpException = error instanceof common_.NotFoundException || error instanceof common_.BadRequestException || error instanceof common_.UnauthorizedException;
|
|
219
|
+
const response = isHttpException ? error.getResponse() : null;
|
|
220
|
+
const statusCode = isHttpException ? error.getStatus() : 404;
|
|
221
|
+
const message = response?.message ?? 'File not found';
|
|
222
|
+
const messageKey = response?.messageKey ?? message_keys/* .FILE_MESSAGES.NOT_FOUND */.uA.NOT_FOUND;
|
|
223
|
+
const messageVariables = response?.messageVariables;
|
|
224
|
+
res.status(statusCode).json({
|
|
225
|
+
success: false,
|
|
226
|
+
message,
|
|
227
|
+
messageKey,
|
|
228
|
+
messageVariables,
|
|
229
|
+
error: statusCode === 401 ? 'Unauthorized' : statusCode === 400 ? 'Bad Request' : 'Not Found',
|
|
230
|
+
statusCode
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
FileServeMiddleware = _ts_decorate([
|
|
235
|
+
(0,common_.Injectable)(),
|
|
236
|
+
_ts_param(0, (0,common_.Inject)(upload_service/* .UploadService */.B)),
|
|
237
|
+
_ts_metadata("design:type", Function),
|
|
238
|
+
_ts_metadata("design:paramtypes", [
|
|
239
|
+
typeof upload_service/* .UploadService */.B === "undefined" ? Object : upload_service/* .UploadService */.B
|
|
240
|
+
])
|
|
241
|
+
], FileServeMiddleware);
|
|
242
|
+
|
|
243
|
+
;// CONCATENATED MODULE: ./projects/nestjs-storage/src/middlewares/index.ts
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
},
|
|
248
|
+
329(module, __webpack_exports__, __webpack_require__) {
|
|
249
|
+
__webpack_require__.a(module, async function (__rspack_load_async_deps, __rspack_async_done) { try {
|
|
250
|
+
/* import */ var _storage_module_js__rspack_import_0 = __webpack_require__(624);
|
|
251
|
+
var __rspack_async_deps = __rspack_load_async_deps([_storage_module_js__rspack_import_0]);
|
|
252
|
+
_storage_module_js__rspack_import_0 = (__rspack_async_deps.then ? (await __rspack_async_deps)() : __rspack_async_deps)[0];
|
|
253
|
+
|
|
254
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
255
|
+
k: () => (/* reexport safe */ _storage_module_js__rspack_import_0.k)
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
__rspack_async_done();
|
|
259
|
+
} catch(e) { __rspack_async_done(e); } });
|
|
260
|
+
|
|
261
|
+
},
|
|
262
|
+
624(module, __webpack_exports__, __webpack_require__) {
|
|
263
|
+
__webpack_require__.a(module, async function (__rspack_load_async_deps, __rspack_async_done) { try {
|
|
264
|
+
/* import */ var _flusys_nestjs_shared_modules__rspack_import_0 = __webpack_require__(236);
|
|
265
|
+
/* import */ var _nestjs_common__rspack_import_1 = __webpack_require__(157);
|
|
266
|
+
/* import */ var _middlewares_index_js__rspack_import_2 = __webpack_require__(969);
|
|
267
|
+
/* import */ var _services_index_js__rspack_import_3 = __webpack_require__(148);
|
|
268
|
+
/* import */ var _config_storage_constants_js__rspack_import_9 = __webpack_require__(188);
|
|
269
|
+
/* import */ var _controllers_index_js__rspack_import_4 = __webpack_require__(488);
|
|
270
|
+
/* import */ var _enums_file_location_enum_js__rspack_import_8 = __webpack_require__(733);
|
|
271
|
+
/* import */ var _providers_index_js__rspack_import_5 = __webpack_require__(922);
|
|
272
|
+
/* import */ var _providers_local_provider_js__rspack_import_6 = __webpack_require__(632);
|
|
273
|
+
/* import */ var _services_storage_provider_config_service_js__rspack_import_7 = __webpack_require__(623);
|
|
274
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
275
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
276
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
277
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
278
|
+
} else {
|
|
279
|
+
for(var i = decorators.length - 1; i >= 0; i--){
|
|
280
|
+
if (d = decorators[i]) {
|
|
281
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
const logger = new _nestjs_common__rspack_import_1.Logger('StorageModule');
|
|
299
|
+
const CONTROLLERS = [
|
|
300
|
+
_controllers_index_js__rspack_import_4/* .FileManagerController */.AE,
|
|
301
|
+
_controllers_index_js__rspack_import_4/* .FolderController */.X$,
|
|
302
|
+
_controllers_index_js__rspack_import_4/* .StorageConfigController */.Iz,
|
|
303
|
+
_controllers_index_js__rspack_import_4/* .UploadController */.I7
|
|
304
|
+
];
|
|
305
|
+
const EXPORTED_SERVICES = [
|
|
306
|
+
_services_index_js__rspack_import_3/* .StorageConfigService */.tP,
|
|
307
|
+
_services_index_js__rspack_import_3/* .StorageDataSourceProvider */.M0,
|
|
308
|
+
_services_index_js__rspack_import_3/* .FileManagerService */.DL,
|
|
309
|
+
_services_index_js__rspack_import_3/* .FolderService */.kh,
|
|
310
|
+
_services_storage_provider_config_service_js__rspack_import_7/* .StorageProviderConfigService */.o,
|
|
311
|
+
_services_index_js__rspack_import_3/* .UploadService */.BB,
|
|
312
|
+
_providers_index_js__rspack_import_5/* .StorageFactoryService */.Lk
|
|
313
|
+
];
|
|
314
|
+
// Register built-in provider
|
|
315
|
+
_providers_index_js__rspack_import_5/* .StorageProviderRegistry.register */.CG.register(_enums_file_location_enum_js__rspack_import_8/* .FileLocationEnum.LOCAL */.N.LOCAL, _providers_local_provider_js__rspack_import_6/* .LocalProvider */.T);
|
|
316
|
+
logger.log('Registered LocalProvider');
|
|
317
|
+
// Register optional providers (skip if dependencies not installed).
|
|
318
|
+
// Each `import()` below MUST stay a literal string, not a variable — a computed
|
|
319
|
+
// specifier can't be statically resolved by Rspack/Webpack ("the request of a
|
|
320
|
+
// dependency is an expression"), which silently drops the dynamic import instead
|
|
321
|
+
// of bundling it, even when the underlying package is installed.
|
|
322
|
+
async function registerOptionalProvider(location, name, dep, loader) {
|
|
323
|
+
try {
|
|
324
|
+
const providerModule = await loader();
|
|
325
|
+
_providers_index_js__rspack_import_5/* .StorageProviderRegistry.register */.CG.register(location, providerModule[name]);
|
|
326
|
+
logger.log(`Registered ${name}`);
|
|
327
|
+
} catch {
|
|
328
|
+
logger.debug(`${name} not available (install ${dep} to enable)`);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
await registerOptionalProvider(_enums_file_location_enum_js__rspack_import_8/* .FileLocationEnum.AWS */.N.AWS, 'S3Provider', '@aws-sdk/client-s3', ()=>__webpack_require__.e(/* import() */ 591).then(__webpack_require__.bind(__webpack_require__, 970)));
|
|
332
|
+
await registerOptionalProvider(_enums_file_location_enum_js__rspack_import_8/* .FileLocationEnum.AZURE */.N.AZURE, 'AzureProvider', '@azure/storage-blob', ()=>__webpack_require__.e(/* import() */ 190).then(__webpack_require__.bind(__webpack_require__, 269)));
|
|
333
|
+
await registerOptionalProvider(_enums_file_location_enum_js__rspack_import_8/* .FileLocationEnum.SFTP */.N.SFTP, 'SftpProvider', 'ssh2-sftp-client', ()=>__webpack_require__.e(/* import() */ 918).then(__webpack_require__.bind(__webpack_require__, 5)));
|
|
334
|
+
class StorageModule {
|
|
335
|
+
configure(consumer) {
|
|
336
|
+
consumer.apply(_middlewares_index_js__rspack_import_2/* .FileServeMiddleware */.u).forRoutes({
|
|
337
|
+
path: 'storage/upload/file/*',
|
|
338
|
+
method: _nestjs_common__rspack_import_1.RequestMethod.GET
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
static forRoot(options) {
|
|
342
|
+
return {
|
|
343
|
+
module: StorageModule,
|
|
344
|
+
global: options.global ?? false,
|
|
345
|
+
imports: [
|
|
346
|
+
_flusys_nestjs_shared_modules__rspack_import_0.CacheModule,
|
|
347
|
+
_flusys_nestjs_shared_modules__rspack_import_0.UtilsModule
|
|
348
|
+
],
|
|
349
|
+
controllers: options.includeController !== false ? CONTROLLERS : [],
|
|
350
|
+
providers: this.buildProviders(options),
|
|
351
|
+
exports: EXPORTED_SERVICES
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
static forRootAsync(options) {
|
|
355
|
+
return {
|
|
356
|
+
module: StorageModule,
|
|
357
|
+
global: options.global ?? false,
|
|
358
|
+
imports: [
|
|
359
|
+
...options.imports || [],
|
|
360
|
+
_flusys_nestjs_shared_modules__rspack_import_0.CacheModule,
|
|
361
|
+
_flusys_nestjs_shared_modules__rspack_import_0.UtilsModule
|
|
362
|
+
],
|
|
363
|
+
controllers: options.includeController !== false ? CONTROLLERS : [],
|
|
364
|
+
providers: [
|
|
365
|
+
...this.createAsyncProviders(options),
|
|
366
|
+
...this.buildProviders()
|
|
367
|
+
],
|
|
368
|
+
exports: EXPORTED_SERVICES
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
// ─── Private Helpers ──────────────────────────────────────────────────────────
|
|
372
|
+
static buildProviders(options) {
|
|
373
|
+
const providers = [
|
|
374
|
+
...EXPORTED_SERVICES,
|
|
375
|
+
_middlewares_index_js__rspack_import_2/* .FileServeMiddleware */.u
|
|
376
|
+
];
|
|
377
|
+
if (options) {
|
|
378
|
+
providers.unshift({
|
|
379
|
+
provide: _config_storage_constants_js__rspack_import_9/* .STORAGE_MODULE_OPTIONS */.k0,
|
|
380
|
+
useValue: options
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
providers.push((0,_flusys_nestjs_shared_modules__rspack_import_0.createModuleEventsProvider)('storage', _config_storage_constants_js__rspack_import_9/* .STORAGE_MODULE_OPTIONS */.k0));
|
|
384
|
+
return providers;
|
|
385
|
+
}
|
|
386
|
+
static createAsyncProviders(options) {
|
|
387
|
+
if (options.useFactory) {
|
|
388
|
+
return [
|
|
389
|
+
{
|
|
390
|
+
provide: _config_storage_constants_js__rspack_import_9/* .STORAGE_MODULE_OPTIONS */.k0,
|
|
391
|
+
useFactory: async (...args)=>({
|
|
392
|
+
...options,
|
|
393
|
+
config: await options.useFactory(...args)
|
|
394
|
+
}),
|
|
395
|
+
inject: options.inject || []
|
|
396
|
+
}
|
|
397
|
+
];
|
|
398
|
+
}
|
|
399
|
+
const factoryClass = options.useClass || options.useExisting;
|
|
400
|
+
if (!factoryClass) return [];
|
|
401
|
+
const providers = [
|
|
402
|
+
{
|
|
403
|
+
provide: _config_storage_constants_js__rspack_import_9/* .STORAGE_MODULE_OPTIONS */.k0,
|
|
404
|
+
useFactory: async (factory)=>({
|
|
405
|
+
...options,
|
|
406
|
+
config: await factory.createStorageOptions()
|
|
407
|
+
}),
|
|
408
|
+
inject: [
|
|
409
|
+
factoryClass
|
|
410
|
+
]
|
|
411
|
+
}
|
|
412
|
+
];
|
|
413
|
+
if (options.useClass) {
|
|
414
|
+
providers.push({
|
|
415
|
+
provide: options.useClass,
|
|
416
|
+
useClass: options.useClass
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
return providers;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
StorageModule = _ts_decorate([
|
|
423
|
+
(0,_nestjs_common__rspack_import_1.Module)({})
|
|
424
|
+
], StorageModule);
|
|
425
|
+
|
|
426
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
427
|
+
k: () => (StorageModule)
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
__rspack_async_done();
|
|
431
|
+
} catch(e) { __rspack_async_done(e); } }, 1);
|
|
432
|
+
|
|
433
|
+
},
|
|
434
|
+
922(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
435
|
+
/* import */ var _storage_factory_service_js__rspack_import_0 = __webpack_require__(192);
|
|
436
|
+
/* import */ var _storage_provider_registry_js__rspack_import_1 = __webpack_require__(264);
|
|
437
|
+
/* import */ var _local_provider_js__rspack_import_2 = __webpack_require__(632);
|
|
438
|
+
// Core exports
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
// Built-in provider (no external dependencies)
|
|
442
|
+
// Note: Optional storage providers are loaded dynamically
|
|
443
|
+
// Import them directly from their files when needed:
|
|
444
|
+
// - import { S3Provider } from '@flusys/nestjs-storage/providers/s3-provider.optional';
|
|
445
|
+
// - import { AzureProvider } from '@flusys/nestjs-storage/providers/azure-provider.optional';
|
|
446
|
+
// - import { SftpProvider } from '@flusys/nestjs-storage/providers/sftp-provider.optional';
|
|
447
|
+
|
|
448
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
449
|
+
CG: () => (/* reexport safe */ _storage_provider_registry_js__rspack_import_1.C),
|
|
450
|
+
Lk: () => (/* reexport safe */ _storage_factory_service_js__rspack_import_0.L),
|
|
451
|
+
TZ: () => (/* reexport safe */ _local_provider_js__rspack_import_2.T)
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
},
|
|
456
|
+
632(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
457
|
+
|
|
458
|
+
// EXPORTS
|
|
459
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
460
|
+
T: () => (/* binding */ LocalProvider)
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
// EXTERNAL MODULE: external "@nestjs/common"
|
|
464
|
+
var common_ = __webpack_require__(157);
|
|
465
|
+
// EXTERNAL MODULE: external "@flusys/nestjs-shared/constants"
|
|
466
|
+
var constants_ = __webpack_require__(140);
|
|
467
|
+
// EXTERNAL MODULE: ./projects/nestjs-storage/src/utils/image-compressor.util.ts + 1 modules
|
|
468
|
+
var image_compressor_util = __webpack_require__(872);
|
|
469
|
+
;// CONCATENATED MODULE: external "fs/promises"
|
|
470
|
+
|
|
471
|
+
// EXTERNAL MODULE: external "path"
|
|
472
|
+
var external_path_ = __webpack_require__(521);
|
|
473
|
+
// EXTERNAL MODULE: external "uuid"
|
|
474
|
+
var external_uuid_ = __webpack_require__(37);
|
|
475
|
+
;// CONCATENATED MODULE: ./projects/nestjs-storage/src/providers/local-provider.ts
|
|
476
|
+
/**
|
|
477
|
+
* LOCAL File System Provider
|
|
478
|
+
*
|
|
479
|
+
* This provider uses Node.js built-in 'fs' module - NO external dependencies required!
|
|
480
|
+
* Perfect for same-server storage scenarios.
|
|
481
|
+
*
|
|
482
|
+
* Usage:
|
|
483
|
+
* import { LocalProvider } from '@flusys/nestjs-storage/providers/local-provider';
|
|
484
|
+
* import { StorageProviderRegistry, FileLocationEnum } from '@flusys/nestjs-storage';
|
|
485
|
+
*
|
|
486
|
+
* StorageProviderRegistry.register(FileLocationEnum.LOCAL, LocalProvider);
|
|
487
|
+
*/
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Local File System Storage Provider
|
|
495
|
+
* Uses Node.js built-in fs module - no external dependencies
|
|
496
|
+
*/ class LocalProvider {
|
|
497
|
+
basePath = '';
|
|
498
|
+
baseUrl = '';
|
|
499
|
+
relativeBasePath = '';
|
|
500
|
+
/**
|
|
501
|
+
* SECURITY: Validates that a target path does not escape the base directory
|
|
502
|
+
* Prevents path traversal attacks using ../ sequences
|
|
503
|
+
* @throws Error if path traversal is detected
|
|
504
|
+
*/ validatePathWithinBase(targetPath) {
|
|
505
|
+
const normalizedBasePath = external_path_.resolve(this.basePath);
|
|
506
|
+
const normalizedTargetPath = external_path_.resolve(targetPath);
|
|
507
|
+
if (!normalizedTargetPath.startsWith(normalizedBasePath + external_path_.sep) && normalizedTargetPath !== normalizedBasePath) {
|
|
508
|
+
throw new common_.BadRequestException({
|
|
509
|
+
message: 'Invalid path: Path traversal attempt detected',
|
|
510
|
+
messageKey: constants_.SYSTEM_MESSAGES.PATH_TRAVERSAL_DETECTED
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* SECURITY: Validates file key format to prevent malicious input
|
|
516
|
+
* @throws Error if key contains suspicious patterns
|
|
517
|
+
*/ validateKeyFormat(key) {
|
|
518
|
+
if (!key || typeof key !== 'string' || key.trim().length === 0) {
|
|
519
|
+
throw new common_.BadRequestException({
|
|
520
|
+
message: 'Invalid file key: empty or invalid',
|
|
521
|
+
messageKey: constants_.SYSTEM_MESSAGES.INVALID_FILE_KEY
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
if (key.includes('\0')) {
|
|
525
|
+
throw new common_.BadRequestException({
|
|
526
|
+
message: 'Invalid file key: contains null bytes',
|
|
527
|
+
messageKey: constants_.SYSTEM_MESSAGES.INVALID_FILE_KEY
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Initialize Local File System provider with configuration
|
|
533
|
+
* @param config.basePath - Base path for file storage (default: './uploads')
|
|
534
|
+
* @param config.baseUrl - Optional base URL for generating file URLs
|
|
535
|
+
*/ async initialize(config) {
|
|
536
|
+
// Store original relative path for key generation
|
|
537
|
+
this.relativeBasePath = config?.basePath || './uploads';
|
|
538
|
+
// Resolve to absolute path for file operations
|
|
539
|
+
this.basePath = external_path_.resolve(this.relativeBasePath);
|
|
540
|
+
this.baseUrl = config?.baseUrl || '';
|
|
541
|
+
// Ensure base directory exists
|
|
542
|
+
await __rspack_external_fs_promises_e1851c9c.mkdir(this.basePath, {
|
|
543
|
+
recursive: true
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
async uploadFile(file, options) {
|
|
547
|
+
let processedBuffer = file.buffer;
|
|
548
|
+
let contentType = file.mimetype;
|
|
549
|
+
const fileName = `${(0,external_uuid_.v4)()}-${file.originalname}`;
|
|
550
|
+
// Compress image if needed
|
|
551
|
+
if (options.compress && file.mimetype.startsWith('image/')) {
|
|
552
|
+
const result = await image_compressor_util/* .ImageCompressor.compress */.J.compress(file.buffer, file.mimetype, {
|
|
553
|
+
maxWidth: options.maxWidth,
|
|
554
|
+
maxHeight: options.maxHeight,
|
|
555
|
+
quality: options.quality,
|
|
556
|
+
format: options.format
|
|
557
|
+
});
|
|
558
|
+
processedBuffer = result.buffer;
|
|
559
|
+
contentType = result.format;
|
|
560
|
+
}
|
|
561
|
+
// Build file path
|
|
562
|
+
const folderPath = options.folderPath ? external_path_.join(this.basePath, options.folderPath) : this.basePath;
|
|
563
|
+
// SECURITY: Validate path does not escape base directory
|
|
564
|
+
this.validatePathWithinBase(folderPath);
|
|
565
|
+
const filePath = external_path_.join(folderPath, fileName);
|
|
566
|
+
// SECURITY: Double-check final file path
|
|
567
|
+
this.validatePathWithinBase(filePath);
|
|
568
|
+
// Ensure directory exists
|
|
569
|
+
await __rspack_external_fs_promises_e1851c9c.mkdir(folderPath, {
|
|
570
|
+
recursive: true
|
|
571
|
+
});
|
|
572
|
+
// Write file to disk
|
|
573
|
+
await __rspack_external_fs_promises_e1851c9c.writeFile(filePath, processedBuffer);
|
|
574
|
+
// Generate key that includes the base path (relative to cwd)
|
|
575
|
+
// This makes the key self-contained for serving files
|
|
576
|
+
const relativeKey = external_path_.join(this.relativeBasePath, options.folderPath || '', fileName).replace(/\\/g, '/'); // Normalize path separators
|
|
577
|
+
return {
|
|
578
|
+
name: fileName,
|
|
579
|
+
key: relativeKey,
|
|
580
|
+
size: processedBuffer.length,
|
|
581
|
+
contentType
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
async uploadMultipleFiles(files, options) {
|
|
585
|
+
return Promise.all(files.map((file)=>this.uploadFile(file, options)));
|
|
586
|
+
}
|
|
587
|
+
async deleteFile(key) {
|
|
588
|
+
try {
|
|
589
|
+
// SECURITY: Validate key format first
|
|
590
|
+
this.validateKeyFormat(key);
|
|
591
|
+
// Key now includes the basePath, resolve from cwd
|
|
592
|
+
let filePath = external_path_.resolve(key);
|
|
593
|
+
// SECURITY: Validate resolved path is within base directory
|
|
594
|
+
this.validatePathWithinBase(filePath);
|
|
595
|
+
// Check if file exists at the resolved path
|
|
596
|
+
try {
|
|
597
|
+
await __rspack_external_fs_promises_e1851c9c.access(filePath);
|
|
598
|
+
} catch {
|
|
599
|
+
// Fallback: try with basePath prefix (for old keys without basePath)
|
|
600
|
+
const fallbackPath = external_path_.join(this.basePath, key);
|
|
601
|
+
// SECURITY: Validate fallback path as well
|
|
602
|
+
this.validatePathWithinBase(fallbackPath);
|
|
603
|
+
try {
|
|
604
|
+
await __rspack_external_fs_promises_e1851c9c.access(fallbackPath);
|
|
605
|
+
filePath = fallbackPath;
|
|
606
|
+
} catch {
|
|
607
|
+
// File doesn't exist at either location - silent return
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
await __rspack_external_fs_promises_e1851c9c.unlink(filePath);
|
|
612
|
+
} catch (error) {
|
|
613
|
+
// Re-throw security errors
|
|
614
|
+
if (error instanceof Error && error.message.includes('Invalid')) {
|
|
615
|
+
throw error;
|
|
616
|
+
}
|
|
617
|
+
// Silent failure for other errors
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
async deleteMultipleFiles(keys) {
|
|
621
|
+
await Promise.all(keys.map((key)=>this.deleteFile(key)));
|
|
622
|
+
}
|
|
623
|
+
async generatePresignedUrl(key, _expiresInSeconds) {
|
|
624
|
+
// Return public URL or relative path
|
|
625
|
+
// Note: Local storage doesn't support true presigned URLs
|
|
626
|
+
// If baseUrl is provided, return full URL with file serving endpoint, otherwise return relative path
|
|
627
|
+
if (this.baseUrl) {
|
|
628
|
+
// Construct URL with the file serving endpoint
|
|
629
|
+
const baseUrlWithoutTrailingSlash = this.baseUrl.replace(/\/$/, '');
|
|
630
|
+
return `${baseUrlWithoutTrailingSlash}/storage/upload/file/${key}`;
|
|
631
|
+
}
|
|
632
|
+
return `/storage/upload/file/${key}`;
|
|
633
|
+
}
|
|
634
|
+
async healthCheck() {
|
|
635
|
+
try {
|
|
636
|
+
await __rspack_external_fs_promises_e1851c9c.access(this.basePath);
|
|
637
|
+
return true;
|
|
638
|
+
} catch {
|
|
639
|
+
return false;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
},
|
|
646
|
+
872(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
647
|
+
|
|
648
|
+
// EXPORTS
|
|
649
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
650
|
+
J: () => (/* binding */ ImageCompressor)
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
;// CONCATENATED MODULE: external "sharp"
|
|
654
|
+
|
|
655
|
+
;// CONCATENATED MODULE: ./projects/nestjs-storage/src/utils/image-compressor.util.ts
|
|
656
|
+
|
|
657
|
+
// Handle ESM/CJS interop - sharp exports differently in each module system
|
|
658
|
+
const sharp = __rspack_external_sharp["default"] || __rspack_external_sharp;
|
|
659
|
+
/**
|
|
660
|
+
* Image Compression Utility
|
|
661
|
+
* Handles image compression and format conversion using sharp
|
|
662
|
+
*/ class ImageCompressor {
|
|
663
|
+
/**
|
|
664
|
+
* Dynamically compresses an image based on its format and provided options.
|
|
665
|
+
* - Preserves original format if not overridden
|
|
666
|
+
* - Adjusts compression settings for each codec
|
|
667
|
+
*/ static async compress(buffer, mimetype, options = {}) {
|
|
668
|
+
const { maxWidth = 1280, maxHeight = 1280, quality = 85, format = 'original' } = options;
|
|
669
|
+
// Skip processing for SVG
|
|
670
|
+
if (mimetype === 'image/svg+xml') {
|
|
671
|
+
return {
|
|
672
|
+
buffer,
|
|
673
|
+
format: mimetype
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
// Initialize sharp with improved error handling
|
|
677
|
+
const image = sharp(buffer, {
|
|
678
|
+
failOnError: false,
|
|
679
|
+
density: 300
|
|
680
|
+
});
|
|
681
|
+
// Apply initial resize
|
|
682
|
+
image.resize({
|
|
683
|
+
width: maxWidth,
|
|
684
|
+
height: maxHeight,
|
|
685
|
+
fit: 'inside',
|
|
686
|
+
withoutEnlargement: true
|
|
687
|
+
});
|
|
688
|
+
// Determine target format
|
|
689
|
+
let targetFormat = format !== 'original' ? format : mimetype.split('/')[1];
|
|
690
|
+
// Format-specific optimizations
|
|
691
|
+
switch(targetFormat.toLowerCase()){
|
|
692
|
+
case 'jpeg':
|
|
693
|
+
case 'jpg':
|
|
694
|
+
image.jpeg({
|
|
695
|
+
quality,
|
|
696
|
+
mozjpeg: true,
|
|
697
|
+
chromaSubsampling: '4:4:4'
|
|
698
|
+
});
|
|
699
|
+
break;
|
|
700
|
+
case 'png':
|
|
701
|
+
image.png({
|
|
702
|
+
compressionLevel: 9,
|
|
703
|
+
adaptiveFiltering: true,
|
|
704
|
+
palette: true,
|
|
705
|
+
quality,
|
|
706
|
+
colors: 256,
|
|
707
|
+
dither: 1.0
|
|
708
|
+
});
|
|
709
|
+
break;
|
|
710
|
+
case 'webp':
|
|
711
|
+
image.webp({
|
|
712
|
+
quality,
|
|
713
|
+
smartSubsample: true,
|
|
714
|
+
effort: 6,
|
|
715
|
+
lossless: quality === 100
|
|
716
|
+
});
|
|
717
|
+
break;
|
|
718
|
+
case 'avif':
|
|
719
|
+
image.avif({
|
|
720
|
+
quality,
|
|
721
|
+
effort: 6,
|
|
722
|
+
chromaSubsampling: '4:4:4'
|
|
723
|
+
});
|
|
724
|
+
break;
|
|
725
|
+
case 'tiff':
|
|
726
|
+
image.tiff({
|
|
727
|
+
quality,
|
|
728
|
+
compression: 'lzw',
|
|
729
|
+
predictor: 'horizontal',
|
|
730
|
+
pyramid: true
|
|
731
|
+
});
|
|
732
|
+
break;
|
|
733
|
+
case 'gif':
|
|
734
|
+
image.gif({
|
|
735
|
+
colours: 256,
|
|
736
|
+
effort: 10,
|
|
737
|
+
dither: 1
|
|
738
|
+
});
|
|
739
|
+
break;
|
|
740
|
+
case 'jp2':
|
|
741
|
+
image.jp2({
|
|
742
|
+
quality,
|
|
743
|
+
lossless: quality === 100
|
|
744
|
+
});
|
|
745
|
+
break;
|
|
746
|
+
default:
|
|
747
|
+
// Fallback to WebP for best compression/quality ratio
|
|
748
|
+
targetFormat = 'webp';
|
|
749
|
+
image.webp({
|
|
750
|
+
quality,
|
|
751
|
+
smartSubsample: true,
|
|
752
|
+
effort: 6
|
|
753
|
+
});
|
|
754
|
+
break;
|
|
755
|
+
}
|
|
756
|
+
try {
|
|
757
|
+
const compressedBuffer = await image.toBuffer();
|
|
758
|
+
return {
|
|
759
|
+
buffer: compressedBuffer,
|
|
760
|
+
format: `image/${targetFormat}`
|
|
761
|
+
};
|
|
762
|
+
} catch {
|
|
763
|
+
return {
|
|
764
|
+
buffer,
|
|
765
|
+
format: mimetype
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
|
|
772
|
+
},
|
|
773
|
+
521(module) {
|
|
774
|
+
|
|
775
|
+
module.exports = __rspack_external_path;
|
|
776
|
+
|
|
777
|
+
|
|
778
|
+
},
|
|
779
|
+
37(module) {
|
|
780
|
+
|
|
781
|
+
module.exports = __rspack_external_uuid;
|
|
782
|
+
|
|
783
|
+
|
|
784
|
+
},
|
|
785
|
+
|
|
786
|
+
};
|