@flusys/nestjs-storage 0.1.0-beta.2 → 1.0.0-beta

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 (57) hide show
  1. package/README.md +3 -7
  2. package/cjs/config/storage-config.service.js +31 -0
  3. package/cjs/controllers/file-manager.controller.js +8 -6
  4. package/cjs/controllers/folder.controller.js +3 -4
  5. package/cjs/controllers/storage-config.controller.js +3 -4
  6. package/cjs/controllers/upload.controller.js +22 -169
  7. package/cjs/dtos/file-manager.dto.js +36 -2
  8. package/cjs/dtos/upload.dto.js +16 -0
  9. package/cjs/middlewares/file-serve.middleware.js +204 -0
  10. package/cjs/middlewares/index.js +18 -0
  11. package/cjs/modules/storage.module.js +58 -14
  12. package/cjs/providers/azure-provider.optional.js +1 -2
  13. package/cjs/providers/local-provider.js +43 -11
  14. package/cjs/providers/storage-factory.service.js +49 -5
  15. package/cjs/services/file-manager.service.js +134 -9
  16. package/cjs/services/folder.service.js +17 -48
  17. package/cjs/services/storage-datasource.provider.js +10 -16
  18. package/cjs/services/storage-provider-config.service.js +26 -32
  19. package/cjs/services/upload.service.js +135 -24
  20. package/cjs/utils/image-compressor.util.js +43 -5
  21. package/config/storage-config.service.d.ts +2 -0
  22. package/controllers/file-manager.controller.d.ts +1 -1
  23. package/controllers/upload.controller.d.ts +5 -4
  24. package/dtos/file-manager.dto.d.ts +4 -0
  25. package/dtos/upload.dto.d.ts +2 -0
  26. package/fesm/config/storage-config.service.js +31 -0
  27. package/fesm/controllers/file-manager.controller.js +8 -6
  28. package/fesm/controllers/folder.controller.js +5 -6
  29. package/fesm/controllers/storage-config.controller.js +5 -6
  30. package/fesm/controllers/upload.controller.js +25 -131
  31. package/fesm/dtos/file-manager.dto.js +36 -2
  32. package/fesm/dtos/upload.dto.js +16 -0
  33. package/fesm/middlewares/file-serve.middleware.js +153 -0
  34. package/fesm/middlewares/index.js +1 -0
  35. package/fesm/modules/storage.module.js +60 -16
  36. package/fesm/providers/azure-provider.optional.js +1 -2
  37. package/fesm/providers/local-provider.js +43 -11
  38. package/fesm/providers/storage-factory.service.js +50 -6
  39. package/fesm/services/file-manager.service.js +134 -9
  40. package/fesm/services/folder.service.js +18 -49
  41. package/fesm/services/storage-datasource.provider.js +10 -16
  42. package/fesm/services/storage-provider-config.service.js +26 -32
  43. package/fesm/services/upload.service.js +135 -24
  44. package/fesm/utils/image-compressor.util.js +3 -1
  45. package/interfaces/file-manager.interface.d.ts +2 -0
  46. package/interfaces/storage-module-options.interface.d.ts +2 -0
  47. package/interfaces/storage-provider.interface.d.ts +2 -0
  48. package/middlewares/file-serve.middleware.d.ts +9 -0
  49. package/middlewares/index.d.ts +1 -0
  50. package/modules/storage.module.d.ts +3 -2
  51. package/package.json +26 -11
  52. package/providers/local-provider.d.ts +2 -1
  53. package/providers/storage-factory.service.d.ts +4 -0
  54. package/services/file-manager.service.d.ts +7 -1
  55. package/services/folder.service.d.ts +1 -2
  56. package/services/storage-provider-config.service.d.ts +2 -2
  57. package/services/upload.service.d.ts +6 -2
