@flusys/nestjs-storage 1.1.0-beta → 2.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 (118) hide show
  1. package/README.md +148 -6
  2. package/cjs/config/index.js +0 -1
  3. package/cjs/config/storage.constants.js +0 -17
  4. package/cjs/controllers/file-manager.controller.js +44 -1
  5. package/cjs/controllers/folder.controller.js +44 -1
  6. package/cjs/controllers/storage-config.controller.js +44 -1
  7. package/cjs/controllers/upload.controller.js +18 -29
  8. package/cjs/docs/storage-swagger.config.js +24 -136
  9. package/cjs/dtos/file-manager.dto.js +70 -34
  10. package/cjs/dtos/folder.dto.js +15 -9
  11. package/cjs/dtos/storage-config.dto.js +4 -85
  12. package/cjs/dtos/upload.dto.js +24 -17
  13. package/cjs/entities/file-manager-with-company.entity.js +3 -4
  14. package/cjs/entities/file-manager.entity.js +71 -3
  15. package/cjs/entities/folder-with-company.entity.js +3 -4
  16. package/cjs/entities/folder.entity.js +19 -3
  17. package/cjs/entities/index.js +9 -10
  18. package/cjs/entities/storage-config-with-company.entity.js +3 -4
  19. package/cjs/entities/storage-config.entity.js +74 -3
  20. package/cjs/interfaces/index.js +0 -1
  21. package/cjs/middlewares/file-serve.middleware.js +113 -100
  22. package/cjs/modules/storage.module.js +82 -136
  23. package/cjs/providers/azure-provider.optional.js +10 -38
  24. package/cjs/providers/local-provider.js +38 -31
  25. package/cjs/providers/s3-provider.optional.js +19 -40
  26. package/cjs/providers/storage-factory.service.js +54 -99
  27. package/cjs/providers/storage-provider.registry.js +8 -18
  28. package/cjs/services/file-manager.service.js +238 -323
  29. package/cjs/services/folder.service.js +8 -11
  30. package/cjs/services/index.js +1 -0
  31. package/cjs/{config → services}/storage-config.service.js +32 -76
  32. package/cjs/services/storage-datasource.provider.js +16 -26
  33. package/cjs/services/storage-provider-config.service.js +15 -37
  34. package/cjs/services/upload.service.js +72 -88
  35. package/cjs/utils/file-validator.util.js +458 -0
  36. package/cjs/utils/image-compressor.util.js +3 -8
  37. package/config/index.d.ts +0 -1
  38. package/config/storage.constants.d.ts +0 -8
  39. package/controllers/upload.controller.d.ts +3 -6
  40. package/dtos/file-manager.dto.d.ts +12 -5
  41. package/dtos/folder.dto.d.ts +5 -5
  42. package/dtos/storage-config.dto.d.ts +7 -13
  43. package/entities/file-manager-with-company.entity.d.ts +2 -2
  44. package/entities/file-manager.entity.d.ts +12 -2
  45. package/entities/folder-with-company.entity.d.ts +2 -2
  46. package/entities/folder.entity.d.ts +4 -2
  47. package/entities/index.d.ts +3 -4
  48. package/entities/storage-config-with-company.entity.d.ts +2 -2
  49. package/entities/storage-config.entity.d.ts +8 -2
  50. package/fesm/config/index.js +0 -1
  51. package/fesm/config/storage.constants.js +0 -8
  52. package/fesm/controllers/file-manager.controller.js +45 -2
  53. package/fesm/controllers/folder.controller.js +45 -2
  54. package/fesm/controllers/storage-config.controller.js +45 -2
  55. package/fesm/controllers/upload.controller.js +19 -30
  56. package/fesm/docs/storage-swagger.config.js +27 -142
  57. package/fesm/dtos/file-manager.dto.js +71 -35
  58. package/fesm/dtos/folder.dto.js +16 -10
  59. package/fesm/dtos/storage-config.dto.js +8 -95
  60. package/fesm/dtos/upload.dto.js +25 -19
  61. package/fesm/entities/file-manager-with-company.entity.js +3 -4
  62. package/fesm/entities/file-manager.entity.js +72 -4
  63. package/fesm/entities/folder-with-company.entity.js +3 -4
  64. package/fesm/entities/folder.entity.js +20 -4
  65. package/fesm/entities/index.js +5 -13
  66. package/fesm/entities/storage-config-with-company.entity.js +3 -4
  67. package/fesm/entities/storage-config.entity.js +75 -4
  68. package/fesm/interfaces/index.js +0 -1
  69. package/fesm/interfaces/storage-config.interface.js +1 -3
  70. package/fesm/middlewares/file-serve.middleware.js +114 -101
  71. package/fesm/modules/storage.module.js +83 -136
  72. package/fesm/providers/azure-provider.optional.js +11 -42
  73. package/fesm/providers/local-provider.js +38 -31
  74. package/fesm/providers/s3-provider.optional.js +20 -44
  75. package/fesm/providers/storage-factory.service.js +52 -97
  76. package/fesm/providers/storage-provider.registry.js +10 -20
  77. package/fesm/services/file-manager.service.js +237 -322
  78. package/fesm/services/folder.service.js +6 -9
  79. package/fesm/services/index.js +1 -0
  80. package/fesm/{config → services}/storage-config.service.js +32 -76
  81. package/fesm/services/storage-datasource.provider.js +16 -26
  82. package/fesm/services/storage-provider-config.service.js +13 -35
  83. package/fesm/services/upload.service.js +71 -87
  84. package/fesm/utils/file-validator.util.js +451 -0
  85. package/fesm/utils/image-compressor.util.js +3 -8
  86. package/interfaces/file-manager.interface.d.ts +7 -4
  87. package/interfaces/index.d.ts +0 -1
  88. package/interfaces/storage-config.interface.d.ts +0 -20
  89. package/interfaces/storage-module-options.interface.d.ts +0 -5
  90. package/middlewares/file-serve.middleware.d.ts +9 -1
  91. package/modules/storage.module.d.ts +1 -2
  92. package/package.json +6 -6
  93. package/providers/azure-provider.optional.d.ts +8 -6
  94. package/providers/local-provider.d.ts +2 -7
  95. package/providers/s3-provider.optional.d.ts +9 -7
  96. package/providers/storage-factory.service.d.ts +8 -9
  97. package/providers/storage-provider.registry.d.ts +4 -4
  98. package/services/file-manager.service.d.ts +23 -16
  99. package/services/folder.service.d.ts +4 -4
  100. package/services/index.d.ts +1 -0
  101. package/services/storage-config.service.d.ts +24 -0
  102. package/services/storage-datasource.provider.d.ts +3 -4
  103. package/services/storage-provider-config.service.d.ts +4 -4
  104. package/services/upload.service.d.ts +3 -2
  105. package/utils/file-validator.util.d.ts +19 -0
  106. package/cjs/entities/file-manager-base.entity.js +0 -115
  107. package/cjs/entities/folder-base.entity.js +0 -55
  108. package/cjs/entities/storage-config-base.entity.js +0 -93
  109. package/cjs/interfaces/file-upload-response.interface.js +0 -4
  110. package/config/storage-config.service.d.ts +0 -22
  111. package/entities/file-manager-base.entity.d.ts +0 -13
  112. package/entities/folder-base.entity.d.ts +0 -5
  113. package/entities/storage-config-base.entity.d.ts +0 -9
  114. package/fesm/entities/file-manager-base.entity.js +0 -108
  115. package/fesm/entities/folder-base.entity.js +0 -48
  116. package/fesm/entities/storage-config-base.entity.js +0 -83
  117. package/fesm/interfaces/file-upload-response.interface.js +0 -1
  118. package/interfaces/file-upload-response.interface.d.ts +0 -6
