@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.
Files changed (202) hide show
  1. package/config/index.d.ts +3 -3
  2. package/controllers/file-manager.controller.d.ts +4 -4
  3. package/controllers/folder.controller.d.ts +2 -2
  4. package/controllers/index.d.ts +4 -4
  5. package/controllers/storage-config.controller.d.ts +2 -2
  6. package/controllers/upload.controller.d.ts +3 -3
  7. package/docs/index.d.ts +1 -1
  8. package/docs/storage-swagger.config.d.ts +2 -2
  9. package/dtos/index.d.ts +4 -4
  10. package/entities/file-manager-with-company.entity.d.ts +2 -2
  11. package/entities/file-manager.entity.d.ts +1 -1
  12. package/entities/folder-with-company.entity.d.ts +1 -1
  13. package/entities/index.d.ts +15 -15
  14. package/entities/storage-config-with-company.entity.d.ts +1 -1
  15. package/enums/index.d.ts +1 -1
  16. package/fesm/493.js +2768 -0
  17. package/fesm/537.js +1157 -0
  18. package/fesm/562.js +786 -0
  19. package/fesm/{providers/azure-provider.optional.js → chunks/190.js} +33 -30
  20. package/fesm/chunks/252.js +12 -0
  21. package/fesm/chunks/327.js +12 -0
  22. package/fesm/chunks/385.js +12 -0
  23. package/fesm/{providers/s3-provider.optional.js → chunks/591.js} +37 -34
  24. package/fesm/chunks/746.js +12 -0
  25. package/fesm/chunks/798.js +12 -0
  26. package/fesm/{providers/sftp-provider.optional.js → chunks/918.js} +35 -31
  27. package/fesm/config/index.js +119 -3
  28. package/fesm/controllers/index.js +16 -4
  29. package/fesm/docs/index.js +206 -1
  30. package/fesm/dtos/index.js +879 -4
  31. package/fesm/entities/index.js +463 -18
  32. package/fesm/enums/index.js +36 -1
  33. package/fesm/index.js +398 -12
  34. package/fesm/interfaces/index.js +19 -5
  35. package/fesm/modules/index.js +16 -1
  36. package/fesm/providers/index.js +364 -3
  37. package/fesm/runtime.js +252 -0
  38. package/fesm/services/index.js +16 -6
  39. package/fesm/utils/index.js +149 -1
  40. package/index.d.ts +11 -11
  41. package/interfaces/file-manager.interface.d.ts +1 -1
  42. package/interfaces/folder.interface.d.ts +1 -1
  43. package/interfaces/index.d.ts +5 -5
  44. package/interfaces/storage-config.interface.d.ts +1 -1
  45. package/interfaces/storage-module-options.interface.d.ts +3 -3
  46. package/interfaces/storage-provider.interface.d.ts +1 -1
  47. package/middlewares/file-serve.middleware.d.ts +4 -3
  48. package/middlewares/index.d.ts +1 -1
  49. package/modules/index.d.ts +1 -1
  50. package/modules/storage.module.d.ts +2 -2
  51. package/package.json +30 -41
  52. package/providers/azure-provider.optional.d.ts +2 -2
  53. package/providers/index.d.ts +3 -3
  54. package/providers/local-provider.d.ts +2 -2
  55. package/providers/s3-provider.optional.d.ts +2 -2
  56. package/providers/sftp-provider.optional.d.ts +2 -2
  57. package/providers/storage-factory.service.d.ts +3 -3
  58. package/providers/storage-provider.registry.d.ts +2 -2
  59. package/services/file-manager.service.d.ts +9 -8
  60. package/services/folder.service.d.ts +6 -6
  61. package/services/index.d.ts +6 -6
  62. package/services/storage-config.service.d.ts +3 -3
  63. package/services/storage-datasource.provider.d.ts +3 -3
  64. package/services/storage-provider-config.service.d.ts +7 -7
  65. package/services/upload.service.d.ts +6 -6
  66. package/utils/image-compressor.util.d.ts +1 -1
  67. package/utils/index.d.ts +1 -1
  68. package/cjs/config/event-actions.js +0 -34
  69. package/cjs/config/index.js +0 -20
  70. package/cjs/config/message-keys.js +0 -46
  71. package/cjs/config/storage.constants.js +0 -28
  72. package/cjs/controllers/file-manager.controller.js +0 -152
  73. package/cjs/controllers/file-manager.controller.spec.js +0 -135
  74. package/cjs/controllers/folder.controller.js +0 -102
  75. package/cjs/controllers/folder.controller.spec.js +0 -86
  76. package/cjs/controllers/index.js +0 -21
  77. package/cjs/controllers/storage-config.controller.js +0 -102
  78. package/cjs/controllers/storage-config.controller.spec.js +0 -85
  79. package/cjs/controllers/upload.controller.js +0 -234
  80. package/cjs/controllers/upload.controller.spec.js +0 -132
  81. package/cjs/docs/index.js +0 -11
  82. package/cjs/docs/storage-swagger.config.js +0 -188
  83. package/cjs/docs/storage-swagger.config.spec.js +0 -72
  84. package/cjs/dtos/file-manager.dto.js +0 -327
  85. package/cjs/dtos/folder.dto.js +0 -105
  86. package/cjs/dtos/index.js +0 -21
  87. package/cjs/dtos/storage-config.dto.js +0 -170
  88. package/cjs/dtos/upload.dto.js +0 -272
  89. package/cjs/entities/file-manager-with-company.entity.js +0 -61
  90. package/cjs/entities/file-manager.entity.js +0 -122
  91. package/cjs/entities/folder-with-company.entity.js +0 -55
  92. package/cjs/entities/folder.entity.js +0 -64
  93. package/cjs/entities/index.js +0 -63
  94. package/cjs/entities/index.spec.js +0 -18
  95. package/cjs/entities/storage-config-with-company.entity.js +0 -51
  96. package/cjs/entities/storage-config.entity.js +0 -96
  97. package/cjs/enums/file-location.enum.js +0 -17
  98. package/cjs/enums/index.js +0 -18
  99. package/cjs/index.js +0 -29
  100. package/cjs/interfaces/file-manager.interface.js +0 -4
  101. package/cjs/interfaces/folder.interface.js +0 -4
  102. package/cjs/interfaces/index.js +0 -22
  103. package/cjs/interfaces/storage-config.interface.js +0 -4
  104. package/cjs/interfaces/storage-module-options.interface.js +0 -4
  105. package/cjs/interfaces/storage-provider.interface.js +0 -4
  106. package/cjs/middlewares/file-serve.middleware.js +0 -241
  107. package/cjs/middlewares/file-serve.middleware.spec.js +0 -211
  108. package/cjs/middlewares/index.js +0 -18
  109. package/cjs/modules/index.js +0 -18
  110. package/cjs/modules/storage.module.js +0 -165
  111. package/cjs/modules/storage.module.spec.js +0 -184
  112. package/cjs/providers/azure-provider.optional.js +0 -155
  113. package/cjs/providers/azure-provider.optional.spec.js +0 -228
  114. package/cjs/providers/index.js +0 -26
  115. package/cjs/providers/local-provider.js +0 -230
  116. package/cjs/providers/local-provider.spec.js +0 -239
  117. package/cjs/providers/s3-provider.optional.js +0 -175
  118. package/cjs/providers/s3-provider.optional.spec.js +0 -243
  119. package/cjs/providers/sftp-provider.optional.js +0 -203
  120. package/cjs/providers/sftp-provider.optional.spec.js +0 -217
  121. package/cjs/providers/storage-factory.service.js +0 -175
  122. package/cjs/providers/storage-factory.service.spec.js +0 -253
  123. package/cjs/providers/storage-provider.registry.js +0 -41
  124. package/cjs/providers/storage-provider.registry.spec.js +0 -67
  125. package/cjs/services/file-manager.service.js +0 -387
  126. package/cjs/services/file-manager.service.spec.js +0 -580
  127. package/cjs/services/folder.service.js +0 -101
  128. package/cjs/services/folder.service.spec.js +0 -167
  129. package/cjs/services/index.js +0 -23
  130. package/cjs/services/storage-config.service.js +0 -129
  131. package/cjs/services/storage-config.service.spec.js +0 -257
  132. package/cjs/services/storage-datasource.provider.js +0 -137
  133. package/cjs/services/storage-datasource.provider.spec.js +0 -217
  134. package/cjs/services/storage-provider-config.service.js +0 -171
  135. package/cjs/services/storage-provider-config.service.spec.js +0 -265
  136. package/cjs/services/upload.service.js +0 -376
  137. package/cjs/services/upload.service.spec.js +0 -426
  138. package/cjs/utils/file-validator.util.js +0 -440
  139. package/cjs/utils/file-validator.util.spec.js +0 -259
  140. package/cjs/utils/image-compressor.util.js +0 -162
  141. package/cjs/utils/image-compressor.util.spec.js +0 -166
  142. package/cjs/utils/index.js +0 -18
  143. package/fesm/config/event-actions.js +0 -13
  144. package/fesm/config/message-keys.js +0 -22
  145. package/fesm/config/storage.constants.js +0 -7
  146. package/fesm/controllers/file-manager.controller.js +0 -142
  147. package/fesm/controllers/file-manager.controller.spec.js +0 -131
  148. package/fesm/controllers/folder.controller.js +0 -92
  149. package/fesm/controllers/folder.controller.spec.js +0 -82
  150. package/fesm/controllers/storage-config.controller.js +0 -92
  151. package/fesm/controllers/storage-config.controller.spec.js +0 -81
  152. package/fesm/controllers/upload.controller.js +0 -224
  153. package/fesm/controllers/upload.controller.spec.js +0 -128
  154. package/fesm/docs/storage-swagger.config.js +0 -181
  155. package/fesm/docs/storage-swagger.config.spec.js +0 -68
  156. package/fesm/dtos/file-manager.dto.js +0 -300
  157. package/fesm/dtos/folder.dto.js +0 -84
  158. package/fesm/dtos/storage-config.dto.js +0 -149
  159. package/fesm/dtos/upload.dto.js +0 -245
  160. package/fesm/entities/file-manager-with-company.entity.js +0 -51
  161. package/fesm/entities/file-manager.entity.js +0 -112
  162. package/fesm/entities/folder-with-company.entity.js +0 -45
  163. package/fesm/entities/folder.entity.js +0 -54
  164. package/fesm/entities/index.spec.js +0 -14
  165. package/fesm/entities/storage-config-with-company.entity.js +0 -41
  166. package/fesm/entities/storage-config.entity.js +0 -86
  167. package/fesm/enums/file-location.enum.js +0 -7
  168. package/fesm/interfaces/file-manager.interface.js +0 -1
  169. package/fesm/interfaces/folder.interface.js +0 -1
  170. package/fesm/interfaces/storage-config.interface.js +0 -1
  171. package/fesm/interfaces/storage-module-options.interface.js +0 -3
  172. package/fesm/interfaces/storage-provider.interface.js +0 -4
  173. package/fesm/middlewares/file-serve.middleware.js +0 -190
  174. package/fesm/middlewares/file-serve.middleware.spec.js +0 -207
  175. package/fesm/middlewares/index.js +0 -1
  176. package/fesm/modules/storage.module.js +0 -156
  177. package/fesm/modules/storage.module.spec.js +0 -180
  178. package/fesm/providers/azure-provider.optional.spec.js +0 -224
  179. package/fesm/providers/local-provider.js +0 -182
  180. package/fesm/providers/local-provider.spec.js +0 -194
  181. package/fesm/providers/s3-provider.optional.spec.js +0 -239
  182. package/fesm/providers/sftp-provider.optional.spec.js +0 -213
  183. package/fesm/providers/storage-factory.service.js +0 -124
  184. package/fesm/providers/storage-factory.service.spec.js +0 -249
  185. package/fesm/providers/storage-provider.registry.js +0 -34
  186. package/fesm/providers/storage-provider.registry.spec.js +0 -63
  187. package/fesm/services/file-manager.service.js +0 -336
  188. package/fesm/services/file-manager.service.spec.js +0 -576
  189. package/fesm/services/folder.service.js +0 -91
  190. package/fesm/services/folder.service.spec.js +0 -163
  191. package/fesm/services/storage-config.service.js +0 -119
  192. package/fesm/services/storage-config.service.spec.js +0 -253
  193. package/fesm/services/storage-datasource.provider.js +0 -86
  194. package/fesm/services/storage-datasource.provider.spec.js +0 -172
  195. package/fesm/services/storage-provider-config.service.js +0 -161
  196. package/fesm/services/storage-provider-config.service.spec.js +0 -261
  197. package/fesm/services/upload.service.js +0 -366
  198. package/fesm/services/upload.service.spec.js +0 -422
  199. package/fesm/utils/file-validator.util.js +0 -433
  200. package/fesm/utils/file-validator.util.spec.js +0 -255
  201. package/fesm/utils/image-compressor.util.js +0 -114
  202. package/fesm/utils/image-compressor.util.spec.js +0 -162