@@ -0,0 +1,204 @@
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 _uploadservice = require("../services/upload.service");
16
+ function _define_property(obj, key, value) {
17
+ if (key in obj) {
18
+ Object.defineProperty(obj, key, {
19
+ value: value,
20
+ enumerable: true,
21
+ configurable: true,
22
+ writable: true
23
+ });
24
+ } else {
25
+ obj[key] = value;
26
+ }
27
+ return obj;
28
+ }
29
+ function _getRequireWildcardCache(nodeInterop) {
30
+ if (typeof WeakMap !== "function") return null;
31
+ var cacheBabelInterop = new WeakMap();
32
+ var cacheNodeInterop = new WeakMap();
33
+ return (_getRequireWildcardCache = function(nodeInterop) {
34
+ return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
35
+ })(nodeInterop);
36
+ }
37
+ function _interop_require_wildcard(obj, nodeInterop) {
38
+ if (!nodeInterop && obj && obj.__esModule) {
39
+ return obj;
40
+ }
41
+ if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
42
+ return {
43
+ default: obj
44
+ };
45
+ }
46
+ var cache = _getRequireWildcardCache(nodeInterop);
47
+ if (cache && cache.has(obj)) {
48
+ return cache.get(obj);
49
+ }
50
+ var newObj = {
51
+ __proto__: null
52
+ };
53
+ var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
54
+ for(var key in obj){
55
+ if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
56
+ var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
57
+ if (desc && (desc.get || desc.set)) {
58
+ Object.defineProperty(newObj, key, desc);
59
+ } else {
60
+ newObj[key] = obj[key];
61
+ }
62
+ }
63
+ }
64
+ newObj.default = obj;
65
+ if (cache) {
66
+ cache.set(obj, newObj);
67
+ }
68
+ return newObj;
69
+ }
70
+ function _ts_decorate(decorators, target, key, desc) {
71
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
72
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
73
+ 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;
74
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
75
+ }
76
+ function _ts_metadata(k, v) {
77
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
78
+ }
79
+ let FileServeMiddleware = class FileServeMiddleware {
80
+ async use(req, res, next) {
81
+ 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
+ }
130
+ const stats = (0, _fs.statSync)(fullPath);
131
+ if (!stats.isFile()) {
132
+ throw new _common.NotFoundException(`Not a file: ${normalizedPath}`);
133
+ }
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
+ });
180
+ } catch (error) {
181
+ this.logger.error('File retrieval error:', error);
182
+ 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
+ });
188
+ }
189
+ }
190
+ }
191
+ constructor(uploadService){
192
+ _define_property(this, "uploadService", void 0);
193
+ _define_property(this, "logger", void 0);
194
+ this.uploadService = uploadService;
195
+ this.logger = new _common.Logger(FileServeMiddleware.name);
196
+ }
197
+ };
198
+ FileServeMiddleware = _ts_decorate([
199
+ (0, _common.Injectable)(),
200
+ _ts_metadata("design:type", Function),
201
+ _ts_metadata("design:paramtypes", [
202
+ typeof _uploadservice.UploadService === "undefined" ? Object : _uploadservice.UploadService
203
+ ])
204
+ ], FileServeMiddleware);
@@ -0,0 +1,18 @@
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
+ }
@@ -10,10 +10,13 @@ Object.defineProperty(exports, "StorageModule", {
10
10
  });
11
11
  const _modules = require("@flusys/nestjs-shared/modules");
12
12
  const _common = require("@nestjs/common");
13
+ const _middlewares = require("../middlewares");
13
14
  const _config = require("../config");
14
15
  const _storageconstants = require("../config/storage.constants");
15
16
  const _controllers = require("../controllers");
17
+ const _filelocationenum = require("../enums/file-location.enum");
16
18
  const _providers = require("../providers");
19
+ const _localprovider = require("../providers/local-provider");
17
20
  const _services = require("../services");
18
21
  const _storageproviderconfigservice = require("../services/storage-provider-config.service");