@@ -76,127 +76,140 @@ function _ts_decorate(decorators, target, key, desc) {
76
76
  function _ts_metadata(k, v) {
77
77
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
78
78
  }
79
+ function _ts_param(paramIndex, decorator) {
80
+ return function(target, key) {
81
+ decorator(target, key, paramIndex);
82
+ };
83
+ }
84
+ /** MIME type prefixes that can be displayed inline in browser */ const VIEWABLE_TYPE_PREFIXES = [
85
+ 'image/',
86
+ 'video/',
87
+ 'audio/',
88
+ 'text/',
89
+ 'application/pdf',
90
+ 'application/json',
91
+ 'application/xml'
92
+ ];
79
93
  let FileServeMiddleware = class FileServeMiddleware {
80
94
  async use(req, res, next) {
81
95
  try {
82
- const uploadDir = process.cwd();
83
- // Extract path after /storage/upload/file/
84
- const urlPath = req.path || req.url;
85
- const match = urlPath.match(/\/storage\/upload\/file\/(.+)/);
86
- const normalizedPath = match ? match[1] : '';
87
- if (!normalizedPath) {
88
- throw new _common.NotFoundException('File path is required');
89
- }
90
- this.logger.debug(`Attempting to serve file, normalizedPath: ${normalizedPath}`);
91
- // Get the local storage basePath for fallback lookups
92
- const basePath = await this.uploadService.getLocalStorageBasePath();
93
- const normalizedBasePath = basePath ? basePath.replace(/^\.\//, '').replace(/\/$/, '') : null;
94
- // Strategy 1: Try path relative to CWD (new format: key includes basePath)
95
- let fullPath = (0, _path.join)(uploadDir, normalizedPath);
96
- let resolvedPath = (0, _path.resolve)(fullPath);
97
- this.logger.debug(`Strategy 1 - CWD relative: ${fullPath}`);
98
- // Validate path is inside the project directory (prevent path traversal)
99
- if (!resolvedPath.startsWith(uploadDir)) {
100
- throw new _common.NotFoundException('Invalid file path');
101
- }
102
- // Check if file exists
103
- if (!(0, _fs.existsSync)(fullPath)) {
104
- // Strategy 2: If key already starts with basePath, try using basePath directly
105
- if (basePath && normalizedBasePath) {
106
- const pathStartsWithBase = normalizedPath.startsWith(normalizedBasePath + '/') || normalizedPath.startsWith(normalizedBasePath);
107
- if (pathStartsWithBase) {
108
- const remainingPath = normalizedPath.substring(normalizedBasePath.length).replace(/^\//, '');
109
- const fallbackPath = (0, _path.join)(basePath, remainingPath);
110
- this.logger.debug(`Strategy 2 - basePath + remaining: ${fallbackPath}`);
111
- if ((0, _fs.existsSync)(fallbackPath)) {
112
- fullPath = fallbackPath;
113
- resolvedPath = (0, _path.resolve)(fullPath);
114
- }
115
- } else {
116
- // Old format: key doesn't include basePath, prepend it
117
- const fallbackPath = (0, _path.join)(basePath, normalizedPath);
118
- this.logger.debug(`Strategy 3 - basePath + full key (old format): ${fallbackPath}`);
119
- if ((0, _fs.existsSync)(fallbackPath)) {
120
- fullPath = fallbackPath;
121
- resolvedPath = (0, _path.resolve)(fullPath);
122
- }
123
- }
124
- }
125
- // Final check if file was found
126
- if (!(0, _fs.existsSync)(fullPath)) {
127
- throw new _common.NotFoundException(`File not found: ${normalizedPath}`);
128
- }
129
- }
96
+ const normalizedPath = this.extractFilePath(req);
97
+ const fullPath = await this.resolveFilePath(normalizedPath);
130
98
  const stats = (0, _fs.statSync)(fullPath);
131
99
  if (!stats.isFile()) {
132
100
  throw new _common.NotFoundException(`Not a file: ${normalizedPath}`);
133
101
  }
134
- // Enhanced MIME type detection
135
- let mimeType = _mimetypes.lookup(fullPath) || 'application/octet-stream';
136
- // Special handling for SVG files
137
- if (fullPath.toLowerCase().endsWith('.svg')) {
138
- mimeType = 'image/svg+xml';
139
- }
140
- // Determine if file should be displayed inline or downloaded
141
- const viewableTypes = [
142
- 'image/',
143
- 'video/',
144
- 'audio/',
145
- 'text/',
146
- 'application/pdf',
147
- 'application/json',
148
- 'application/xml'
149
- ];
150
- const isViewable = viewableTypes.some((type)=>mimeType.startsWith(type));
151
- const contentDisposition = isViewable ? 'inline' : `attachment; filename="${encodeURIComponent(normalizedPath.split('/').pop() || 'download')}"`;
152
- // Set headers
153
- res.set({
154
- 'Content-Type': mimeType,
155
- 'Content-Length': stats.size,
156
- 'Content-Disposition': contentDisposition,
157
- 'Cache-Control': 'public, max-age=3600',
158
- 'Accept-Ranges': 'bytes',
159
- 'Cross-Origin-Resource-Policy': 'cross-origin',
160
- 'Access-Control-Allow-Origin': '*',
161
- 'X-Content-Type-Options': 'nosniff'
162
- });
163
- // Stream the file
164
- const stream = (0, _fs.createReadStream)(fullPath);
165
- stream.pipe(res);
166
- stream.on('error', (err)=>{
167
- this.logger.error('File stream error', err);
168
- if (!res.headersSent) {
169
- res.status(404).json({
170
- message: `Failed to serve file: ${normalizedPath}`,
171
- error: 'Not Found',
172
- statusCode: 404
173
- });
174
- }
175
- });
176
- // Handle client disconnection
177
- res.on('close', ()=>{
178
- stream.destroy();
179
- });
102
+ const mimeType = this.getMimeType(fullPath);
103
+ this.setResponseHeaders(res, stats.size, mimeType, normalizedPath);
104
+ this.streamFile(res, fullPath, normalizedPath);
180
105
  } catch (error) {
181
- this.logger.error('File retrieval error:', error);
106
+ this.sendErrorResponse(res, error);
107
+ }
108
+ }
109
+ /** Extract and validate file path from request URL */ extractFilePath(req) {
110
+ const urlPath = req.path || req.url;
111
+ const match = urlPath.match(/\/storage\/upload\/file\/(.+)/);
112
+ const normalizedPath = match ? match[1] : '';
113
+ if (!normalizedPath) {
114
+ throw new _common.NotFoundException('File path is required');
115
+ }
116
+ this.logger.debug(`Attempting to serve file, normalizedPath: ${normalizedPath}`);
117
+ return normalizedPath;
118
+ }
119
+ /** Resolve file path using multiple fallback strategies */ async resolveFilePath(normalizedPath) {
120
+ // Strategy 1: Path relative to CWD (new format: key includes basePath)
121
+ let fullPath = (0, _path.join)(this.uploadDir, normalizedPath);
122
+ const resolvedPath = (0, _path.resolve)(fullPath);
123
+ this.logger.debug(`Strategy 1 - CWD relative: ${fullPath}`);
124
+ // Prevent path traversal attacks
125
+ if (!resolvedPath.startsWith(this.uploadDir)) {
126
+ throw new _common.NotFoundException('Invalid file path');
127
+ }
128
+ if ((0, _fs.existsSync)(fullPath)) {
129
+ return fullPath;
130
+ }
131
+ // Try fallback strategies with basePath
132
+ const basePath = await this.uploadService.getLocalStorageBasePath();
133
+ if (basePath) {
134
+ const fallbackPath = this.tryFallbackPaths(normalizedPath, basePath);
135
+ if (fallbackPath) {
136
+ return fallbackPath;
137
+ }
138
+ }
139
+ throw new _common.NotFoundException(`File not found: ${normalizedPath}`);
140
+ }
141
+ /** Try basePath-based fallback strategies */ tryFallbackPaths(normalizedPath, basePath) {
142
+ const normalizedBasePath = basePath.replace(/^\.\//, '').replace(/\/$/, '');
143
+ const pathStartsWithBase = normalizedPath.startsWith(normalizedBasePath + '/') || normalizedPath.startsWith(normalizedBasePath);
144
+ if (pathStartsWithBase) {
145
+ // Strategy 2: basePath + remaining path
146
+ const remainingPath = normalizedPath.substring(normalizedBasePath.length).replace(/^\//, '');
147
+ const fallbackPath = (0, _path.join)(basePath, remainingPath);
148
+ this.logger.debug(`Strategy 2 - basePath + remaining: ${fallbackPath}`);
149
+ if ((0, _fs.existsSync)(fallbackPath)) return fallbackPath;
150
+ } else {
151
+ // Strategy 3: Old format - prepend basePath
152
+ const fallbackPath = (0, _path.join)(basePath, normalizedPath);
153
+ this.logger.debug(`Strategy 3 - basePath + full key (old format): ${fallbackPath}`);
154
+ if ((0, _fs.existsSync)(fallbackPath)) return fallbackPath;
155
+ }
156
+ return null;
157
+ }
158
+ /** Get MIME type with SVG special handling */ getMimeType(fullPath) {
159
+ if (fullPath.toLowerCase().endsWith('.svg')) {
160
+ return 'image/svg+xml';
161
+ }
162
+ return _mimetypes.lookup(fullPath) || 'application/octet-stream';
163
+ }
164
+ /** Set response headers for file serving */ setResponseHeaders(res, size, mimeType, normalizedPath) {
165
+ const isViewable = VIEWABLE_TYPE_PREFIXES.some((type)=>mimeType.startsWith(type));
166
+ const filename = normalizedPath.split('/').pop() || 'download';
167
+ const contentDisposition = isViewable ? 'inline' : `attachment; filename="${encodeURIComponent(filename)}"`;
168
+ res.set({
169
+ 'Content-Type': mimeType,
170
+ 'Content-Length': size,
171
+ 'Content-Disposition': contentDisposition,
172
+ 'Cache-Control': 'public, max-age=3600',
173
+ 'Accept-Ranges': 'bytes',
174
+ 'Cross-Origin-Resource-Policy': 'cross-origin',
175
+ 'Access-Control-Allow-Origin': '*',
176
+ 'X-Content-Type-Options': 'nosniff'
177
+ });
178
+ }
179
+ /** Stream file to response with error handling */ streamFile(res, fullPath, normalizedPath) {
180
+ const stream = (0, _fs.createReadStream)(fullPath);
181
+ stream.pipe(res);
182
+ stream.on('error', (err)=>{
183
+ this.logger.error('File stream error', err);
182
184
  if (!res.headersSent) {
183
- res.status(404).json({
184
- message: error instanceof _common.NotFoundException ? error.message : `File not found`,
185
- error: 'Not Found',
186
- statusCode: 404
187
- });
185
+ this.sendErrorResponse(res, new _common.NotFoundException(`Failed to serve file: ${normalizedPath}`));
188
186
  }
187
+ });
188
+ res.on('close', ()=>stream.destroy());
189
+ }
190
+ /** Send consistent error response */ sendErrorResponse(res, error) {
191
+ this.logger.error('File retrieval error:', error);
192
+ if (!res.headersSent) {
193
+ const message = error instanceof _common.NotFoundException ? error.message : 'File not found';
194
+ res.status(404).json({
195
+ message,
196
+ error: 'Not Found',
197
+ statusCode: 404
198
+ });
189
199
  }
190
200
  }
191
201
  constructor(uploadService){
192
202
  _define_property(this, "uploadService", void 0);
193
203
  _define_property(this, "logger", void 0);
204
+ _define_property(this, "uploadDir", void 0);
194
205
  this.uploadService = uploadService;
195
206
  this.logger = new _common.Logger(FileServeMiddleware.name);
207
+ this.uploadDir = process.cwd();
196
208
  }
197
209
  };
198
210
  FileServeMiddleware = _ts_decorate([
199
211
  (0, _common.Injectable)(),
212
+ _ts_param(0, (0, _common.Inject)(_uploadservice.UploadService)),
200
213
  _ts_metadata("design:type", Function),
201
214
  _ts_metadata("design:paramtypes", [
202
215
  typeof _uploadservice.UploadService === "undefined" ? Object : _uploadservice.UploadService
@@ -11,13 +11,12 @@ Object.defineProperty(exports, "StorageModule", {
11
11
  const _modules = require("@flusys/nestjs-shared/modules");
12
12
  const _common = require("@nestjs/common");
13
13
  const _middlewares = require("../middlewares");
14
- const _config = require("../config");
14
+ const _services = require("../services");
15
15
  const _storageconstants = require("../config/storage.constants");
16
16
  const _controllers = require("../controllers");
17
17
  const _filelocationenum = require("../enums/file-location.enum");
18
18
  const _providers = require("../providers");
19
19
  const _localprovider = require("../providers/local-provider");
20
- const _services = require("../services");
21
20
  const _storageproviderconfigservice = require("../services/storage-provider-config.service");
22
21
  function _ts_decorate(decorators, target, key, desc) {
23
22
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -25,48 +24,62 @@ function _ts_decorate(decorators, target, key, desc) {
25
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;
26
25
  return c > 3 && r && Object.defineProperty(target, key, r), r;
27
26
  }
28
- // Auto-register built-in storage providers
27
+ // ─── Module Constants ─────────────────────────────────────────────────────────
29
28
  const logger = new _common.Logger('StorageModule');
30
- // Register LocalProvider (always available - uses Node.js built-in fs)
29
+ const CONTROLLERS = [
30
+ _controllers.FileManagerController,
31
+ _controllers.FolderController,
32
+ _controllers.StorageConfigController,
33
+ _controllers.UploadController
34
+ ];
35
+ const EXPORTED_SERVICES = [
36
+ _services.StorageConfigService,
37
+ _services.StorageDataSourceProvider,
38
+ _services.FileManagerService,
39
+ _services.FolderService,
40
+ _storageproviderconfigservice.StorageProviderConfigService,
41
+ _services.UploadService,
42
+ _providers.StorageFactoryService
43
+ ];
44
+ // ─── Provider Registration ────────────────────────────────────────────────────
31
45
  _providers.StorageProviderRegistry.register(_filelocationenum.FileLocationEnum.LOCAL, _localprovider.LocalProvider);
32
46
  logger.log('Registered LocalProvider');
33
- // Try to register optional providers (only if dependencies are installed)
34
- try {
35
- const { S3Provider } = require('../providers/s3-provider.optional');
36
- _providers.StorageProviderRegistry.register(_filelocationenum.FileLocationEnum.AWS, S3Provider);
37
- logger.log('Registered S3Provider');
38
- } catch {
39
- logger.debug('S3Provider not available (install @aws-sdk/client-s3 to enable)');
40
- }
41
- try {
42
- const { AzureProvider } = require('../providers/azure-provider.optional');
43
- _providers.StorageProviderRegistry.register(_filelocationenum.FileLocationEnum.AZURE, AzureProvider);
44
- logger.log('Registered AzureProvider');
45
- } catch {
46
- logger.debug('AzureProvider not available (install @azure/storage-blob to enable)');
47
- }
48
- try {
49
- const { SftpProvider } = require('../providers/sftp-provider.optional');
50
- _providers.StorageProviderRegistry.register(_filelocationenum.FileLocationEnum.SFTP, SftpProvider);
51
- logger.log('Registered SftpProvider');
52
- } catch {
53
- logger.debug('SftpProvider not available (install ssh2-sftp-client to enable)');
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
+ }
54
74
  }
55
75
  let StorageModule = class StorageModule {
56
- /**
57
- * Configure middleware for file serving
58
- * This bypasses path-to-regexp issues with wildcard routes
59
- */ configure(consumer) {
76
+ configure(consumer) {
60
77
  consumer.apply(_middlewares.FileServeMiddleware).forRoutes({
61
78
  path: 'storage/upload/file/*',
62
79
  method: _common.RequestMethod.GET
63
80
  });
64
81
  }
65
- /**
66
- * Register StorageModule synchronously
67
- */ static forRoot(options) {
68
- const controllers = this.getControllers(options);
69
- const providers = this.getProviders(options);
82
+ static forRoot(options) {
70
83
  return {
71
84
  module: StorageModule,
72
85
  global: options.global ?? false,
@@ -74,24 +87,12 @@ let StorageModule = class StorageModule {
74
87
  _modules.CacheModule,
75
88
  _modules.UtilsModule
76
89
  ],
77
- controllers: options.includeController !== false ? controllers : [],
78
- providers,
79
- exports: [
80
- _config.StorageConfigService,
81
- _services.StorageDataSourceProvider,
82
- _services.FileManagerService,
83
- _services.FolderService,
84
- _storageproviderconfigservice.StorageProviderConfigService,
85
- _services.UploadService,
86
- _providers.StorageFactoryService
87
- ]
90
+ controllers: options.includeController !== false ? CONTROLLERS : [],
91
+ providers: this.buildProviders(options),
92
+ exports: EXPORTED_SERVICES
88
93
  };
89
94
  }
90
- /**
91
- * Register StorageModule asynchronously
92
- */ static forRootAsync(options) {
93
- const controllers = this.getControllers(options);
94
- const asyncProviders = this.createAsyncProviders(options);
95
+ static forRootAsync(options) {
95
96
  return {
96
97
  module: StorageModule,
97
98
  global: options.global ?? false,
@@ -100,53 +101,21 @@ let StorageModule = class StorageModule {
100
101
  _modules.CacheModule,
101
102
  _modules.UtilsModule
102
103
  ],
103
- controllers: options.includeController !== false ? controllers : [],
104
- // Pass false to exclude STORAGE_MODULE_OPTIONS - it's already in asyncProviders
104
+ controllers: options.includeController !== false ? CONTROLLERS : [],
105
105
  providers: [
106
- ...asyncProviders,
107
- ...this.getProviders(options, false)
106
+ ...this.createAsyncProviders(options),
107
+ ...this.buildProviders()
108
108
  ],
109
- exports: [
110
- _config.StorageConfigService,
111
- _services.StorageDataSourceProvider,
112
- _services.FileManagerService,
113
- _services.FolderService,
114
- _storageproviderconfigservice.StorageProviderConfigService,
115
- _services.UploadService,
116
- _providers.StorageFactoryService
117
- ]
109
+ exports: EXPORTED_SERVICES
118
110
  };
119
111
  }
120
- // Private Helper Methods
121
- /** Get controllers (all controllers always loaded) */ static getControllers(options) {
122
- return [
123
- _controllers.FileManagerController,
124
- _controllers.FolderController,
125
- _controllers.StorageConfigController,
126
- _controllers.UploadController
127
- ];
128
- }
129
- /**
130
- * NOTE: Repository providers removed - services now use StorageDataSourceProvider directly
131
- * This ensures dynamic entity loading based on runtime configuration
132
- */ /**
133
- * Get providers (all providers always loaded)
134
- * @param options Module options
135
- * @param includeOptionsProvider Whether to include the STORAGE_MODULE_OPTIONS provider (false for async registration)
136
- */ static getProviders(options, includeOptionsProvider = true) {
112
+ // ─── Private Helpers ──────────────────────────────────────────────────────────
113
+ static buildProviders(options) {
137
114
  const providers = [
138
- _config.StorageConfigService,
139
- _services.StorageDataSourceProvider,
140
- _services.FileManagerService,
141
- _services.FolderService,
142
- _storageproviderconfigservice.StorageProviderConfigService,
143
- _services.UploadService,
144
- _providers.StorageFactoryService,
115
+ ...EXPORTED_SERVICES,
145
116
  _middlewares.FileServeMiddleware
146
117
  ];
147
- // Only include options provider for sync registration
148
- // For async registration, createAsyncProviders handles it
149
- if (includeOptionsProvider) {
118
+ if (options) {
150
119
  providers.unshift({
151
120
  provide: _storageconstants.STORAGE_MODULE_OPTIONS,
152
121
  useValue: options
@@ -154,63 +123,40 @@ let StorageModule = class StorageModule {
154
123
  }
155
124
  return providers;
156
125
  }
157
- /**
158
- * Create async providers for forRootAsync
159
- */ static createAsyncProviders(options) {
126
+ static createAsyncProviders(options) {
160
127
  if (options.useFactory) {
161
128
  return [
162
129
  {
163
130
  provide: _storageconstants.STORAGE_MODULE_OPTIONS,
164
- useFactory: async (...args)=>{
165
- const config = await options.useFactory(...args);
166
- return {
131
+ useFactory: async (...args)=>({
167
132
  ...options,
168
- config
169
- };
170
- },
133
+ config: await options.useFactory(...args)
134
+ }),
171
135
  inject: options.inject || []
172
136
  }
173
137
  ];
174
138
  }
139
+ const factoryClass = options.useClass || options.useExisting;
140
+ if (!factoryClass) return [];
141
+ const providers = [
142
+ {
143
+ provide: _storageconstants.STORAGE_MODULE_OPTIONS,
144
+ useFactory: async (factory)=>({
145
+ ...options,
146
+ config: await factory.createStorageOptions()
147
+ }),
148
+ inject: [
149
+ factoryClass
150
+ ]
151
+ }
152
+ ];
175
153
  if (options.useClass) {
176
- return [
177
- {
178
- provide: _storageconstants.STORAGE_MODULE_OPTIONS,
179
- useFactory: async (optionsFactory)=>{
180
- const config = await optionsFactory.createStorageOptions();
181
- return {
182
- ...options,
183
- config
184
- };
185
- },
186
- inject: [
187
- options.useClass
188
- ]
189
- },
190
- {
191
- provide: options.useClass,
192
- useClass: options.useClass
193
- }
194
- ];
195
- }
196
- if (options.useExisting) {
197
- return [
198
- {
199
- provide: _storageconstants.STORAGE_MODULE_OPTIONS,
200
- useFactory: async (optionsFactory)=>{
201
- const config = await optionsFactory.createStorageOptions();
202
- return {
203
- ...options,
204
- config
205
- };
206
- },
207
- inject: [
208
- options.useExisting
209
- ]
210
- }
211
- ];
154
+ providers.push({
155
+ provide: options.useClass,
156
+ useClass: options.useClass
157
+ });
212
158
  }
213
- return [];
159
+ return providers;
214
160
  }
215
161
  };
216
162
  StorageModule = _ts_decorate([
@@ -1,17 +1,6 @@
1
1
  /**
2
- * OPTIONAL Azure Blob Storage Provider
3
- *
4
- * This provider requires @azure/storage-blob package to be installed.
5
- * Only import this if you need Azure Blob storage.
6
- *
7
- * Installation:
8
- * npm install @azure/storage-blob
9
- *
10
- * Usage:
11
- * import { AzureProvider } from '@flusys/nestjs-storage/providers/azure-provider.optional';
12
- * import { StorageProviderRegistry, FileLocationEnum } from '@flusys/nestjs-storage';
13
- *
14
- * StorageProviderRegistry.register(FileLocationEnum.AZURE, AzureProvider);
2
+ * Optional Azure Blob Storage Provider
3
+ * Requires: npm install @azure/storage-blob
15
4
  */ "use strict";
16
5
  Object.defineProperty(exports, "__esModule", {
17
6
  value: true
@@ -22,9 +11,9 @@ Object.defineProperty(exports, "AzureProvider", {
22
11
  return AzureProvider;
23
12
  }
24
13
  });
25
- const _imagecompressorutil = require("../utils/image-compressor.util");
26
14
  const _common = require("@nestjs/common");
27
15
  const _uuid = require("uuid");
16
+ const _imagecompressorutil = require("../utils/image-compressor.util");
28
17
  function _define_property(obj, key, value) {
29
18
  if (key in obj) {
30
19
  Object.defineProperty(obj, key, {
@@ -80,37 +69,23 @@ function _interop_require_wildcard(obj, nodeInterop) {
80
69
  return newObj;
81
70
  }
82
71
  let AzureProvider = class AzureProvider {
83
- /**
84
- * Initialize Azure provider with configuration
85
- */ async initialize(config) {
72
+ async initialize(config) {
86
73
  try {
87
- // Dynamic import - only loads Azure SDK if this provider is used
88
- const { BlobServiceClient } = await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("@azure/storage-blob")));
74
+ const { BlobServiceClient, StorageSharedKeyCredential } = await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("@azure/storage-blob")));
89
75
  this.accountName = config.accountName;
90
76
  this.containerName = config.containerName;
91
- // Create BlobServiceClient
92
- if (config.connectionString) {
93
- this.blobServiceClient = BlobServiceClient.fromConnectionString(config.connectionString);
94
- } else {
95
- const { StorageSharedKeyCredential } = await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("@azure/storage-blob")));
96
- const sharedKeyCredential = new StorageSharedKeyCredential(config.accountName, config.accountKey);
97
- this.blobServiceClient = new BlobServiceClient(`https://${config.accountName}.blob.core.windows.net`, sharedKeyCredential);
98
- }
99
- // Get container client
77
+ this.blobServiceClient = config.connectionString ? BlobServiceClient.fromConnectionString(config.connectionString) : new BlobServiceClient(`https://${config.accountName}.blob.core.windows.net`, new StorageSharedKeyCredential(config.accountName, config.accountKey));
100
78
  this.containerClient = this.blobServiceClient.getContainerClient(config.containerName);
101
- // Ensure container exists
102
79
  await this.containerClient.createIfNotExists();
103
80
  this.logger.log(`Azure Provider initialized: account=${config.accountName}, container=${config.containerName}`);
104
- } catch (_error) {
105
- this.logger.error('Failed to initialize AzureProvider. Make sure @azure/storage-blob is installed.');
106
- throw new Error('Azure Storage SDK not found. Install it with: npm install @azure/storage-blob');
81
+ } catch {
82
+ throw new Error('Azure Storage SDK not found. Install: npm install @azure/storage-blob');
107
83
  }
108
84
  }
109
85
  async uploadFile(file, options) {
110
86
  let processedBuffer = file.buffer;
111
87
  let contentType = file.mimetype;
112
88
  const fileName = `${(0, _uuid.v4)()}-${file.originalname}`;
113
- // Compress image if needed
114
89
  if (options.compress && file.mimetype.startsWith('image/')) {
115
90
  const result = await _imagecompressorutil.ImageCompressor.compress(file.buffer, file.mimetype, {
116
91
  maxWidth: options.maxWidth,
@@ -122,9 +97,7 @@ let AzureProvider = class AzureProvider {
122
97
  contentType = result.format;
123
98
  }
124
99
  const blobName = options.folderPath ? `${options.folderPath}/${fileName}` : fileName;
125
- const blockBlobClient = this.containerClient.getBlockBlobClient(blobName);
126
- // Upload to Azure
127
- await blockBlobClient.upload(processedBuffer, processedBuffer.length, {
100
+ await this.containerClient.getBlockBlobClient(blobName).upload(processedBuffer, processedBuffer.length, {
128
101
  blobHTTPHeaders: {
129
102
  blobContentType: contentType
130
103
  }
@@ -140,8 +113,7 @@ let AzureProvider = class AzureProvider {
140
113
  return Promise.all(files.map((file)=>this.uploadFile(file, options)));
141
114
  }
142
115
  async deleteFile(key) {
143
- const blockBlobClient = this.containerClient.getBlockBlobClient(key);
144
- await blockBlobClient.deleteIfExists();
116
+ await this.containerClient.getBlockBlobClient(key).deleteIfExists();
145
117
  this.logger.log(`Deleted file from Azure: ${key}`);
146
118
  }
147
119
  async deleteMultipleFiles(keys) {