@@ -1,241 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- Object.defineProperty(exports, "FileServeMiddleware", {
6
- enumerable: true,
7
- get: function() {
8
- return FileServeMiddleware;
9
- }
10
- });
11
- const _common = require("@nestjs/common");
12
- const _fs = require("fs");
13
- const _path = require("path");
14
- const _mimetypes = /*#__PURE__*/ _interop_require_wildcard(require("mime-types"));
15
- const _config = require("../config");
16
- const _uploadservice = require("../services/upload.service");
17
- function _define_property(obj, key, value) {
18
- if (key in obj) {
19
- Object.defineProperty(obj, key, {
20
- value: value,
21
- enumerable: true,
22
- configurable: true,
23
- writable: true
24
- });
25
- } else {
26
- obj[key] = value;
27
- }
28
- return obj;
29
- }
30
- function _getRequireWildcardCache(nodeInterop) {
31
- if (typeof WeakMap !== "function") return null;
32
- var cacheBabelInterop = new WeakMap();
33
- var cacheNodeInterop = new WeakMap();
34
- return (_getRequireWildcardCache = function(nodeInterop) {
35
- return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
36
- })(nodeInterop);
37
- }
38
- function _interop_require_wildcard(obj, nodeInterop) {
39
- if (!nodeInterop && obj && obj.__esModule) {
40
- return obj;
41
- }
42
- if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
43
- return {
44
- default: obj
45
- };
46
- }
47
- var cache = _getRequireWildcardCache(nodeInterop);
48
- if (cache && cache.has(obj)) {
49
- return cache.get(obj);
50
- }
51
- var newObj = {
52
- __proto__: null
53
- };
54
- var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
55
- for(var key in obj){
56
- if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
57
- var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
58
- if (desc && (desc.get || desc.set)) {
59
- Object.defineProperty(newObj, key, desc);
60
- } else {
61
- newObj[key] = obj[key];
62
- }
63
- }
64
- }
65
- newObj.default = obj;
66
- if (cache) {
67
- cache.set(obj, newObj);
68
- }
69
- return newObj;
70
- }
71
- function _ts_decorate(decorators, target, key, desc) {
72
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
73
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
74
- 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;
75
- return c > 3 && r && Object.defineProperty(target, key, r), r;
76
- }
77
- function _ts_metadata(k, v) {
78
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
79
- }
80
- function _ts_param(paramIndex, decorator) {
81
- return function(target, key) {
82
- decorator(target, key, paramIndex);
83
- };
84
- }
85
- /** MIME type prefixes that can be displayed inline in browser */ const VIEWABLE_TYPE_PREFIXES = [
86
- 'image/',
87
- 'video/',
88
- 'audio/',
89
- 'text/',
90
- 'application/pdf',
91
- 'application/json',
92
- 'application/xml'
93
- ];
94
- let FileServeMiddleware = class FileServeMiddleware {
95
- async use(req, res, next) {
96
- try {
97
- const normalizedPath = this.extractFilePath(req);
98
- const fullPath = await this.resolveFilePath(normalizedPath);
99
- const stats = (0, _fs.statSync)(fullPath);
100
- if (!stats.isFile()) {
101
- throw new _common.NotFoundException({
102
- message: `Not a file: ${normalizedPath}`,
103
- messageKey: _config.FILE_MESSAGES.NOT_FOUND,
104
- messageVariables: {
105
- path: normalizedPath
106
- }
107
- });
108
- }
109
- const mimeType = this.getMimeType(fullPath);
110
- this.setResponseHeaders(res, stats.size, mimeType, normalizedPath);
111
- this.streamFile(res, fullPath, normalizedPath);
112
- } catch (error) {
113
- this.sendErrorResponse(res, error);
114
- }
115
- }
116
- /** Extract and validate file path from request URL */ extractFilePath(req) {
117
- const urlPath = req.path || req.url;
118
- const match = urlPath.match(/\/storage\/upload\/file\/(.+)/);
119
- const normalizedPath = match ? match[1] : '';
120
- if (!normalizedPath) {
121
- throw new _common.BadRequestException({
122
- message: 'File path is required',
123
- messageKey: _config.UPLOAD_MESSAGES.NO_FILE_PATH
124
- });
125
- }
126
- return normalizedPath;
127
- }
128
- /** Resolve file path using multiple fallback strategies */ async resolveFilePath(normalizedPath) {
129
- // Strategy 1: Path relative to CWD (new format: key includes basePath)
130
- let fullPath = (0, _path.join)(this.uploadDir, normalizedPath);
131
- const resolvedPath = (0, _path.resolve)(fullPath);
132
- // Prevent path traversal attacks
133
- if (!resolvedPath.startsWith(this.uploadDir)) {
134
- throw new _common.BadRequestException({
135
- message: 'Invalid file path',
136
- messageKey: _config.UPLOAD_MESSAGES.INVALID_FILE_PATH
137
- });
138
- }
139
- if ((0, _fs.existsSync)(fullPath)) {
140
- return fullPath;
141
- }
142
- // Try fallback strategies with basePath
143
- const basePath = await this.uploadService.getLocalStorageBasePath();
144
- if (basePath) {
145
- const fallbackPath = this.tryFallbackPaths(normalizedPath, basePath);
146
- if (fallbackPath) {
147
- return fallbackPath;
148
- }
149
- }
150
- throw new _common.NotFoundException({
151
- message: `File not found: ${normalizedPath}`,
152
- messageKey: _config.FILE_MESSAGES.NOT_FOUND,
153
- messageVariables: {
154
- path: normalizedPath
155
- }
156
- });
157
- }
158
- /** Try basePath-based fallback strategies */ tryFallbackPaths(normalizedPath, basePath) {
159
- const normalizedBasePath = basePath.replace(/^\.\//, '').replace(/\/$/, '');
160
- const pathStartsWithBase = normalizedPath.startsWith(normalizedBasePath + '/') || normalizedPath.startsWith(normalizedBasePath);
161
- if (pathStartsWithBase) {
162
- // Strategy 2: basePath + remaining path
163
- const remainingPath = normalizedPath.substring(normalizedBasePath.length).replace(/^\//, '');
164
- const fallbackPath = (0, _path.join)(basePath, remainingPath);
165
- if ((0, _fs.existsSync)(fallbackPath)) return fallbackPath;
166
- } else {
167
- // Strategy 3: Old format - prepend basePath
168
- const fallbackPath = (0, _path.join)(basePath, normalizedPath);
169
- if ((0, _fs.existsSync)(fallbackPath)) return fallbackPath;
170
- }
171
- return null;
172
- }
173
- /** Get MIME type with SVG special handling */ getMimeType(fullPath) {
174
- if (fullPath.toLowerCase().endsWith('.svg')) {
175
- return 'image/svg+xml';
176
- }
177
- return _mimetypes.lookup(fullPath) || 'application/octet-stream';
178
- }
179
- /** Set response headers for file serving */ setResponseHeaders(res, size, mimeType, normalizedPath) {
180
- const isViewable = VIEWABLE_TYPE_PREFIXES.some((type)=>mimeType.startsWith(type));
181
- const filename = normalizedPath.split('/').pop() || 'download';
182
- const contentDisposition = isViewable ? 'inline' : `attachment; filename="${encodeURIComponent(filename)}"`;
183
- res.set({
184
- 'Content-Type': mimeType,
185
- 'Content-Length': size,
186
- 'Content-Disposition': contentDisposition,
187
- 'Cache-Control': 'public, max-age=3600',
188
- 'Accept-Ranges': 'bytes',
189
- 'Cross-Origin-Resource-Policy': 'cross-origin',
190
- 'Access-Control-Allow-Origin': '*',
191
- 'X-Content-Type-Options': 'nosniff'
192
- });
193
- }
194
- /** Stream file to response with error handling */ streamFile(res, fullPath, normalizedPath) {
195
- const stream = (0, _fs.createReadStream)(fullPath);
196
- stream.pipe(res);
197
- stream.on('error', ()=>{
198
- if (!res.headersSent) {
199
- this.sendErrorResponse(res, new _common.NotFoundException({
200
- message: `Failed to serve file: ${normalizedPath}`,
201
- messageKey: _config.FILE_MESSAGES.NOT_FOUND,
202
- messageVariables: {
203
- path: normalizedPath
204
- }
205
- }));
206
- }
207
- });
208
- res.on('close', ()=>stream.destroy());
209
- }
210
- /** Send consistent error response */ sendErrorResponse(res, error) {
211
- if (!res.headersSent) {
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 messageVariables = response?.messageVariables;
217
- res.status(404).json({
218
- success: false,
219
- message,
220
- messageKey,
221
- messageVariables,
222
- error: 'Not Found',
223
- statusCode: 404
224
- });
225
- }
226
- }
227
- constructor(uploadService){
228
- _define_property(this, "uploadService", void 0);
229
- _define_property(this, "uploadDir", void 0);
230
- this.uploadService = uploadService;
231
- this.uploadDir = process.cwd();
232
- }
233
- };
234
- FileServeMiddleware = _ts_decorate([
235
- (0, _common.Injectable)(),
236
- _ts_param(0, (0, _common.Inject)(_uploadservice.UploadService)),
237
- _ts_metadata("design:type", Function),
238
- _ts_metadata("design:paramtypes", [
239
- typeof _uploadservice.UploadService === "undefined" ? Object : _uploadservice.UploadService
240
- ])
241
- ], FileServeMiddleware);
@@ -1,211 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- const _events = require("events");
6
- const _path = require("path");
7
- const _fileservemiddleware = require("./file-serve.middleware");
8
- function _define_property(obj, key, value) {
9
- if (key in obj) {
10
- Object.defineProperty(obj, key, {
11
- value: value,
12
- enumerable: true,
13
- configurable: true,
14
- writable: true
15
- });
16
- } else {
17
- obj[key] = value;
18
- }
19
- return obj;
20
- }
21
- const mockCreateReadStream = jest.fn();
22
- const mockExistsSync = jest.fn();
23
- const mockStatSync = jest.fn();
24
- jest.mock('fs', ()=>({
25
- ...jest.requireActual('fs'),
26
- createReadStream: (...args)=>mockCreateReadStream(...args),
27
- existsSync: (...args)=>mockExistsSync(...args),
28
- statSync: (...args)=>mockStatSync(...args)
29
- }));
30
- const mockMimeLookup = jest.fn();
31
- jest.mock('mime-types', ()=>({
32
- lookup: (...args)=>mockMimeLookup(...args)
33
- }));
34
- let FakeStream = class FakeStream extends _events.EventEmitter {
35
- constructor(...args){
36
- super(...args), _define_property(this, "pipe", jest.fn()), _define_property(this, "destroy", jest.fn());
37
- }
38
- };
39
- function buildRes() {
40
- const listeners = {};
41
- return {
42
- headersSent: false,
43
- set: jest.fn(),
44
- status: jest.fn().mockReturnThis(),
45
- json: jest.fn(),
46
- on: jest.fn((event, cb)=>{
47
- listeners[event] = listeners[event] || [];
48
- listeners[event].push(cb);
49
- }),
50
- emitClose: ()=>(listeners['close'] || []).forEach((cb)=>cb())
51
- };
52
- }
53
- function buildReq(urlPath) {
54
- return {
55
- path: urlPath,
56
- url: urlPath
57
- };
58
- }
59
- describe('FileServeMiddleware', ()=>{
60
- let middleware;
61
- let mockUploadService;
62
- let next;
63
- beforeEach(()=>{
64
- jest.clearAllMocks();
65
- mockUploadService = {
66
- getLocalStorageBasePath: jest.fn().mockResolvedValue(null)
67
- };
68
- middleware = new _fileservemiddleware.FileServeMiddleware(mockUploadService);
69
- next = jest.fn();
70
- });
71
- it('responds 404 when the file path segment is missing from the URL', async ()=>{
72
- const res = buildRes();
73
- await middleware.use(buildReq('/storage/upload/file/'), res, next);
74
- expect(res.status).toHaveBeenCalledWith(404);
75
- expect(res.json).toHaveBeenCalledWith(expect.objectContaining({
76
- success: false,
77
- statusCode: 404
78
- }));
79
- expect(mockExistsSync).not.toHaveBeenCalled();
80
- });
81
- it('blocks path traversal attempts that resolve outside the upload directory', async ()=>{
82
- const res = buildRes();
83
- mockExistsSync.mockReturnValue(false);
84
- await middleware.use(buildReq('/storage/upload/file/../../etc/passwd'), res, next);
85
- expect(res.status).toHaveBeenCalledWith(404);
86
- expect(mockStatSync).not.toHaveBeenCalled();
87
- });
88
- it('serves a file that exists directly under the upload directory with inline disposition for viewable types', async ()=>{
89
- const res = buildRes();
90
- mockExistsSync.mockReturnValue(true);
91
- mockStatSync.mockReturnValue({
92
- isFile: ()=>true,
93
- size: 1234
94
- });
95
- mockMimeLookup.mockReturnValue('application/pdf');
96
- const stream = new FakeStream();
97
- mockCreateReadStream.mockReturnValue(stream);
98
- await middleware.use(buildReq('/storage/upload/file/report.pdf'), res, next);
99
- expect(res.set).toHaveBeenCalledWith(expect.objectContaining({
100
- 'Content-Type': 'application/pdf',
101
- 'Content-Length': 1234,
102
- 'Content-Disposition': 'inline'
103
- }));
104
- expect(stream.pipe).toHaveBeenCalledWith(res);
105
- });
106
- it('sets an attachment content-disposition with the filename for non-viewable mime types', async ()=>{
107
- const res = buildRes();
108
- mockExistsSync.mockReturnValue(true);
109
- mockStatSync.mockReturnValue({
110
- isFile: ()=>true,
111
- size: 500
112
- });
113
- mockMimeLookup.mockReturnValue('application/zip');
114
- mockCreateReadStream.mockReturnValue(new FakeStream());
115
- await middleware.use(buildReq('/storage/upload/file/archive.zip'), res, next);
116
- expect(res.set).toHaveBeenCalledWith(expect.objectContaining({
117
- 'Content-Disposition': expect.stringContaining('attachment; filename="archive.zip"')
118
- }));
119
- });
120
- it('always serves .svg files as image/svg+xml regardless of mime-types lookup', async ()=>{
121
- const res = buildRes();
122
- mockExistsSync.mockReturnValue(true);
123
- mockStatSync.mockReturnValue({
124
- isFile: ()=>true,
125
- size: 200
126
- });
127
- mockCreateReadStream.mockReturnValue(new FakeStream());
128
- await middleware.use(buildReq('/storage/upload/file/logo.svg'), res, next);
129
- expect(mockMimeLookup).not.toHaveBeenCalled();
130
- expect(res.set).toHaveBeenCalledWith(expect.objectContaining({
131
- 'Content-Type': 'image/svg+xml'
132
- }));
133
- });
134
- it('returns 404 when the resolved path exists but is not a regular file', async ()=>{
135
- const res = buildRes();
136
- mockExistsSync.mockReturnValue(true);
137
- mockStatSync.mockReturnValue({
138
- isFile: ()=>false
139
- });
140
- await middleware.use(buildReq('/storage/upload/file/some-dir'), res, next);
141
- expect(res.status).toHaveBeenCalledWith(404);
142
- expect(mockCreateReadStream).not.toHaveBeenCalled();
143
- });
144
- it('falls back to the configured local storage base path (strategy 2: path already prefixed with base)', async ()=>{
145
- const res = buildRes();
146
- const fallbackPath = (0, _path.join)('uploads', 'report.pdf');
147
- mockUploadService.getLocalStorageBasePath.mockResolvedValue('./uploads');
148
- mockExistsSync.mockImplementation((p)=>p === fallbackPath);
149
- mockStatSync.mockReturnValue({
150
- isFile: ()=>true,
151
- size: 10
152
- });
153
- mockMimeLookup.mockReturnValue('application/pdf');
154
- mockCreateReadStream.mockReturnValue(new FakeStream());
155
- await middleware.use(buildReq('/storage/upload/file/uploads/report.pdf'), res, next);
156
- expect(mockCreateReadStream).toHaveBeenCalledWith(fallbackPath);
157
- });
158
- it('falls back to the configured local storage base path (strategy 3: legacy path without base prefix)', async ()=>{
159
- const res = buildRes();
160
- const fallbackPath = (0, _path.join)('uploads', 'report.pdf');
161
- mockUploadService.getLocalStorageBasePath.mockResolvedValue('uploads');
162
- mockExistsSync.mockImplementation((p)=>p === fallbackPath);
163
- mockStatSync.mockReturnValue({
164
- isFile: ()=>true,
165
- size: 10
166
- });
167
- mockMimeLookup.mockReturnValue('application/pdf');
168
- mockCreateReadStream.mockReturnValue(new FakeStream());
169
- await middleware.use(buildReq('/storage/upload/file/report.pdf'), res, next);
170
- expect(mockCreateReadStream).toHaveBeenCalledWith(fallbackPath);
171
- });
172
- it('returns the underlying not-found message when no direct or fallback path resolves', async ()=>{
173
- const res = buildRes();
174
- mockExistsSync.mockReturnValue(false);
175
- mockUploadService.getLocalStorageBasePath.mockResolvedValue(null);
176
- await middleware.use(buildReq('/storage/upload/file/missing.pdf'), res, next);
177
- expect(res.status).toHaveBeenCalledWith(404);
178
- expect(res.json).toHaveBeenCalledWith(expect.objectContaining({
179
- message: expect.stringContaining('File not found'),
180
- statusCode: 404
181
- }));
182
- });
183
- it('sends a 404 if the file stream errors before headers are sent', async ()=>{
184
- const res = buildRes();
185
- mockExistsSync.mockReturnValue(true);
186
- mockStatSync.mockReturnValue({
187
- isFile: ()=>true,
188
- size: 10
189
- });
190
- mockMimeLookup.mockReturnValue('application/pdf');
191
- const stream = new FakeStream();
192
- mockCreateReadStream.mockReturnValue(stream);
193
- await middleware.use(buildReq('/storage/upload/file/report.pdf'), res, next);
194
- stream.emit('error', new Error('boom'));
195
- expect(res.status).toHaveBeenCalledWith(404);
196
- });
197
- it('destroys the stream when the response is closed early', async ()=>{
198
- const res = buildRes();
199
- mockExistsSync.mockReturnValue(true);
200
- mockStatSync.mockReturnValue({
201
- isFile: ()=>true,
202
- size: 10
203
- });
204
- mockMimeLookup.mockReturnValue('application/pdf');
205
- const stream = new FakeStream();
206
- mockCreateReadStream.mockReturnValue(stream);
207
- await middleware.use(buildReq('/storage/upload/file/report.pdf'), res, next);
208
- res.emitClose();
209
- expect(stream.destroy).toHaveBeenCalled();
210
- });
211
- });
@@ -1,18 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- _export_star(require("./file-serve.middleware"), 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,18 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- _export_star(require("./storage.module"), 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,165 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- Object.defineProperty(exports, "StorageModule", {
6
- enumerable: true,
7
- get: function() {
8
- return StorageModule;
9
- }
10
- });
11
- const _modules = require("@flusys/nestjs-shared/modules");
12
- const _common = require("@nestjs/common");
13
- const _middlewares = require("../middlewares");
14
- const _services = require("../services");
15
- const _storageconstants = require("../config/storage.constants");
16
- const _controllers = require("../controllers");
17
- const _filelocationenum = require("../enums/file-location.enum");
18
- const _providers = require("../providers");
19
- const _localprovider = require("../providers/local-provider");
20
- const _storageproviderconfigservice = require("../services/storage-provider-config.service");
21
- function _ts_decorate(decorators, target, key, desc) {
22
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
23
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
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
- return c > 3 && r && Object.defineProperty(target, key, r), r;
26
- }
27
- const logger = new _common.Logger('StorageModule');
28
- const CONTROLLERS = [
29
- _controllers.FileManagerController,
30
- _controllers.FolderController,
31
- _controllers.StorageConfigController,
32
- _controllers.UploadController
33
- ];
34
- const EXPORTED_SERVICES = [
35
- _services.StorageConfigService,
36
- _services.StorageDataSourceProvider,
37
- _services.FileManagerService,
38
- _services.FolderService,
39
- _storageproviderconfigservice.StorageProviderConfigService,
40
- _services.UploadService,
41
- _providers.StorageFactoryService
42
- ];
43
- // Register built-in provider
44
- _providers.StorageProviderRegistry.register(_filelocationenum.FileLocationEnum.LOCAL, _localprovider.LocalProvider);
45
- logger.log('Registered LocalProvider');
46
- // Register optional providers (skip if dependencies not installed)
47
- const OPTIONAL_PROVIDERS = [
48
- {
49
- location: _filelocationenum.FileLocationEnum.AWS,
50
- path: '../providers/s3-provider.optional',
51
- name: 'S3Provider',
52
- dep: '@aws-sdk/client-s3'
53
- },
54
- {
55
- location: _filelocationenum.FileLocationEnum.AZURE,
56
- path: '../providers/azure-provider.optional',
57
- name: 'AzureProvider',
58
- dep: '@azure/storage-blob'
59
- },
60
- {
61
- location: _filelocationenum.FileLocationEnum.SFTP,
62
- path: '../providers/sftp-provider.optional',
63
- name: 'SftpProvider',
64
- dep: 'ssh2-sftp-client'
65
- }
66
- ];
67
- for (const { location, path, name, dep } of OPTIONAL_PROVIDERS){
68
- try {
69
- _providers.StorageProviderRegistry.register(location, require(path)[name]);
70
- logger.log(`Registered ${name}`);
71
- } catch {
72
- logger.debug(`${name} not available (install ${dep} to enable)`);
73
- }
74
- }
75
- let StorageModule = class StorageModule {
76
- configure(consumer) {
77
- consumer.apply(_middlewares.FileServeMiddleware).forRoutes({
78
- path: 'storage/upload/file/*',
79
- method: _common.RequestMethod.GET
80
- });
81
- }
82
- static forRoot(options) {
83
- return {
84
- module: StorageModule,
85
- global: options.global ?? false,
86
- imports: [
87
- _modules.CacheModule,
88
- _modules.UtilsModule
89
- ],
90
- controllers: options.includeController !== false ? CONTROLLERS : [],
91
- providers: this.buildProviders(options),
92
- exports: EXPORTED_SERVICES
93
- };
94
- }
95
- static forRootAsync(options) {
96
- return {
97
- module: StorageModule,
98
- global: options.global ?? false,
99
- imports: [
100
- ...options.imports || [],
101
- _modules.CacheModule,
102
- _modules.UtilsModule
103
- ],
104
- controllers: options.includeController !== false ? CONTROLLERS : [],
105
- providers: [
106
- ...this.createAsyncProviders(options),
107
- ...this.buildProviders()
108
- ],
109
- exports: EXPORTED_SERVICES
110
- };
111
- }
112
- // ─── Private Helpers ──────────────────────────────────────────────────────────
113
- static buildProviders(options) {
114
- const providers = [
115
- ...EXPORTED_SERVICES,
116
- _middlewares.FileServeMiddleware
117
- ];
118
- if (options) {
119
- providers.unshift({
120
- provide: _storageconstants.STORAGE_MODULE_OPTIONS,
121
- useValue: options
122
- });
123
- }
124
- providers.push((0, _modules.createModuleEventsProvider)('storage', _storageconstants.STORAGE_MODULE_OPTIONS));
125
- return providers;
126
- }
127
- static createAsyncProviders(options) {
128
- if (options.useFactory) {
129
- return [
130
- {
131
- provide: _storageconstants.STORAGE_MODULE_OPTIONS,
132
- useFactory: async (...args)=>({
133
- ...options,
134
- config: await options.useFactory(...args)
135
- }),
136
- inject: options.inject || []
137
- }
138
- ];
139
- }
140
- const factoryClass = options.useClass || options.useExisting;
141
- if (!factoryClass) return [];
142
- const providers = [
143
- {
144
- provide: _storageconstants.STORAGE_MODULE_OPTIONS,
145
- useFactory: async (factory)=>({
146
- ...options,
147
- config: await factory.createStorageOptions()
148
- }),
149
- inject: [
150
- factoryClass
151
- ]
152
- }
153
- ];
154
- if (options.useClass) {
155
- providers.push({
156
- provide: options.useClass,
157
- useClass: options.useClass
158
- });
159
- }
160
- return providers;
161
- }
162
- };
163
- StorageModule = _ts_decorate([
164
- (0, _common.Module)({})
165
- ], StorageModule);