19
22
  function _ts_decorate(decorators, target, key, desc) {
@@ -22,8 +25,44 @@ function _ts_decorate(decorators, target, key, desc) {
22
25
  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;
23
26
  return c > 3 && r && Object.defineProperty(target, key, r), r;
24
27
  }
28
+ // Auto-register built-in storage providers
29
+ const logger = new _common.Logger('StorageModule');
30
+ // Register LocalProvider (always available - uses Node.js built-in fs)
31
+ _providers.StorageProviderRegistry.register(_filelocationenum.FileLocationEnum.LOCAL, _localprovider.LocalProvider);
32
+ 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)');
54
+ }
25
55
  let StorageModule = class StorageModule {
26
56
  /**
57
+ * Configure middleware for file serving
58
+ * This bypasses path-to-regexp issues with wildcard routes
59
+ */ configure(consumer) {
60
+ consumer.apply(_middlewares.FileServeMiddleware).forRoutes({
61
+ path: 'storage/upload/file/*',
62
+ method: _common.RequestMethod.GET
63
+ });
64
+ }
65
+ /**
27
66
  * Register StorageModule synchronously
28
67
  */ static forRoot(options) {
29
68
  const controllers = this.getControllers(options);
@@ -62,9 +101,10 @@ let StorageModule = class StorageModule {
62
101
  _modules.UtilsModule
63
102
  ],
64
103
  controllers: options.includeController !== false ? controllers : [],
104
+ // Pass false to exclude STORAGE_MODULE_OPTIONS - it's already in asyncProviders
65
105
  providers: [
66
106
  ...asyncProviders,
67
- ...this.getProviders(options)
107
+ ...this.getProviders(options, false)
68
108
  ],
69
109
  exports: [
70
110
  _config.StorageConfigService,
@@ -77,10 +117,8 @@ let StorageModule = class StorageModule {
77
117
  ]
78
118
  };
79
119
  }
80
- // ==================== Private Helper Methods ====================
81
- /**
82
- * Get controllers (all controllers always loaded)
83
- */ static getControllers(options) {
120
+ // Private Helper Methods
121
+ /** Get controllers (all controllers always loaded) */ static getControllers(options) {
84
122
  return [
85
123
  _controllers.FileManagerController,
86
124
  _controllers.FolderController,
@@ -93,22 +131,28 @@ let StorageModule = class StorageModule {
93
131
  * This ensures dynamic entity loading based on runtime configuration
94
132
  */ /**
95
133
  * Get providers (all providers always loaded)
96
- */ static getProviders(options) {
97
- const enableCompanyFeature = options.bootstrapAppConfig?.enableCompanyFeature ?? false;
98
- const optionsProvider = {
99
- provide: _storageconstants.STORAGE_MODULE_OPTIONS,
100
- useValue: options
101
- };
102
- return [
103
- optionsProvider,
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) {
137
+ const providers = [
104
138
  _config.StorageConfigService,
105
139
  _services.StorageDataSourceProvider,
106
140
  _services.FileManagerService,
107
141
  _services.FolderService,
108
142
  _storageproviderconfigservice.StorageProviderConfigService,
109
143
  _services.UploadService,
110
- _providers.StorageFactoryService
144
+ _providers.StorageFactoryService,
145
+ _middlewares.FileServeMiddleware
111
146
  ];
147
+ // Only include options provider for sync registration
148
+ // For async registration, createAsyncProviders handles it
149
+ if (includeOptionsProvider) {
150
+ providers.unshift({
151
+ provide: _storageconstants.STORAGE_MODULE_OPTIONS,
152
+ useValue: options
153
+ });
154
+ }
155
+ return providers;
112
156
  }
113
157
  /**
114
158
  * Create async providers for forRootAsync
@@ -101,7 +101,7 @@ let AzureProvider = class AzureProvider {
101
101
  // Ensure container exists
102
102
  await this.containerClient.createIfNotExists();
103
103
  this.logger.log(`Azure Provider initialized: account=${config.accountName}, container=${config.containerName}`);
104
- } catch (error) {
104
+ } catch (_error) {
105
105
  this.logger.error('Failed to initialize AzureProvider. Make sure @azure/storage-blob is installed.');
106
106
  throw new Error('Azure Storage SDK not found. Install it with: npm install @azure/storage-blob');
107
107
  }
@@ -129,7 +129,6 @@ let AzureProvider = class AzureProvider {
129
129
  blobContentType: contentType
130
130
  }
131
131
  });
132
- const url = blockBlobClient.url;
133
132
  return {
134
133
  name: fileName,
135
134
  key: blobName,
@@ -81,14 +81,19 @@ function _interop_require_wildcard(obj, nodeInterop) {
81
81
  let LocalProvider = class LocalProvider {
82
82
  /**
83
83
  * Initialize Local File System provider with configuration
84
+ * @param config.basePath - Base path for file storage (default: './uploads')
85
+ * @param config.baseUrl - Optional base URL for generating file URLs
84
86
  */ async initialize(config) {
85
- this.basePath = _path.resolve(config.basePath);
86
- this.baseUrl = config.baseUrl || '';
87
+ // Store original relative path for key generation
88
+ this.relativeBasePath = config?.basePath || './uploads';
89
+ // Resolve to absolute path for file operations
90
+ this.basePath = _path.resolve(this.relativeBasePath);
91
+ this.baseUrl = config?.baseUrl || '';
87
92
  // Ensure base directory exists
88
93
  await _promises.mkdir(this.basePath, {
89
94
  recursive: true
90
95
  });
91
- this.logger.log(`Local Provider initialized: ${this.basePath}`);
96
+ this.logger.log(`Local Provider initialized: ${this.basePath} (relative: ${this.relativeBasePath})`);
92
97
  }
93
98
  async uploadFile(file, options) {
94
99
  let processedBuffer = file.buffer;
@@ -114,8 +119,9 @@ let LocalProvider = class LocalProvider {
114
119
  });
115
120
  // Write file to disk
116
121
  await _promises.writeFile(filePath, processedBuffer);
117
- // Generate relative key (for URL generation and deletion)
118
- const relativeKey = _path.relative(this.basePath, filePath);
122
+ // Generate key that includes the base path (relative to cwd)
123
+ // This makes the key self-contained for serving files
124
+ const relativeKey = _path.join(this.relativeBasePath, options.folderPath || '', fileName).replace(/\\/g, '/'); // Normalize path separators
119
125
  this.logger.log(`Uploaded file to local storage: ${relativeKey}`);
120
126
  return {
121
127
  name: fileName,
@@ -129,7 +135,24 @@ let LocalProvider = class LocalProvider {
129
135
  }
130
136
  async deleteFile(key) {
131
137
  try {
132
- const filePath = _path.join(this.basePath, key);
138
+ // Key now includes the basePath, resolve from cwd
139
+ let filePath = _path.resolve(key);
140
+ // Check if file exists at the resolved path
141
+ try {
142
+ await _promises.access(filePath);
143
+ } catch {
144
+ // Fallback: try with basePath prefix (for old keys without basePath)
145
+ const fallbackPath = _path.join(this.basePath, key);
146
+ try {
147
+ await _promises.access(fallbackPath);
148
+ filePath = fallbackPath;
149
+ this.logger.debug(`Using fallback path for delete: ${fallbackPath}`);
150
+ } catch {
151
+ // File doesn't exist at either location
152
+ this.logger.warn(`File not found for deletion: ${key}`);
153
+ return;
154
+ }
155
+ }
133
156
  await _promises.unlink(filePath);
134
157
  this.logger.log(`Deleted file from local storage: ${key}`);
135
158
  } catch (_error) {
@@ -143,8 +166,13 @@ let LocalProvider = class LocalProvider {
143
166
  async generatePresignedUrl(key, _expiresInSeconds) {
144
167
  // Return public URL or relative path
145
168
  // Note: Local storage doesn't support true presigned URLs
146
- // If baseUrl is provided, return full URL, otherwise return relative path
147
- return this.baseUrl ? `${this.baseUrl}/${key}` : `/${key}`;
169
+ // If baseUrl is provided, return full URL with file serving endpoint, otherwise return relative path
170
+ if (this.baseUrl) {
171
+ // Construct URL with the file serving endpoint
172
+ const baseUrlWithoutTrailingSlash = this.baseUrl.replace(/\/$/, '');
173
+ return `${baseUrlWithoutTrailingSlash}/storage/upload/file/${key}`;
174
+ }
175
+ return `/storage/upload/file/${key}`;
148
176
  }
149
177
  async healthCheck() {
150
178
  try {
@@ -156,14 +184,16 @@ let LocalProvider = class LocalProvider {
156
184
  }
157
185
  /**
158
186
  * Get the absolute path for a file key
187
+ * Key now includes basePath, so resolve from cwd
159
188
  */ getAbsolutePath(key) {
160
- return _path.join(this.basePath, key);
189
+ return _path.resolve(key);
161
190
  }
162
191
  /**
163
192
  * Check if a file exists
164
193
  */ async fileExists(key) {
165
194
  try {
166
- const filePath = _path.join(this.basePath, key);
195
+ // Key now includes basePath, resolve from cwd
196
+ const filePath = _path.resolve(key);
167
197
  await _promises.access(filePath);
168
198
  return true;
169
199
  } catch {
@@ -173,7 +203,8 @@ let LocalProvider = class LocalProvider {
173
203
  /**
174
204
  * Get file stats
175
205
  */ async getFileStats(key) {
176
- const filePath = _path.join(this.basePath, key);
206
+ // Key now includes basePath, resolve from cwd
207
+ const filePath = _path.resolve(key);
177
208
  const stats = await _promises.stat(filePath);
178
209
  return {
179
210
  size: stats.size,
@@ -185,5 +216,6 @@ let LocalProvider = class LocalProvider {
185
216
  _define_property(this, "logger", new _common.Logger(LocalProvider.name));
186
217
  _define_property(this, "basePath", '');
187
218
  _define_property(this, "baseUrl", '');
219
+ _define_property(this, "relativeBasePath", ''); // Path relative to cwd for key generation
188
220
  }
189
221
  };
@@ -11,6 +11,7 @@ Object.defineProperty(exports, "StorageFactoryService", {
11
11
  const _storageproviderregistry = require("./storage-provider.registry");
12
12
  const _common = require("@nestjs/common");
13
13
  const _crypto = /*#__PURE__*/ _interop_require_wildcard(require("crypto"));
14
+ const _config = require("../config");
14
15
  function _define_property(obj, key, value) {
15
16
  if (key in obj) {
16
17
  Object.defineProperty(obj, key, {
@@ -71,6 +72,14 @@ function _ts_decorate(decorators, target, key, desc) {
71
72
  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;
72
73
  return c > 3 && r && Object.defineProperty(target, key, r), r;
73
74
  }
75
+ function _ts_metadata(k, v) {
76
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
77
+ }
78
+ function _ts_param(paramIndex, decorator) {
79
+ return function(target, key) {
80
+ decorator(target, key, paramIndex);
81
+ };
82
+ }
74
83
  let StorageFactoryService = class StorageFactoryService {
75
84
  /**
76
85
  * Generate a stable cache key for provider configuration
@@ -100,7 +109,19 @@ let StorageFactoryService = class StorageFactoryService {
100
109
  const instance = new ProviderClass();
101
110
  // Initialize if method exists
102
111
  if ('initialize' in instance && typeof instance.initialize === 'function') {
103
- await instance.initialize(config.config);
112
+ // For local provider, inject appUrl as fallback for baseUrl
113
+ let initConfig = config.config;
114
+ if (config.provider === 'local' && !config.config?.baseUrl) {
115
+ const appUrl = this.storageConfigService.getAppUrl();
116
+ if (appUrl) {
117
+ initConfig = {
118
+ ...config.config,
119
+ baseUrl: appUrl
120
+ };
121
+ this.logger.debug(`Using appUrl from config as baseUrl: ${appUrl}`);
122
+ }
123
+ }
124
+ await instance.initialize(initConfig);
104
125
  }
105
126
  // Cache the instance
106
127
  this.providerCache.set(providerKey, instance);
@@ -142,6 +163,20 @@ let StorageFactoryService = class StorageFactoryService {
142
163
  return _storageproviderregistry.StorageProviderRegistry.getAll();
143
164
  }
144
165
  /**
166
+ * Get the basePath from cached local provider
167
+ * Returns null if no local provider is cached
168
+ */ getLocalProviderBasePath() {
169
+ // Find cached local provider
170
+ const localKey = Array.from(this.providerCache.keys()).find((key)=>key.startsWith('local'));
171
+ if (localKey) {
172
+ const provider = this.providerCache.get(localKey);
173
+ if (provider && 'basePath' in provider) {
174
+ return provider.basePath;
175
+ }
176
+ }
177
+ return null;
178
+ }
179
+ /**
145
180
  * Cleanup all cached provider connections on module destroy
146
181
  * Ensures SFTP connections and other resources are properly released
147
182
  */ async onModuleDestroy() {
@@ -157,11 +192,20 @@ let StorageFactoryService = class StorageFactoryService {
157
192
  this.providerCache.clear();
158
193
  this.logger.log('Storage provider cleanup complete');
159
194
  }
160
- constructor(){
161
- _define_property(this, "logger", new _common.Logger(StorageFactoryService.name));
162
- _define_property(this, "providerCache", new Map());
195
+ constructor(storageConfigService){
196
+ _define_property(this, "storageConfigService", void 0);
197
+ _define_property(this, "logger", void 0);
198
+ _define_property(this, "providerCache", void 0);
199
+ this.storageConfigService = storageConfigService;
200
+ this.logger = new _common.Logger(StorageFactoryService.name);
201
+ this.providerCache = new Map();
163
202
  }
164
203
  };
165
204
  StorageFactoryService = _ts_decorate([
166
- (0, _common.Injectable)()
205
+ (0, _common.Injectable)(),
206
+ _ts_param(0, (0, _common.Inject)(_config.StorageConfigService)),
207
+ _ts_metadata("design:type", Function),
208
+ _ts_metadata("design:paramtypes", [
209
+ typeof _config.StorageConfigService === "undefined" ? Object : _config.StorageConfigService
210
+ ])
167
211
  ], StorageFactoryService);