@flusys/nestjs-storage 3.0.0 → 4.0.0-lts
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 +1 -1
- package/cjs/config/index.js +1 -0
- package/cjs/config/message-keys.js +90 -0
- package/cjs/controllers/file-manager.controller.js +7 -1
- package/cjs/controllers/folder.controller.js +1 -0
- package/cjs/controllers/storage-config.controller.js +1 -0
- package/cjs/controllers/upload.controller.js +9 -4
- package/cjs/middlewares/file-serve.middleware.js +39 -15
- package/cjs/modules/storage.module.js +2 -2
- package/cjs/providers/azure-provider.optional.js +8 -5
- package/cjs/providers/local-provider.js +16 -14
- package/cjs/providers/s3-provider.optional.js +8 -5
- package/cjs/providers/sftp-provider.optional.js +13 -11
- package/cjs/providers/storage-factory.service.js +19 -10
- package/cjs/services/file-manager.service.js +30 -23
- package/cjs/services/folder.service.js +2 -1
- package/cjs/services/storage-datasource.provider.js +6 -2
- package/cjs/services/storage-provider-config.service.js +2 -1
- package/cjs/services/upload.service.js +129 -79
- package/cjs/utils/file-validator.util.js +4 -22
- package/config/index.d.ts +1 -0
- package/config/message-keys.d.ts +114 -0
- package/controllers/folder.controller.d.ts +7 -7
- package/controllers/storage-config.controller.d.ts +7 -7
- package/controllers/upload.controller.d.ts +1 -1
- package/fesm/config/index.js +1 -0
- package/fesm/config/message-keys.js +64 -0
- package/fesm/controllers/file-manager.controller.js +7 -1
- package/fesm/controllers/folder.controller.js +1 -0
- package/fesm/controllers/storage-config.controller.js +1 -0
- package/fesm/controllers/upload.controller.js +9 -4
- package/fesm/middlewares/file-serve.middleware.js +40 -16
- package/fesm/modules/storage.module.js +2 -2
- package/fesm/providers/azure-provider.optional.js +9 -6
- package/fesm/providers/local-provider.js +28 -26
- package/fesm/providers/s3-provider.optional.js +9 -6
- package/fesm/providers/sftp-provider.optional.js +26 -24
- package/fesm/providers/storage-factory.service.js +20 -11
- package/fesm/services/file-manager.service.js +31 -24
- package/fesm/services/folder.service.js +2 -1
- package/fesm/services/storage-datasource.provider.js +7 -3
- package/fesm/services/storage-provider-config.service.js +2 -1
- package/fesm/services/upload.service.js +131 -81
- package/fesm/utils/file-validator.util.js +3 -21
- package/middlewares/file-serve.middleware.d.ts +0 -1
- package/package.json +5 -4
- package/providers/azure-provider.optional.d.ts +0 -1
- package/providers/local-provider.d.ts +0 -1
- package/providers/s3-provider.optional.d.ts +0 -1
- package/providers/sftp-provider.optional.d.ts +0 -1
- package/providers/storage-factory.service.d.ts +0 -1
- package/services/file-manager.service.d.ts +2 -2
- package/services/storage-datasource.provider.d.ts +0 -2
- package/services/upload.service.d.ts +0 -1
- package/utils/file-validator.util.d.ts +0 -1
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Storage Package Guide
|
|
2
2
|
|
|
3
3
|
> **Package:** `@flusys/nestjs-storage`
|
|
4
|
-
> **Version:** 3.0.
|
|
4
|
+
> **Version:** 3.0.1
|
|
5
5
|
> **Type:** File storage system with pluggable providers and multi-tenant support
|
|
6
6
|
|
|
7
7
|
This comprehensive guide covers the storage package - flexible file storage with multiple provider support.
|
package/cjs/config/index.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
3
3
|
value: true
|
|
4
4
|
});
|
|
5
5
|
_export_star(require("./storage.constants"), exports);
|
|
6
|
+
_export_star(require("./message-keys"), exports);
|
|
6
7
|
function _export_star(from, to) {
|
|
7
8
|
Object.keys(from).forEach(function(k) {
|
|
8
9
|
if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// ==================== STORAGE MODULE MESSAGE KEYS ====================
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
function _export(target, all) {
|
|
7
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
8
|
+
enumerable: true,
|
|
9
|
+
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
_export(exports, {
|
|
13
|
+
get FILE_MESSAGES () {
|
|
14
|
+
return FILE_MESSAGES;
|
|
15
|
+
},
|
|
16
|
+
get FOLDER_MESSAGES () {
|
|
17
|
+
return FOLDER_MESSAGES;
|
|
18
|
+
},
|
|
19
|
+
get STORAGE_CONFIG_MESSAGES () {
|
|
20
|
+
return STORAGE_CONFIG_MESSAGES;
|
|
21
|
+
},
|
|
22
|
+
get STORAGE_MODULE_MESSAGES () {
|
|
23
|
+
return STORAGE_MODULE_MESSAGES;
|
|
24
|
+
},
|
|
25
|
+
get UPLOAD_MESSAGES () {
|
|
26
|
+
return UPLOAD_MESSAGES;
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
const FILE_MESSAGES = {
|
|
30
|
+
CREATE_SUCCESS: 'file.create.success',
|
|
31
|
+
CREATE_MANY_SUCCESS: 'file.create.many.success',
|
|
32
|
+
GET_SUCCESS: 'file.get.success',
|
|
33
|
+
GET_ALL_SUCCESS: 'file.get.all.success',
|
|
34
|
+
UPDATE_SUCCESS: 'file.update.success',
|
|
35
|
+
UPDATE_MANY_SUCCESS: 'file.update.many.success',
|
|
36
|
+
DELETE_SUCCESS: 'file.delete.success',
|
|
37
|
+
RESTORE_SUCCESS: 'file.restore.success',
|
|
38
|
+
NOT_FOUND: 'file.not.found',
|
|
39
|
+
UPLOAD_SUCCESS: 'file.upload.success',
|
|
40
|
+
UPLOAD_MANY_SUCCESS: 'file.upload.many.success',
|
|
41
|
+
DOWNLOAD_SUCCESS: 'file.download.success',
|
|
42
|
+
MOVE_SUCCESS: 'file.move.success',
|
|
43
|
+
COPY_SUCCESS: 'file.copy.success'
|
|
44
|
+
};
|
|
45
|
+
const FOLDER_MESSAGES = {
|
|
46
|
+
CREATE_SUCCESS: 'folder.create.success',
|
|
47
|
+
CREATE_MANY_SUCCESS: 'folder.create.many.success',
|
|
48
|
+
GET_SUCCESS: 'folder.get.success',
|
|
49
|
+
GET_ALL_SUCCESS: 'folder.get.all.success',
|
|
50
|
+
UPDATE_SUCCESS: 'folder.update.success',
|
|
51
|
+
UPDATE_MANY_SUCCESS: 'folder.update.many.success',
|
|
52
|
+
DELETE_SUCCESS: 'folder.delete.success',
|
|
53
|
+
RESTORE_SUCCESS: 'folder.restore.success',
|
|
54
|
+
NOT_FOUND: 'folder.not.found',
|
|
55
|
+
TREE_SUCCESS: 'folder.tree.success',
|
|
56
|
+
CONTENTS_SUCCESS: 'folder.contents.success'
|
|
57
|
+
};
|
|
58
|
+
const STORAGE_CONFIG_MESSAGES = {
|
|
59
|
+
CREATE_SUCCESS: 'storage.config.create.success',
|
|
60
|
+
CREATE_MANY_SUCCESS: 'storage.config.create.many.success',
|
|
61
|
+
GET_SUCCESS: 'storage.config.get.success',
|
|
62
|
+
GET_ALL_SUCCESS: 'storage.config.get.all.success',
|
|
63
|
+
UPDATE_SUCCESS: 'storage.config.update.success',
|
|
64
|
+
UPDATE_MANY_SUCCESS: 'storage.config.update.many.success',
|
|
65
|
+
DELETE_SUCCESS: 'storage.config.delete.success',
|
|
66
|
+
RESTORE_SUCCESS: 'storage.config.restore.success',
|
|
67
|
+
NOT_FOUND: 'storage.config.not.found',
|
|
68
|
+
ACTIVE_SUCCESS: 'storage.config.active.success',
|
|
69
|
+
TEST_SUCCESS: 'storage.config.test.success',
|
|
70
|
+
TEST_FAILED: 'storage.config.test.failed'
|
|
71
|
+
};
|
|
72
|
+
const UPLOAD_MESSAGES = {
|
|
73
|
+
SUCCESS: 'upload.success',
|
|
74
|
+
MANY_SUCCESS: 'upload.many.success',
|
|
75
|
+
FAILED: 'upload.failed',
|
|
76
|
+
FILE_TOO_LARGE: 'upload.file.too.large',
|
|
77
|
+
INVALID_TYPE: 'upload.invalid.type',
|
|
78
|
+
CHUNK_SUCCESS: 'upload.chunk.success',
|
|
79
|
+
COMPLETE_SUCCESS: 'upload.complete.success',
|
|
80
|
+
NO_FILES_PROVIDED: 'upload.no.files.provided',
|
|
81
|
+
NO_FILE_PATH: 'upload.no.file.path',
|
|
82
|
+
INVALID_FILE_PATH: 'upload.invalid.file.path',
|
|
83
|
+
CONFIG_NOT_FOUND: 'upload.config.not.found'
|
|
84
|
+
};
|
|
85
|
+
const STORAGE_MODULE_MESSAGES = {
|
|
86
|
+
FILE: FILE_MESSAGES,
|
|
87
|
+
FOLDER: FOLDER_MESSAGES,
|
|
88
|
+
STORAGE_CONFIG: STORAGE_CONFIG_MESSAGES,
|
|
89
|
+
UPLOAD: UPLOAD_MESSAGES
|
|
90
|
+
};
|
|
@@ -10,6 +10,7 @@ Object.defineProperty(exports, "FileManagerController", {
|
|
|
10
10
|
});
|
|
11
11
|
const _common = require("@nestjs/common");
|
|
12
12
|
const _classes = require("@flusys/nestjs-shared/classes");
|
|
13
|
+
const _config = require("../config");
|
|
13
14
|
const _decorators = require("@flusys/nestjs-shared/decorators");
|
|
14
15
|
const _dtos = require("@flusys/nestjs-shared/dtos");
|
|
15
16
|
const _guards = require("@flusys/nestjs-shared/guards");
|
|
@@ -46,6 +47,7 @@ function _ts_param(paramIndex, decorator) {
|
|
|
46
47
|
};
|
|
47
48
|
}
|
|
48
49
|
let FileManagerController = class FileManagerController extends (0, _classes.createApiController)(_dtos1.CreateFileManagerDto, _dtos1.UpdateFileManagerDto, _dtos1.FileManagerResponseDto, {
|
|
50
|
+
entityName: 'file',
|
|
49
51
|
security: {
|
|
50
52
|
insert: {
|
|
51
53
|
level: 'permission',
|
|
@@ -93,13 +95,17 @@ let FileManagerController = class FileManagerController extends (0, _classes.cre
|
|
|
93
95
|
}) {
|
|
94
96
|
async getFiles(dto, req, user) {
|
|
95
97
|
if (!dto || !dto.length) {
|
|
96
|
-
throw new _common.BadRequestException(
|
|
98
|
+
throw new _common.BadRequestException({
|
|
99
|
+
message: 'No files provided',
|
|
100
|
+
messageKey: _config.UPLOAD_MESSAGES.NO_FILES_PROVIDED
|
|
101
|
+
});
|
|
97
102
|
}
|
|
98
103
|
// Fetch files from service
|
|
99
104
|
const files = await this.fileService.getFiles(dto, req.protocol, req.get('host') || req.hostname, user);
|
|
100
105
|
return {
|
|
101
106
|
success: true,
|
|
102
107
|
message: 'Files retrieved successfully',
|
|
108
|
+
messageKey: _config.FILE_MESSAGES.GET_ALL_SUCCESS,
|
|
103
109
|
data: files
|
|
104
110
|
};
|
|
105
111
|
}
|
|
@@ -41,6 +41,7 @@ function _ts_param(paramIndex, decorator) {
|
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
43
|
let FolderController = class FolderController extends (0, _classes.createApiController)(_dtos.CreateFolderDto, _dtos.UpdateFolderDto, _dtos.FolderResponseDto, {
|
|
44
|
+
entityName: 'folder',
|
|
44
45
|
security: {
|
|
45
46
|
insert: {
|
|
46
47
|
level: 'permission',
|
|
@@ -41,6 +41,7 @@ function _ts_param(paramIndex, decorator) {
|
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
43
|
let StorageConfigController = class StorageConfigController extends (0, _classes.createApiController)(_dtos.CreateStorageConfigDto, _dtos.UpdateStorageConfigDto, _dtos.StorageConfigResponseDto, {
|
|
44
|
+
entityName: 'storageConfig',
|
|
44
45
|
security: {
|
|
45
46
|
insert: {
|
|
46
47
|
level: 'permission',
|
|
@@ -8,8 +8,9 @@ Object.defineProperty(exports, "UploadController", {
|
|
|
8
8
|
return UploadController;
|
|
9
9
|
}
|
|
10
10
|
});
|
|
11
|
-
const
|
|
11
|
+
const _config = require("../config");
|
|
12
12
|
const _decorators = require("@flusys/nestjs-shared/decorators");
|
|
13
|
+
const _guards = require("@flusys/nestjs-shared/guards");
|
|
13
14
|
const _interfaces = require("@flusys/nestjs-shared/interfaces");
|
|
14
15
|
const _dtos = require("../dtos");
|
|
15
16
|
const _common = require("@nestjs/common");
|
|
@@ -44,7 +45,7 @@ function _ts_param(paramIndex, decorator) {
|
|
|
44
45
|
};
|
|
45
46
|
}
|
|
46
47
|
let UploadController = class UploadController {
|
|
47
|
-
|
|
48
|
+
buildFileResponse(file) {
|
|
48
49
|
return {
|
|
49
50
|
size: this.uploadService.bytesToKb(file.size),
|
|
50
51
|
name: file.name,
|
|
@@ -59,7 +60,8 @@ let UploadController = class UploadController {
|
|
|
59
60
|
return {
|
|
60
61
|
success: true,
|
|
61
62
|
message: 'File uploaded successfully',
|
|
62
|
-
|
|
63
|
+
messageKey: _config.UPLOAD_MESSAGES.SUCCESS,
|
|
64
|
+
data: this.buildFileResponse(result)
|
|
63
65
|
};
|
|
64
66
|
}
|
|
65
67
|
async uploadMultipleFiles(files, options, user) {
|
|
@@ -67,7 +69,8 @@ let UploadController = class UploadController {
|
|
|
67
69
|
return {
|
|
68
70
|
success: true,
|
|
69
71
|
message: 'Files uploaded successfully',
|
|
70
|
-
|
|
72
|
+
messageKey: _config.UPLOAD_MESSAGES.MANY_SUCCESS,
|
|
73
|
+
data: uploadResponses.map((file)=>this.buildFileResponse(file))
|
|
71
74
|
};
|
|
72
75
|
}
|
|
73
76
|
async deleteSingleFile(dto, user) {
|
|
@@ -75,6 +78,7 @@ let UploadController = class UploadController {
|
|
|
75
78
|
return {
|
|
76
79
|
success: true,
|
|
77
80
|
message: 'File deleted successfully',
|
|
81
|
+
messageKey: _config.FILE_MESSAGES.DELETE_SUCCESS,
|
|
78
82
|
data
|
|
79
83
|
};
|
|
80
84
|
}
|
|
@@ -83,6 +87,7 @@ let UploadController = class UploadController {
|
|
|
83
87
|
return {
|
|
84
88
|
success: true,
|
|
85
89
|
message: 'Files deleted successfully',
|
|
90
|
+
messageKey: _config.FILE_MESSAGES.DELETE_SUCCESS,
|
|
86
91
|
data
|
|
87
92
|
};
|
|
88
93
|
}
|
|
@@ -12,6 +12,7 @@ const _common = require("@nestjs/common");
|
|
|
12
12
|
const _fs = require("fs");
|
|
13
13
|
const _path = require("path");
|
|
14
14
|
const _mimetypes = /*#__PURE__*/ _interop_require_wildcard(require("mime-types"));
|
|
15
|
+
const _config = require("../config");
|
|
15
16
|
const _uploadservice = require("../services/upload.service");
|
|
16
17
|
function _define_property(obj, key, value) {
|
|
17
18
|
if (key in obj) {
|
|
@@ -97,7 +98,13 @@ let FileServeMiddleware = class FileServeMiddleware {
|
|
|
97
98
|
const fullPath = await this.resolveFilePath(normalizedPath);
|
|
98
99
|
const stats = (0, _fs.statSync)(fullPath);
|
|
99
100
|
if (!stats.isFile()) {
|
|
100
|
-
throw new _common.NotFoundException(
|
|
101
|
+
throw new _common.NotFoundException({
|
|
102
|
+
message: `Not a file: ${normalizedPath}`,
|
|
103
|
+
messageKey: _config.FILE_MESSAGES.NOT_FOUND,
|
|
104
|
+
messageParams: {
|
|
105
|
+
path: normalizedPath
|
|
106
|
+
}
|
|
107
|
+
});
|
|
101
108
|
}
|
|
102
109
|
const mimeType = this.getMimeType(fullPath);
|
|
103
110
|
this.setResponseHeaders(res, stats.size, mimeType, normalizedPath);
|
|
@@ -111,19 +118,23 @@ let FileServeMiddleware = class FileServeMiddleware {
|
|
|
111
118
|
const match = urlPath.match(/\/storage\/upload\/file\/(.+)/);
|
|
112
119
|
const normalizedPath = match ? match[1] : '';
|
|
113
120
|
if (!normalizedPath) {
|
|
114
|
-
throw new _common.
|
|
121
|
+
throw new _common.BadRequestException({
|
|
122
|
+
message: 'File path is required',
|
|
123
|
+
messageKey: _config.UPLOAD_MESSAGES.NO_FILE_PATH
|
|
124
|
+
});
|
|
115
125
|
}
|
|
116
|
-
this.logger.debug(`Attempting to serve file, normalizedPath: ${normalizedPath}`);
|
|
117
126
|
return normalizedPath;
|
|
118
127
|
}
|
|
119
128
|
/** Resolve file path using multiple fallback strategies */ async resolveFilePath(normalizedPath) {
|
|
120
129
|
// Strategy 1: Path relative to CWD (new format: key includes basePath)
|
|
121
130
|
let fullPath = (0, _path.join)(this.uploadDir, normalizedPath);
|
|
122
131
|
const resolvedPath = (0, _path.resolve)(fullPath);
|
|
123
|
-
this.logger.debug(`Strategy 1 - CWD relative: ${fullPath}`);
|
|
124
132
|
// Prevent path traversal attacks
|
|
125
133
|
if (!resolvedPath.startsWith(this.uploadDir)) {
|
|
126
|
-
throw new _common.
|
|
134
|
+
throw new _common.BadRequestException({
|
|
135
|
+
message: 'Invalid file path',
|
|
136
|
+
messageKey: _config.UPLOAD_MESSAGES.INVALID_FILE_PATH
|
|
137
|
+
});
|
|
127
138
|
}
|
|
128
139
|
if ((0, _fs.existsSync)(fullPath)) {
|
|
129
140
|
return fullPath;
|
|
@@ -136,7 +147,13 @@ let FileServeMiddleware = class FileServeMiddleware {
|
|
|
136
147
|
return fallbackPath;
|
|
137
148
|
}
|
|
138
149
|
}
|
|
139
|
-
throw new _common.NotFoundException(
|
|
150
|
+
throw new _common.NotFoundException({
|
|
151
|
+
message: `File not found: ${normalizedPath}`,
|
|
152
|
+
messageKey: _config.FILE_MESSAGES.NOT_FOUND,
|
|
153
|
+
messageParams: {
|
|
154
|
+
path: normalizedPath
|
|
155
|
+
}
|
|
156
|
+
});
|
|
140
157
|
}
|
|
141
158
|
/** Try basePath-based fallback strategies */ tryFallbackPaths(normalizedPath, basePath) {
|
|
142
159
|
const normalizedBasePath = basePath.replace(/^\.\//, '').replace(/\/$/, '');
|
|
@@ -145,12 +162,10 @@ let FileServeMiddleware = class FileServeMiddleware {
|
|
|
145
162
|
// Strategy 2: basePath + remaining path
|
|
146
163
|
const remainingPath = normalizedPath.substring(normalizedBasePath.length).replace(/^\//, '');
|
|
147
164
|
const fallbackPath = (0, _path.join)(basePath, remainingPath);
|
|
148
|
-
this.logger.debug(`Strategy 2 - basePath + remaining: ${fallbackPath}`);
|
|
149
165
|
if ((0, _fs.existsSync)(fallbackPath)) return fallbackPath;
|
|
150
166
|
} else {
|
|
151
167
|
// Strategy 3: Old format - prepend basePath
|
|
152
168
|
const fallbackPath = (0, _path.join)(basePath, normalizedPath);
|
|
153
|
-
this.logger.debug(`Strategy 3 - basePath + full key (old format): ${fallbackPath}`);
|
|
154
169
|
if ((0, _fs.existsSync)(fallbackPath)) return fallbackPath;
|
|
155
170
|
}
|
|
156
171
|
return null;
|
|
@@ -179,20 +194,31 @@ let FileServeMiddleware = class FileServeMiddleware {
|
|
|
179
194
|
/** Stream file to response with error handling */ streamFile(res, fullPath, normalizedPath) {
|
|
180
195
|
const stream = (0, _fs.createReadStream)(fullPath);
|
|
181
196
|
stream.pipe(res);
|
|
182
|
-
stream.on('error', (
|
|
183
|
-
this.logger.error('File stream error', err);
|
|
197
|
+
stream.on('error', ()=>{
|
|
184
198
|
if (!res.headersSent) {
|
|
185
|
-
this.sendErrorResponse(res, new _common.NotFoundException(
|
|
199
|
+
this.sendErrorResponse(res, new _common.NotFoundException({
|
|
200
|
+
message: `Failed to serve file: ${normalizedPath}`,
|
|
201
|
+
messageKey: _config.FILE_MESSAGES.NOT_FOUND,
|
|
202
|
+
messageParams: {
|
|
203
|
+
path: normalizedPath
|
|
204
|
+
}
|
|
205
|
+
}));
|
|
186
206
|
}
|
|
187
207
|
});
|
|
188
208
|
res.on('close', ()=>stream.destroy());
|
|
189
209
|
}
|
|
190
210
|
/** Send consistent error response */ sendErrorResponse(res, error) {
|
|
191
|
-
this.logger.error('File retrieval error:', error);
|
|
192
211
|
if (!res.headersSent) {
|
|
193
|
-
const
|
|
212
|
+
const isNotFound = error instanceof _common.NotFoundException;
|
|
213
|
+
const response = isNotFound ? error.getResponse() : null;
|
|
214
|
+
const message = response?.message ?? 'File not found';
|
|
215
|
+
const messageKey = response?.messageKey ?? _config.FILE_MESSAGES.NOT_FOUND;
|
|
216
|
+
const messageParams = response?.messageParams;
|
|
194
217
|
res.status(404).json({
|
|
218
|
+
success: false,
|
|
195
219
|
message,
|
|
220
|
+
messageKey,
|
|
221
|
+
messageParams,
|
|
196
222
|
error: 'Not Found',
|
|
197
223
|
statusCode: 404
|
|
198
224
|
});
|
|
@@ -200,10 +226,8 @@ let FileServeMiddleware = class FileServeMiddleware {
|
|
|
200
226
|
}
|
|
201
227
|
constructor(uploadService){
|
|
202
228
|
_define_property(this, "uploadService", void 0);
|
|
203
|
-
_define_property(this, "logger", void 0);
|
|
204
229
|
_define_property(this, "uploadDir", void 0);
|
|
205
230
|
this.uploadService = uploadService;
|
|
206
|
-
this.logger = new _common.Logger(FileServeMiddleware.name);
|
|
207
231
|
this.uploadDir = process.cwd();
|
|
208
232
|
}
|
|
209
233
|
};
|
|
@@ -24,7 +24,6 @@ function _ts_decorate(decorators, target, key, desc) {
|
|
|
24
24
|
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;
|
|
25
25
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
26
26
|
}
|
|
27
|
-
// ─── Module Constants ─────────────────────────────────────────────────────────
|
|
28
27
|
const logger = new _common.Logger('StorageModule');
|
|
29
28
|
const CONTROLLERS = [
|
|
30
29
|
_controllers.FileManagerController,
|
|
@@ -41,9 +40,10 @@ const EXPORTED_SERVICES = [
|
|
|
41
40
|
_services.UploadService,
|
|
42
41
|
_providers.StorageFactoryService
|
|
43
42
|
];
|
|
44
|
-
//
|
|
43
|
+
// Register built-in provider
|
|
45
44
|
_providers.StorageProviderRegistry.register(_filelocationenum.FileLocationEnum.LOCAL, _localprovider.LocalProvider);
|
|
46
45
|
logger.log('Registered LocalProvider');
|
|
46
|
+
// Register optional providers (skip if dependencies not installed)
|
|
47
47
|
const OPTIONAL_PROVIDERS = [
|
|
48
48
|
{
|
|
49
49
|
location: _filelocationenum.FileLocationEnum.AWS,
|
|
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "AzureProvider", {
|
|
|
12
12
|
}
|
|
13
13
|
});
|
|
14
14
|
const _common = require("@nestjs/common");
|
|
15
|
+
const _constants = require("@flusys/nestjs-shared/constants");
|
|
15
16
|
const _uuid = require("uuid");
|
|
16
17
|
const _imagecompressorutil = require("../utils/image-compressor.util");
|
|
17
18
|
function _define_property(obj, key, value) {
|
|
@@ -77,9 +78,14 @@ let AzureProvider = class AzureProvider {
|
|
|
77
78
|
this.blobServiceClient = config.connectionString ? BlobServiceClient.fromConnectionString(config.connectionString) : new BlobServiceClient(`https://${config.accountName}.blob.core.windows.net`, new StorageSharedKeyCredential(config.accountName, config.accountKey));
|
|
78
79
|
this.containerClient = this.blobServiceClient.getContainerClient(config.containerName);
|
|
79
80
|
await this.containerClient.createIfNotExists();
|
|
80
|
-
this.logger.log(`Azure Provider initialized: account=${config.accountName}, container=${config.containerName}`);
|
|
81
81
|
} catch {
|
|
82
|
-
throw new
|
|
82
|
+
throw new _common.InternalServerErrorException({
|
|
83
|
+
message: 'Azure Storage SDK not found',
|
|
84
|
+
messageKey: _constants.SYSTEM_MESSAGES.SDK_NOT_INSTALLED,
|
|
85
|
+
messageParams: {
|
|
86
|
+
sdk: '@azure/storage-blob'
|
|
87
|
+
}
|
|
88
|
+
});
|
|
83
89
|
}
|
|
84
90
|
}
|
|
85
91
|
async uploadFile(file, options) {
|
|
@@ -114,11 +120,9 @@ let AzureProvider = class AzureProvider {
|
|
|
114
120
|
}
|
|
115
121
|
async deleteFile(key) {
|
|
116
122
|
await this.containerClient.getBlockBlobClient(key).deleteIfExists();
|
|
117
|
-
this.logger.log(`Deleted file from Azure: ${key}`);
|
|
118
123
|
}
|
|
119
124
|
async deleteMultipleFiles(keys) {
|
|
120
125
|
await Promise.all(keys.map((key)=>this.deleteFile(key)));
|
|
121
|
-
this.logger.log(`Deleted ${keys.length} files from Azure`);
|
|
122
126
|
}
|
|
123
127
|
async generatePresignedUrl(key, expiresInSeconds = 3600) {
|
|
124
128
|
const { BlobSASPermissions, generateBlobSASQueryParameters } = await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("@azure/storage-blob")));
|
|
@@ -143,7 +147,6 @@ let AzureProvider = class AzureProvider {
|
|
|
143
147
|
}
|
|
144
148
|
}
|
|
145
149
|
constructor(){
|
|
146
|
-
_define_property(this, "logger", new _common.Logger(AzureProvider.name));
|
|
147
150
|
_define_property(this, "blobServiceClient", void 0);
|
|
148
151
|
_define_property(this, "containerClient", void 0);
|
|
149
152
|
_define_property(this, "containerName", '');
|
|
@@ -19,8 +19,9 @@ Object.defineProperty(exports, "LocalProvider", {
|
|
|
19
19
|
return LocalProvider;
|
|
20
20
|
}
|
|
21
21
|
});
|
|
22
|
-
const _imagecompressorutil = require("../utils/image-compressor.util");
|
|
23
22
|
const _common = require("@nestjs/common");
|
|
23
|
+
const _constants = require("@flusys/nestjs-shared/constants");
|
|
24
|
+
const _imagecompressorutil = require("../utils/image-compressor.util");
|
|
24
25
|
const _promises = /*#__PURE__*/ _interop_require_wildcard(require("fs/promises"));
|
|
25
26
|
const _path = /*#__PURE__*/ _interop_require_wildcard(require("path"));
|
|
26
27
|
const _uuid = require("uuid");
|
|
@@ -87,8 +88,10 @@ let LocalProvider = class LocalProvider {
|
|
|
87
88
|
const normalizedBasePath = _path.resolve(this.basePath);
|
|
88
89
|
const normalizedTargetPath = _path.resolve(targetPath);
|
|
89
90
|
if (!normalizedTargetPath.startsWith(normalizedBasePath + _path.sep) && normalizedTargetPath !== normalizedBasePath) {
|
|
90
|
-
|
|
91
|
-
|
|
91
|
+
throw new _common.BadRequestException({
|
|
92
|
+
message: 'Invalid path: Path traversal attempt detected',
|
|
93
|
+
messageKey: _constants.SYSTEM_MESSAGES.PATH_TRAVERSAL_DETECTED
|
|
94
|
+
});
|
|
92
95
|
}
|
|
93
96
|
}
|
|
94
97
|
/**
|
|
@@ -96,10 +99,16 @@ let LocalProvider = class LocalProvider {
|
|
|
96
99
|
* @throws Error if key contains suspicious patterns
|
|
97
100
|
*/ validateKeyFormat(key) {
|
|
98
101
|
if (!key || typeof key !== 'string' || key.trim().length === 0) {
|
|
99
|
-
throw new
|
|
102
|
+
throw new _common.BadRequestException({
|
|
103
|
+
message: 'Invalid file key: empty or invalid',
|
|
104
|
+
messageKey: _constants.SYSTEM_MESSAGES.INVALID_FILE_KEY
|
|
105
|
+
});
|
|
100
106
|
}
|
|
101
107
|
if (key.includes('\0')) {
|
|
102
|
-
throw new
|
|
108
|
+
throw new _common.BadRequestException({
|
|
109
|
+
message: 'Invalid file key: contains null bytes',
|
|
110
|
+
messageKey: _constants.SYSTEM_MESSAGES.INVALID_FILE_KEY
|
|
111
|
+
});
|
|
103
112
|
}
|
|
104
113
|
}
|
|
105
114
|
/**
|
|
@@ -116,7 +125,6 @@ let LocalProvider = class LocalProvider {
|
|
|
116
125
|
await _promises.mkdir(this.basePath, {
|
|
117
126
|
recursive: true
|
|
118
127
|
});
|
|
119
|
-
this.logger.log(`Local Provider initialized: ${this.basePath} (relative: ${this.relativeBasePath})`);
|
|
120
128
|
}
|
|
121
129
|
async uploadFile(file, options) {
|
|
122
130
|
let processedBuffer = file.buffer;
|
|
@@ -149,7 +157,6 @@ let LocalProvider = class LocalProvider {
|
|
|
149
157
|
// Generate key that includes the base path (relative to cwd)
|
|
150
158
|
// This makes the key self-contained for serving files
|
|
151
159
|
const relativeKey = _path.join(this.relativeBasePath, options.folderPath || '', fileName).replace(/\\/g, '/'); // Normalize path separators
|
|
152
|
-
this.logger.log(`Uploaded file to local storage: ${relativeKey}`);
|
|
153
160
|
return {
|
|
154
161
|
name: fileName,
|
|
155
162
|
key: relativeKey,
|
|
@@ -179,26 +186,22 @@ let LocalProvider = class LocalProvider {
|
|
|
179
186
|
try {
|
|
180
187
|
await _promises.access(fallbackPath);
|
|
181
188
|
filePath = fallbackPath;
|
|
182
|
-
this.logger.debug(`Using fallback path for delete: ${fallbackPath}`);
|
|
183
189
|
} catch {
|
|
184
|
-
// File doesn't exist at either location
|
|
185
|
-
this.logger.warn(`File not found for deletion: ${key}`);
|
|
190
|
+
// File doesn't exist at either location - silent return
|
|
186
191
|
return;
|
|
187
192
|
}
|
|
188
193
|
}
|
|
189
194
|
await _promises.unlink(filePath);
|
|
190
|
-
this.logger.log(`Deleted file from local storage: ${key}`);
|
|
191
195
|
} catch (error) {
|
|
192
196
|
// Re-throw security errors
|
|
193
197
|
if (error instanceof Error && error.message.includes('Invalid')) {
|
|
194
198
|
throw error;
|
|
195
199
|
}
|
|
196
|
-
|
|
200
|
+
// Silent failure for other errors
|
|
197
201
|
}
|
|
198
202
|
}
|
|
199
203
|
async deleteMultipleFiles(keys) {
|
|
200
204
|
await Promise.all(keys.map((key)=>this.deleteFile(key)));
|
|
201
|
-
this.logger.log(`Deleted ${keys.length} files from local storage`);
|
|
202
205
|
}
|
|
203
206
|
async generatePresignedUrl(key, _expiresInSeconds) {
|
|
204
207
|
// Return public URL or relative path
|
|
@@ -220,7 +223,6 @@ let LocalProvider = class LocalProvider {
|
|
|
220
223
|
}
|
|
221
224
|
}
|
|
222
225
|
constructor(){
|
|
223
|
-
_define_property(this, "logger", new _common.Logger(LocalProvider.name));
|
|
224
226
|
_define_property(this, "basePath", '');
|
|
225
227
|
_define_property(this, "baseUrl", '');
|
|
226
228
|
_define_property(this, "relativeBasePath", ''); // Path relative to cwd for key generation
|
|
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "S3Provider", {
|
|
|
12
12
|
}
|
|
13
13
|
});
|
|
14
14
|
const _common = require("@nestjs/common");
|
|
15
|
+
const _constants = require("@flusys/nestjs-shared/constants");
|
|
15
16
|
const _uuid = require("uuid");
|
|
16
17
|
const _imagecompressorutil = require("../utils/image-compressor.util");
|
|
17
18
|
function _define_property(obj, key, value) {
|
|
@@ -82,9 +83,14 @@ let S3Provider = class S3Provider {
|
|
|
82
83
|
secretAccessKey: config.secretAccessKey
|
|
83
84
|
} : undefined
|
|
84
85
|
});
|
|
85
|
-
this.logger.log(`S3 Provider initialized: region=${config.region}, bucket=${config.bucket}`);
|
|
86
86
|
} catch {
|
|
87
|
-
throw new
|
|
87
|
+
throw new _common.InternalServerErrorException({
|
|
88
|
+
message: 'AWS SDK not found',
|
|
89
|
+
messageKey: _constants.SYSTEM_MESSAGES.SDK_NOT_INSTALLED,
|
|
90
|
+
messageParams: {
|
|
91
|
+
sdk: '@aws-sdk/client-s3'
|
|
92
|
+
}
|
|
93
|
+
});
|
|
88
94
|
}
|
|
89
95
|
}
|
|
90
96
|
async uploadFile(file, options) {
|
|
@@ -128,7 +134,6 @@ let S3Provider = class S3Provider {
|
|
|
128
134
|
Bucket: this.bucketName,
|
|
129
135
|
Key: key
|
|
130
136
|
}));
|
|
131
|
-
this.logger.log(`Deleted file from S3: ${key}`);
|
|
132
137
|
}
|
|
133
138
|
async deleteMultipleFiles(keys) {
|
|
134
139
|
const { DeleteObjectsCommand } = await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("@aws-sdk/client-s3")));
|
|
@@ -140,7 +145,6 @@ let S3Provider = class S3Provider {
|
|
|
140
145
|
}))
|
|
141
146
|
}
|
|
142
147
|
}));
|
|
143
|
-
this.logger.log(`Deleted ${keys.length} files from S3`);
|
|
144
148
|
}
|
|
145
149
|
async generatePresignedUrl(key, expiresInSeconds = 3600) {
|
|
146
150
|
const { GetObjectCommand } = await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("@aws-sdk/client-s3")));
|
|
@@ -164,7 +168,6 @@ let S3Provider = class S3Provider {
|
|
|
164
168
|
}
|
|
165
169
|
}
|
|
166
170
|
constructor(){
|
|
167
|
-
_define_property(this, "logger", new _common.Logger(S3Provider.name));
|
|
168
171
|
_define_property(this, "s3", void 0);
|
|
169
172
|
_define_property(this, "bucketName", '');
|
|
170
173
|
_define_property(this, "region", '');
|
|
@@ -23,8 +23,9 @@ Object.defineProperty(exports, "SftpProvider", {
|
|
|
23
23
|
return SftpProvider;
|
|
24
24
|
}
|
|
25
25
|
});
|
|
26
|
-
const _imagecompressorutil = require("../utils/image-compressor.util");
|
|
27
26
|
const _common = require("@nestjs/common");
|
|
27
|
+
const _constants = require("@flusys/nestjs-shared/constants");
|
|
28
|
+
const _imagecompressorutil = require("../utils/image-compressor.util");
|
|
28
29
|
const _path = /*#__PURE__*/ _interop_require_wildcard(require("path"));
|
|
29
30
|
const _uuid = require("uuid");
|
|
30
31
|
function _define_property(obj, key, value) {
|
|
@@ -102,10 +103,14 @@ let SftpProvider = class SftpProvider {
|
|
|
102
103
|
await this.connect();
|
|
103
104
|
// Ensure base directory exists
|
|
104
105
|
await this.ensureDirectory(this.config.basePath);
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
106
|
+
} catch {
|
|
107
|
+
throw new _common.InternalServerErrorException({
|
|
108
|
+
message: 'SFTP client not found',
|
|
109
|
+
messageKey: _constants.SYSTEM_MESSAGES.SDK_NOT_INSTALLED,
|
|
110
|
+
messageParams: {
|
|
111
|
+
sdk: 'ssh2-sftp-client'
|
|
112
|
+
}
|
|
113
|
+
});
|
|
109
114
|
}
|
|
110
115
|
}
|
|
111
116
|
async connect() {
|
|
@@ -123,7 +128,7 @@ let SftpProvider = class SftpProvider {
|
|
|
123
128
|
async ensureDirectory(dirPath) {
|
|
124
129
|
try {
|
|
125
130
|
await this.sftp.mkdir(dirPath, true);
|
|
126
|
-
} catch
|
|
131
|
+
} catch {
|
|
127
132
|
// Directory might already exist
|
|
128
133
|
}
|
|
129
134
|
}
|
|
@@ -163,14 +168,12 @@ let SftpProvider = class SftpProvider {
|
|
|
163
168
|
await this.connect();
|
|
164
169
|
try {
|
|
165
170
|
await this.sftp.delete(key);
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
this.logger.warn(`Failed to delete file from SFTP: ${key}`);
|
|
171
|
+
} catch {
|
|
172
|
+
// Silent failure
|
|
169
173
|
}
|
|
170
174
|
}
|
|
171
175
|
async deleteMultipleFiles(keys) {
|
|
172
176
|
await Promise.all(keys.map((key)=>this.deleteFile(key)));
|
|
173
|
-
this.logger.log(`Deleted ${keys.length} files from SFTP`);
|
|
174
177
|
}
|
|
175
178
|
async generatePresignedUrl(key, _expiresInSeconds) {
|
|
176
179
|
// SFTP doesn't support presigned URLs, return the key
|
|
@@ -193,7 +196,6 @@ let SftpProvider = class SftpProvider {
|
|
|
193
196
|
}
|
|
194
197
|
}
|
|
195
198
|
constructor(){
|
|
196
|
-
_define_property(this, "logger", new _common.Logger(SftpProvider.name));
|
|
197
199
|
_define_property(this, "sftp", void 0); // SftpClient
|
|
198
200
|
_define_property(this, "config", void 0);
|
|
199
201
|
_define_property(this, "connected", false);
|