@flusys/nestjs-storage 3.0.1 → 4.0.0-lts

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/cjs/config/index.js +1 -0
  2. package/cjs/config/message-keys.js +90 -0
  3. package/cjs/controllers/file-manager.controller.js +7 -1
  4. package/cjs/controllers/folder.controller.js +1 -0
  5. package/cjs/controllers/storage-config.controller.js +1 -0
  6. package/cjs/controllers/upload.controller.js +9 -4
  7. package/cjs/middlewares/file-serve.middleware.js +39 -15
  8. package/cjs/modules/storage.module.js +2 -2
  9. package/cjs/providers/azure-provider.optional.js +8 -5
  10. package/cjs/providers/local-provider.js +16 -14
  11. package/cjs/providers/s3-provider.optional.js +8 -5
  12. package/cjs/providers/sftp-provider.optional.js +13 -11
  13. package/cjs/providers/storage-factory.service.js +19 -10
  14. package/cjs/services/file-manager.service.js +30 -23
  15. package/cjs/services/folder.service.js +2 -1
  16. package/cjs/services/storage-datasource.provider.js +6 -2
  17. package/cjs/services/storage-provider-config.service.js +2 -1
  18. package/cjs/services/upload.service.js +129 -79
  19. package/cjs/utils/file-validator.util.js +4 -22
  20. package/config/index.d.ts +1 -0
  21. package/config/message-keys.d.ts +114 -0
  22. package/controllers/folder.controller.d.ts +7 -7
  23. package/controllers/storage-config.controller.d.ts +7 -7
  24. package/controllers/upload.controller.d.ts +1 -1
  25. package/fesm/config/index.js +1 -0
  26. package/fesm/config/message-keys.js +64 -0
  27. package/fesm/controllers/file-manager.controller.js +7 -1
  28. package/fesm/controllers/folder.controller.js +1 -0
  29. package/fesm/controllers/storage-config.controller.js +1 -0
  30. package/fesm/controllers/upload.controller.js +9 -4
  31. package/fesm/middlewares/file-serve.middleware.js +40 -16
  32. package/fesm/modules/storage.module.js +2 -2
  33. package/fesm/providers/azure-provider.optional.js +9 -6
  34. package/fesm/providers/local-provider.js +28 -26
  35. package/fesm/providers/s3-provider.optional.js +9 -6
  36. package/fesm/providers/sftp-provider.optional.js +26 -24
  37. package/fesm/providers/storage-factory.service.js +20 -11
  38. package/fesm/services/file-manager.service.js +31 -24
  39. package/fesm/services/folder.service.js +2 -1
  40. package/fesm/services/storage-datasource.provider.js +7 -3
  41. package/fesm/services/storage-provider-config.service.js +2 -1
  42. package/fesm/services/upload.service.js +131 -81
  43. package/fesm/utils/file-validator.util.js +3 -21
  44. package/middlewares/file-serve.middleware.d.ts +0 -1
  45. package/package.json +5 -4
  46. package/providers/azure-provider.optional.d.ts +0 -1
  47. package/providers/local-provider.d.ts +0 -1
  48. package/providers/s3-provider.optional.d.ts +0 -1
  49. package/providers/sftp-provider.optional.d.ts +0 -1
  50. package/providers/storage-factory.service.d.ts +0 -1
  51. package/services/file-manager.service.d.ts +2 -2
  52. package/services/storage-datasource.provider.d.ts +0 -2
  53. package/services/upload.service.d.ts +0 -1
  54. package/utils/file-validator.util.d.ts +0 -1
@@ -10,6 +10,7 @@ Object.defineProperty(exports, "StorageFactoryService", {
10
10
  });
11
11
  const _common = require("@nestjs/common");
12
12
  const _crypto = /*#__PURE__*/ _interop_require_wildcard(require("crypto"));
13
+ const _constants = require("@flusys/nestjs-shared/constants");
13
14
  const _services = require("../services");
14
15
  const _storageproviderregistry = require("./storage-provider.registry");
15
16
  function _define_property(obj, key, value) {
@@ -87,17 +88,30 @@ let StorageFactoryService = class StorageFactoryService {
87
88
  if (cached) return cached;
88
89
  const ProviderClass = _storageproviderregistry.StorageProviderRegistry.get(config.provider);
89
90
  if (!ProviderClass) {
90
- throw new _common.NotFoundException(`Storage provider '${config.provider}' not registered. Available: ${_storageproviderregistry.StorageProviderRegistry.getAll().join(', ')}`);
91
+ throw new _common.NotFoundException({
92
+ message: `Storage provider '${config.provider}' not registered. Available: ${_storageproviderregistry.StorageProviderRegistry.getAll().join(', ')}`,
93
+ messageKey: _constants.SYSTEM_MESSAGES.SERVICE_NOT_AVAILABLE,
94
+ messageParams: {
95
+ provider: config.provider,
96
+ available: _storageproviderregistry.StorageProviderRegistry.getAll().join(', ')
97
+ }
98
+ });
91
99
  }
92
100
  try {
93
101
  const instance = new ProviderClass();
94
102
  await this.initializeProvider(instance, config);
95
103
  this.cache.set(cacheKey, instance);
96
- this.logger.log(`Created provider: ${config.provider} (${cacheKey})`);
97
104
  return instance;
98
105
  } catch (error) {
99
- const message = error instanceof Error ? error.message : 'Unknown error';
100
- throw new Error(`Failed to initialize '${config.provider}': ${message}`);
106
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
107
+ throw new _common.InternalServerErrorException({
108
+ message: `Failed to initialize '${config.provider}': ${errorMessage}`,
109
+ messageKey: _constants.SYSTEM_MESSAGES.INTERNAL_ERROR,
110
+ messageParams: {
111
+ provider: config.provider,
112
+ error: errorMessage
113
+ }
114
+ });
101
115
  }
102
116
  }
103
117
  async getProvider(providerName) {
@@ -117,11 +131,9 @@ let StorageFactoryService = class StorageFactoryService {
117
131
  this.cache.clear();
118
132
  }
119
133
  async onModuleDestroy() {
120
- this.logger.log('Cleaning up storage providers...');
121
- const closePromises = Array.from(this.cache.entries()).filter(([, p])=>'close' in p && typeof p.close === 'function').map(([key, p])=>p.close().then(()=>this.logger.debug(`Closed: ${key}`)).catch((e)=>this.logger.warn(`Failed to close ${key}: ${e.message}`)));
134
+ const closePromises = Array.from(this.cache.entries()).filter(([, p])=>'close' in p && typeof p.close === 'function').map(([, p])=>p.close().catch(()=>{}));
122
135
  await Promise.allSettled(closePromises);
123
136
  this.cache.clear();
124
- this.logger.log('Storage provider cleanup complete');
125
137
  }
126
138
  // ─── Private Helpers ──────────────────────────────────────────────────────────
127
139
  generateCacheKey(config) {
@@ -142,17 +154,14 @@ let StorageFactoryService = class StorageFactoryService {
142
154
  ...config.config,
143
155
  baseUrl: appUrl
144
156
  };
145
- this.logger.debug(`Using appUrl as baseUrl: ${appUrl}`);
146
157
  }
147
158
  }
148
159
  await instance.initialize(initConfig);
149
160
  }
150
161
  constructor(configService){
151
162
  _define_property(this, "configService", void 0);
152
- _define_property(this, "logger", void 0);
153
163
  _define_property(this, "cache", void 0);
154
164
  this.configService = configService;
155
- this.logger = new _common.Logger(StorageFactoryService.name);
156
165
  this.cache = new Map();
157
166
  }
158
167
  };
@@ -11,6 +11,7 @@ Object.defineProperty(exports, "FileManagerService", {
11
11
  const _classes = require("@flusys/nestjs-shared/classes");
12
12
  const _modules = require("@flusys/nestjs-shared/modules");
13
13
  const _utils = require("@flusys/nestjs-shared/utils");
14
+ const _config = require("../config");
14
15
  const _common = require("@nestjs/common");
15
16
  const _typeorm = require("typeorm");
16
17
  const _storageconfigservice = require("./storage-config.service");
@@ -118,7 +119,10 @@ let FileManagerService = class FileManagerService extends _classes.RequestScoped
118
119
  id: dto.id
119
120
  }
120
121
  });
121
- if (!existing) throw new _common.NotFoundException('Entity not found for update');
122
+ if (!existing) throw new _common.NotFoundException({
123
+ message: 'Entity not found for update',
124
+ messageKey: _config.FILE_MESSAGES.NOT_FOUND
125
+ });
122
126
  entity = existing;
123
127
  }
124
128
  const validatedFolder = dto.folderId ? await this.validateFolder(dto.folderId, user) : null;
@@ -148,7 +152,7 @@ let FileManagerService = class FileManagerService extends _classes.RequestScoped
148
152
  }
149
153
  async getFilterQuery(query, filter, _user) {
150
154
  for (const [key, value] of Object.entries(filter)){
151
- if (key === 'contentType') {
155
+ if (key === 'contentType' && typeof value === 'string') {
152
156
  this.applyContentTypeFilter(query, value);
153
157
  } else {
154
158
  query.andWhere(`${this.entityName}.${key} = :value`, {
@@ -220,8 +224,7 @@ let FileManagerService = class FileManagerService extends _classes.RequestScoped
220
224
  ...item,
221
225
  providerName: item.storageConfigId ? nameMap.get(item.storageConfigId) : undefined
222
226
  }));
223
- } catch (error) {
224
- this.logger.warn(`Failed to fetch provider names: ${_utils.ErrorHandler.getErrorMessage(error)}`);
227
+ } catch {
225
228
  return items.map((item)=>({
226
229
  ...item,
227
230
  providerName: undefined
@@ -231,7 +234,10 @@ let FileManagerService = class FileManagerService extends _classes.RequestScoped
231
234
  async getFiles(dtos, protocol, host, user) {
232
235
  await this.ensureRepositoryInitialized();
233
236
  const ids = dtos.map((d)=>d.id).filter(Boolean);
234
- if (!ids.length) throw new _common.BadRequestException('No valid file IDs provided');
237
+ if (!ids.length) throw new _common.BadRequestException({
238
+ message: 'No valid file IDs provided',
239
+ messageKey: _config.UPLOAD_MESSAGES.NO_FILES_PROVIDED
240
+ });
235
241
  const files = await this.repository.findBy({
236
242
  id: (0, _typeorm.In)(ids)
237
243
  });
@@ -245,17 +251,13 @@ let FileManagerService = class FileManagerService extends _classes.RequestScoped
245
251
  if (updatedFiles.length) {
246
252
  try {
247
253
  await this.repository.save(updatedFiles);
248
- } catch (error) {
249
- this.logger.error(`Failed to save updated URLs: ${_utils.ErrorHandler.getErrorMessage(error)}`);
254
+ } catch {
255
+ // Silent failure - URL update is non-critical
250
256
  }
251
257
  }
252
258
  return responses;
253
259
  }
254
260
  // ─── Private Helpers ────────────────────────────────────────────────────────
255
- async getStorageConfigRepository() {
256
- const entity = this.storageConfig.isCompanyFeatureEnabled() ? (await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("../entities")))).StorageConfigWithCompany : (await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("../entities")))).StorageConfig;
257
- return this.dataSourceProvider.getRepository(entity);
258
- }
259
261
  buildWhereWithCompany(id, user) {
260
262
  const where = {
261
263
  id
@@ -265,6 +267,10 @@ let FileManagerService = class FileManagerService extends _classes.RequestScoped
265
267
  }
266
268
  return where;
267
269
  }
270
+ async getStorageConfigRepository() {
271
+ const entity = this.storageConfig.isCompanyFeatureEnabled() ? (await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("../entities")))).StorageConfigWithCompany : (await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("../entities")))).StorageConfig;
272
+ return this.dataSourceProvider.getRepository(entity);
273
+ }
268
274
  async getStorageConfigBasePath(configId) {
269
275
  try {
270
276
  const repo = await this.getStorageConfigRepository();
@@ -278,8 +284,7 @@ let FileManagerService = class FileManagerService extends _classes.RequestScoped
278
284
  ]
279
285
  });
280
286
  return config?.config?.basePath?.replace(/^\.\//, '') ?? null;
281
- } catch (error) {
282
- this.logger.warn(`Failed to get basePath for ${configId}: ${_utils.ErrorHandler.getErrorMessage(error)}`);
287
+ } catch {
283
288
  return null;
284
289
  }
285
290
  }
@@ -290,7 +295,13 @@ let FileManagerService = class FileManagerService extends _classes.RequestScoped
290
295
  where: this.buildWhereWithCompany(folderId, user)
291
296
  });
292
297
  if (!folder) {
293
- throw new _common.BadRequestException(`Folder ${folderId} not found or access denied`);
298
+ throw new _common.BadRequestException({
299
+ message: `Folder "${folderId}" not found or access denied`,
300
+ messageKey: _config.FOLDER_MESSAGES.NOT_FOUND,
301
+ messageParams: {
302
+ folderId
303
+ }
304
+ });
294
305
  }
295
306
  return folder;
296
307
  }
@@ -303,8 +314,7 @@ let FileManagerService = class FileManagerService extends _classes.RequestScoped
303
314
  where: this.buildWhereWithCompany(configId, user)
304
315
  });
305
316
  return config?.storage || dto.location || _filelocationenum.FileLocationEnum.LOCAL;
306
- } catch (error) {
307
- this.logger.warn(`Failed to resolve storage location: ${_utils.ErrorHandler.getErrorMessage(error)}`);
317
+ } catch {
308
318
  return dto.location || _filelocationenum.FileLocationEnum.LOCAL;
309
319
  }
310
320
  }
@@ -350,12 +360,10 @@ let FileManagerService = class FileManagerService extends _classes.RequestScoped
350
360
  return grouped;
351
361
  }
352
362
  getFileBaseUrl(protocol, host) {
353
- return this.storageConfig.getAppUrl()?.replace(/\/$/, '') ?? `${protocol}://${host}`;
363
+ const appUrl = this.storageConfig.getAppUrl()?.replace(/\/$/, '');
364
+ return appUrl || `${protocol}://${host}`;
354
365
  }
355
366
  async refreshFileUrl(file, protocol, host, now, user) {
356
- if (!file.storageConfigId) {
357
- this.logger.warn(`File ${file.id} has no storageConfigId`);
358
- }
359
367
  const isCloudProvider = file.location === _filelocationenum.FileLocationEnum.AWS || file.location === _filelocationenum.FileLocationEnum.AZURE;
360
368
  const needsNewUrl = !file.url || isCloudProvider && (typeof file.expiresAt !== 'number' || now >= file.expiresAt);
361
369
  if (needsNewUrl && file.storageConfigId) {
@@ -363,8 +371,7 @@ let FileManagerService = class FileManagerService extends _classes.RequestScoped
363
371
  file.url = await this.uploadService.makeFileUrl(file.key, file.storageConfigId, URL_EXPIRY_SECONDS, user);
364
372
  file.expiresAt = now + URL_EXPIRY_SECONDS * 1000;
365
373
  return true;
366
- } catch (error) {
367
- this.logger.error(`Failed to generate URL for ${file.id}: ${_utils.ErrorHandler.getErrorMessage(error)}`);
374
+ } catch {
368
375
  file.url = `${this.getFileBaseUrl(protocol, host)}/storage/upload/file/${file.key}`;
369
376
  }
370
377
  }
@@ -393,7 +400,7 @@ let FileManagerService = class FileManagerService extends _classes.RequestScoped
393
400
  };
394
401
  }
395
402
  constructor(cacheManager, utilsService, uploadService, storageConfig, dataSourceProvider){
396
- super('file_manager', null, cacheManager, utilsService, FileManagerService.name, true), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "uploadService", void 0), _define_property(this, "storageConfig", void 0), _define_property(this, "dataSourceProvider", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.uploadService = uploadService, this.storageConfig = storageConfig, this.dataSourceProvider = dataSourceProvider;
403
+ super('file_manager', null, cacheManager, utilsService, FileManagerService.name, true, 'storage'), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "uploadService", void 0), _define_property(this, "storageConfig", void 0), _define_property(this, "dataSourceProvider", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.uploadService = uploadService, this.storageConfig = storageConfig, this.dataSourceProvider = dataSourceProvider;
397
404
  }
398
405
  };
399
406
  FileManagerService = _ts_decorate([
@@ -49,6 +49,7 @@ let FolderService = class FolderService extends _classes.RequestScopedApiService
49
49
  getDataSourceProvider() {
50
50
  return this.dataSourceProvider;
51
51
  }
52
+ // ─── Override Methods ───────────────────────────────────────────────────────
52
53
  async convertSingleDtoToEntity(dto, user) {
53
54
  const entity = await super.convertSingleDtoToEntity(dto, user);
54
55
  if (this.storageConfig.isCompanyFeatureEnabled()) {
@@ -84,7 +85,7 @@ let FolderService = class FolderService extends _classes.RequestScopedApiService
84
85
  return result;
85
86
  }
86
87
  constructor(cacheManager, utilsService, storageConfig, dataSourceProvider){
87
- super('folder', null, cacheManager, utilsService, FolderService.name, true), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "storageConfig", void 0), _define_property(this, "dataSourceProvider", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.storageConfig = storageConfig, this.dataSourceProvider = dataSourceProvider;
88
+ super('folder', null, cacheManager, utilsService, FolderService.name, true, 'storage'), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "storageConfig", void 0), _define_property(this, "dataSourceProvider", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.storageConfig = storageConfig, this.dataSourceProvider = dataSourceProvider;
88
89
  }
89
90
  };
90
91
  FolderService = _ts_decorate([
@@ -10,6 +10,7 @@ Object.defineProperty(exports, "StorageDataSourceProvider", {
10
10
  });
11
11
  const _modules = require("@flusys/nestjs-shared/modules");
12
12
  const _common = require("@nestjs/common");
13
+ const _constants = require("@flusys/nestjs-shared/constants");
13
14
  const _core = require("@nestjs/core");
14
15
  const _express = require("express");
15
16
  const _storageconfigservice = require("./storage-config.service");
@@ -141,7 +142,10 @@ let StorageDataSourceProvider = class StorageDataSourceProvider extends _modules
141
142
  }
142
143
  const config = this.getDefaultDatabaseConfig();
143
144
  if (!config) {
144
- throw new Error('No database config available. Provide defaultDatabaseConfig or tenantDefaultDatabaseConfig.');
145
+ throw new _common.InternalServerErrorException({
146
+ message: 'No database config available. Provide defaultDatabaseConfig or tenantDefaultDatabaseConfig.',
147
+ messageKey: _constants.SYSTEM_MESSAGES.DATABASE_CONFIG_NOT_AVAILABLE
148
+ });
145
149
  }
146
150
  // Create connection with lock to prevent race conditions
147
151
  const connectionPromise = this.createDataSourceFromConfig(config);
@@ -180,7 +184,7 @@ let StorageDataSourceProvider = class StorageDataSourceProvider extends _modules
180
184
  }
181
185
  }
182
186
  constructor(configService, request){
183
- super(StorageDataSourceProvider.buildParentOptions(configService.getOptions()), request), _define_property(this, "configService", void 0), _define_property(this, "logger", void 0), this.configService = configService, this.logger = new _common.Logger(StorageDataSourceProvider.name);
187
+ super(StorageDataSourceProvider.buildParentOptions(configService.getOptions()), request), _define_property(this, "configService", void 0), this.configService = configService;
184
188
  }
185
189
  };
186
190
  // Override parent's static properties to have Storage-specific cache
@@ -49,6 +49,7 @@ let StorageProviderConfigService = class StorageProviderConfigService extends _c
49
49
  getDataSourceProvider() {
50
50
  return this.dataSourceProvider;
51
51
  }
52
+ // ─── Override Methods ───────────────────────────────────────────────────────
52
53
  async convertSingleDtoToEntity(dto, user) {
53
54
  const entity = await super.convertSingleDtoToEntity(dto, user);
54
55
  if (this.storageConfig.isCompanyFeatureEnabled()) {
@@ -130,7 +131,7 @@ let StorageProviderConfigService = class StorageProviderConfigService extends _c
130
131
  });
131
132
  }
132
133
  constructor(cacheManager, utilsService, storageConfig, dataSourceProvider){
133
- super('storageConfig', null, cacheManager, utilsService, StorageProviderConfigService.name, true), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "storageConfig", void 0), _define_property(this, "dataSourceProvider", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.storageConfig = storageConfig, this.dataSourceProvider = dataSourceProvider;
134
+ super('storageConfig', null, cacheManager, utilsService, StorageProviderConfigService.name, true, 'storage'), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "storageConfig", void 0), _define_property(this, "dataSourceProvider", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.storageConfig = storageConfig, this.dataSourceProvider = dataSourceProvider;
134
135
  }
135
136
  };
136
137
  StorageProviderConfigService = _ts_decorate([
@@ -8,9 +8,13 @@ Object.defineProperty(exports, "UploadService", {
8
8
  return UploadService;
9
9
  }
10
10
  });
11
+ const _nestjsshared = require("@flusys/nestjs-shared");
12
+ const _config = require("../config");
13
+ const _interfaces = require("@flusys/nestjs-shared/interfaces");
11
14
  const _utils = require("@flusys/nestjs-shared/utils");
12
15
  const _common = require("@nestjs/common");
13
16
  const _storageconfigservice = require("./storage-config.service");
17
+ const _uploaddto = require("../dtos/upload.dto");
14
18
  const _filelocationenum = require("../enums/file-location.enum");
15
19
  const _storagefactoryservice = require("../providers/storage-factory.service");
16
20
  const _storageproviderconfigservice = require("./storage-provider-config.service");
@@ -50,23 +54,28 @@ let UploadService = class UploadService {
50
54
  // Validate file size
51
55
  const sizeValidation = this.storageConfigService.validateFileSize(file.size);
52
56
  if (!sizeValidation.valid) {
53
- throw new _common.BadRequestException(sizeValidation.message);
57
+ throw new _common.BadRequestException({
58
+ message: sizeValidation.message,
59
+ messageKey: _config.UPLOAD_MESSAGES.FILE_TOO_LARGE
60
+ });
54
61
  }
55
62
  // Validate declared file type (MIME)
56
63
  const typeValidation = this.storageConfigService.validateFileType(file.mimetype);
57
64
  if (!typeValidation.valid) {
58
- throw new _common.BadRequestException(typeValidation.message);
65
+ throw new _common.BadRequestException({
66
+ message: typeValidation.message,
67
+ messageKey: _config.UPLOAD_MESSAGES.INVALID_TYPE
68
+ });
59
69
  }
60
70
  // Validate file content matches declared type (magic bytes check)
61
71
  // This prevents MIME type spoofing attacks
62
72
  const allowedTypes = this.storageConfigService.getAllowedFileTypes();
63
73
  const contentValidation = _filevalidatorutil.FileValidator.validateFileContent(file.buffer, file.mimetype, allowedTypes);
64
74
  if (!contentValidation.valid) {
65
- this.logger.warn(`File content validation failed: ${contentValidation.message}`, {
66
- declaredType: file.mimetype,
67
- detectedType: contentValidation.detectedType
75
+ throw new _common.BadRequestException({
76
+ message: contentValidation.message || 'File content validation failed',
77
+ messageKey: _config.UPLOAD_MESSAGES.INVALID_TYPE
68
78
  });
69
- throw new _common.BadRequestException(contentValidation.message || 'File content validation failed');
70
79
  }
71
80
  // Sanitize filename to prevent path traversal attacks
72
81
  file.originalname = _filevalidatorutil.FileValidator.sanitizeFilename(file.originalname);
@@ -82,7 +91,6 @@ let UploadService = class UploadService {
82
91
  /**
83
92
  * Create fallback local provider
84
93
  */ async createFallbackLocalProvider() {
85
- this.logger.warn('No storage config found, using fallback local provider');
86
94
  return this.storageFactory.createProvider({
87
95
  provider: _filelocationenum.FileLocationEnum.LOCAL,
88
96
  config: {
@@ -100,7 +108,10 @@ let UploadService = class UploadService {
100
108
  // Use direct lookup (bypasses company filtering, returns null instead of throwing)
101
109
  const config = await this.storageProviderConfigService.findByIdDirect(storageConfigId);
102
110
  if (!config) {
103
- throw new _common.NotFoundException('Storage configuration not found');
111
+ throw new _common.NotFoundException({
112
+ message: 'Storage configuration not found',
113
+ messageKey: _config.UPLOAD_MESSAGES.CONFIG_NOT_FOUND
114
+ });
104
115
  }
105
116
  // Validate company ownership using shared utility
106
117
  (0, _utils.validateCompanyOwnership)(config, user, this.storageConfigService.isCompanyFeatureEnabled(), 'Storage configuration');
@@ -109,7 +120,10 @@ let UploadService = class UploadService {
109
120
  // Use default config (scoped to user's company/branch)
110
121
  const defaultConfig = await this.storageProviderConfigService.getDefaultConfig(user);
111
122
  if (!defaultConfig) {
112
- throw new _common.NotFoundException('No default storage configuration found for your company/branch. Please create one.');
123
+ throw new _common.NotFoundException({
124
+ message: 'No default storage configuration found for your company/branch. Please create one.',
125
+ messageKey: _config.UPLOAD_MESSAGES.CONFIG_NOT_FOUND
126
+ });
113
127
  }
114
128
  storageConfig = defaultConfig;
115
129
  }
@@ -130,7 +144,6 @@ let UploadService = class UploadService {
130
144
  if (config) {
131
145
  return this.createProviderFromConfig(config);
132
146
  }
133
- this.logger.warn(`Storage config ${storageConfigId} not found, trying fallback`);
134
147
  }
135
148
  // Fallback: Use locationHint to find a matching config
136
149
  if (locationHint) {
@@ -150,62 +163,44 @@ let UploadService = class UploadService {
150
163
  return this.createFallbackLocalProvider();
151
164
  }
152
165
  async uploadSingleFile(file, options, user) {
153
- try {
154
- // Validate file before upload
166
+ this.validateFile(file);
167
+ const { provider, location, configId } = await this.getStorageProviderWithConfig(options.storageConfigId, user);
168
+ const result = await provider.uploadFile(file, options);
169
+ return {
170
+ ...result,
171
+ location,
172
+ storageConfigId: configId
173
+ };
174
+ }
175
+ async uploadMultipleFiles(files, options, user) {
176
+ for (const file of files){
155
177
  this.validateFile(file);
156
- const { provider, location, configId } = await this.getStorageProviderWithConfig(options.storageConfigId, user);
157
- const result = await provider.uploadFile(file, options);
158
- // Enrich result with storage info
159
- return {
178
+ }
179
+ const { provider, location, configId } = await this.getStorageProviderWithConfig(options.storageConfigId, user);
180
+ const results = await provider.uploadMultipleFiles(files, options);
181
+ return results.map((result)=>({
160
182
  ...result,
161
183
  location,
162
184
  storageConfigId: configId
163
- };
164
- } catch (error) {
165
- _utils.ErrorHandler.logError(this.logger, error, 'uploadSingleFile');
166
- _utils.ErrorHandler.rethrowError(error);
167
- }
168
- }
169
- async uploadMultipleFiles(files, options, user) {
170
- try {
171
- // Validate each file before upload
172
- for (const file of files){
173
- this.validateFile(file);
174
- }
175
- const { provider, location, configId } = await this.getStorageProviderWithConfig(options.storageConfigId, user);
176
- const results = await provider.uploadMultipleFiles(files, options);
177
- // Enrich results with storage info
178
- return results.map((result)=>({
179
- ...result,
180
- location,
181
- storageConfigId: configId
182
- }));
183
- } catch (error) {
184
- _utils.ErrorHandler.logError(this.logger, error, 'uploadMultipleFiles');
185
- _utils.ErrorHandler.rethrowError(error);
186
- }
185
+ }));
187
186
  }
188
187
  async deleteSingleFile(key, storageConfigId, user, locationHint) {
189
- try {
190
- if (!key) throw new _common.BadRequestException('No file path provided');
191
- const provider = await this.getStorageProviderForDelete(storageConfigId, user, locationHint);
192
- await provider.deleteFile(key);
193
- return true;
194
- } catch (error) {
195
- _utils.ErrorHandler.logError(this.logger, error, 'deleteSingleFile');
196
- _utils.ErrorHandler.rethrowError(error);
197
- }
188
+ if (!key) throw new _common.BadRequestException({
189
+ message: 'No file path provided',
190
+ messageKey: _config.UPLOAD_MESSAGES.NO_FILE_PATH
191
+ });
192
+ const provider = await this.getStorageProviderForDelete(storageConfigId, user, locationHint);
193
+ await provider.deleteFile(key);
194
+ return true;
198
195
  }
199
196
  async deleteMultipleFile(keys, storageConfigId, user, locationHint) {
200
- try {
201
- if (!keys || !keys.length) throw new _common.BadRequestException('No file paths provided');
202
- const provider = await this.getStorageProviderForDelete(storageConfigId, user, locationHint);
203
- await provider.deleteMultipleFiles(keys);
204
- return true;
205
- } catch (error) {
206
- _utils.ErrorHandler.logError(this.logger, error, 'deleteMultipleFiles');
207
- _utils.ErrorHandler.rethrowError(error);
208
- }
197
+ if (!keys || !keys.length) throw new _common.BadRequestException({
198
+ message: 'No file paths provided',
199
+ messageKey: _config.UPLOAD_MESSAGES.NO_FILE_PATH
200
+ });
201
+ const provider = await this.getStorageProviderForDelete(storageConfigId, user, locationHint);
202
+ await provider.deleteMultipleFiles(keys);
203
+ return true;
209
204
  }
210
205
  bytesToKb(bytes) {
211
206
  return Number((bytes * 0.001).toFixed(2));
@@ -233,42 +228,97 @@ let UploadService = class UploadService {
233
228
  return this.storageFactory.getLocalProviderBasePath();
234
229
  }
235
230
  return null;
236
- } catch (error) {
237
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
238
- this.logger.warn(`Failed to get local storage basePath: ${errorMessage}`);
231
+ } catch {
239
232
  return null;
240
233
  }
241
234
  }
242
- /**
243
- * Generate a URL for accessing a file
244
- * For S3/Azure: generates presigned URL
245
- * For Local/SFTP: returns direct path
246
- */ async makeFileUrl(key, storageConfigId, expiresIn = 3600, user) {
247
- try {
248
- const { provider } = await this.getStorageProviderWithConfig(storageConfigId, user);
249
- // Check if provider supports presigned URLs
250
- if (provider.generatePresignedUrl) {
251
- return await provider.generatePresignedUrl(key, expiresIn);
252
- }
253
- // For SFTP or other providers without presigned URLs
254
- return key;
255
- } catch (error) {
256
- _utils.ErrorHandler.logError(this.logger, error, 'makeFileUrl');
257
- _utils.ErrorHandler.rethrowError(error);
235
+ async makeFileUrl(key, storageConfigId, expiresIn = 3600, user) {
236
+ const { provider } = await this.getStorageProviderWithConfig(storageConfigId, user);
237
+ // Check if provider supports presigned URLs
238
+ if (provider.generatePresignedUrl) {
239
+ return await provider.generatePresignedUrl(key, expiresIn);
258
240
  }
241
+ // For SFTP or other providers without presigned URLs
242
+ return key;
259
243
  }
260
244
  // NOTE: @Inject() required for bundled code - type metadata may be lost during esbuild
261
245
  constructor(storageFactory, storageConfigService, storageProviderConfigService){
262
246
  _define_property(this, "storageFactory", void 0);
263
247
  _define_property(this, "storageConfigService", void 0);
264
248
  _define_property(this, "storageProviderConfigService", void 0);
265
- _define_property(this, "logger", void 0);
266
249
  this.storageFactory = storageFactory;
267
250
  this.storageConfigService = storageConfigService;
268
251
  this.storageProviderConfigService = storageProviderConfigService;
269
- this.logger = new _common.Logger(UploadService.name);
270
252
  }
271
253
  };
254
+ _ts_decorate([
255
+ (0, _nestjsshared.LogAction)({
256
+ action: 'storage.uploadSingle',
257
+ module: 'storage'
258
+ }),
259
+ _ts_metadata("design:type", Function),
260
+ _ts_metadata("design:paramtypes", [
261
+ typeof Express === "undefined" || typeof Express.Multer === "undefined" || typeof Express.Multer.File === "undefined" ? Object : Express.Multer.File,
262
+ typeof _uploaddto.UploadOptionsDto === "undefined" ? Object : _uploaddto.UploadOptionsDto,
263
+ typeof _interfaces.ILoggedUserInfo === "undefined" ? Object : _interfaces.ILoggedUserInfo
264
+ ]),
265
+ _ts_metadata("design:returntype", Promise)
266
+ ], UploadService.prototype, "uploadSingleFile", null);
267
+ _ts_decorate([
268
+ (0, _nestjsshared.LogAction)({
269
+ action: 'storage.uploadMultiple',
270
+ module: 'storage'
271
+ }),
272
+ _ts_metadata("design:type", Function),
273
+ _ts_metadata("design:paramtypes", [
274
+ Array,
275
+ typeof _uploaddto.UploadOptionsDto === "undefined" ? Object : _uploaddto.UploadOptionsDto,
276
+ typeof _interfaces.ILoggedUserInfo === "undefined" ? Object : _interfaces.ILoggedUserInfo
277
+ ]),
278
+ _ts_metadata("design:returntype", Promise)
279
+ ], UploadService.prototype, "uploadMultipleFiles", null);
280
+ _ts_decorate([
281
+ (0, _nestjsshared.LogAction)({
282
+ action: 'storage.deleteSingle',
283
+ module: 'storage'
284
+ }),
285
+ _ts_metadata("design:type", Function),
286
+ _ts_metadata("design:paramtypes", [
287
+ String,
288
+ String,
289
+ typeof _interfaces.ILoggedUserInfo === "undefined" ? Object : _interfaces.ILoggedUserInfo,
290
+ String
291
+ ]),
292
+ _ts_metadata("design:returntype", Promise)
293
+ ], UploadService.prototype, "deleteSingleFile", null);
294
+ _ts_decorate([
295
+ (0, _nestjsshared.LogAction)({
296
+ action: 'storage.deleteMultiple',
297
+ module: 'storage'
298
+ }),
299
+ _ts_metadata("design:type", Function),
300
+ _ts_metadata("design:paramtypes", [
301
+ Array,
302
+ String,
303
+ typeof _interfaces.ILoggedUserInfo === "undefined" ? Object : _interfaces.ILoggedUserInfo,
304
+ String
305
+ ]),
306
+ _ts_metadata("design:returntype", Promise)
307
+ ], UploadService.prototype, "deleteMultipleFile", null);
308
+ _ts_decorate([
309
+ (0, _nestjsshared.LogAction)({
310
+ action: 'storage.makeFileUrl',
311
+ module: 'storage'
312
+ }),
313
+ _ts_metadata("design:type", Function),
314
+ _ts_metadata("design:paramtypes", [
315
+ String,
316
+ String,
317
+ Number,
318
+ typeof _interfaces.ILoggedUserInfo === "undefined" ? Object : _interfaces.ILoggedUserInfo
319
+ ]),
320
+ _ts_metadata("design:returntype", Promise)
321
+ ], UploadService.prototype, "makeFileUrl", null);
272
322
  UploadService = _ts_decorate([
273
323
  (0, _common.Injectable)({
274
324
  scope: _common.Scope.REQUEST
@@ -1,4 +1,6 @@
1
- "use strict";
1
+ /**
2
+ * Result of file content validation.
3
+ */ "use strict";
2
4
  Object.defineProperty(exports, "__esModule", {
3
5
  value: true
4
6
  });
@@ -8,20 +10,6 @@ Object.defineProperty(exports, "FileValidator", {
8
10
  return FileValidator;
9
11
  }
10
12
  });
11
- const _common = require("@nestjs/common");
12
- function _define_property(obj, key, value) {
13
- if (key in obj) {
14
- Object.defineProperty(obj, key, {
15
- value: value,
16
- enumerable: true,
17
- configurable: true,
18
- writable: true
19
- });
20
- } else {
21
- obj[key] = value;
22
- }
23
- return obj;
24
- }
25
13
  /**
26
14
  * Magic byte signatures for common file types.
27
15
  * Each entry maps a hex signature pattern to its MIME type.
@@ -408,7 +396,6 @@ let FileValidator = class FileValidator {
408
396
  }
409
397
  // Verify detected type matches declared type
410
398
  if (!this.mimeTypesMatch(detectedType, declaredMimeType)) {
411
- this.logger.warn(`MIME type mismatch: declared=${declaredMimeType}, detected=${detectedType}`);
412
399
  return this.failureResult(`File content does not match declared type. Detected: ${detectedType}, Declared: ${declaredMimeType}`, detectedType, declaredMimeType);
413
400
  }
414
401
  // Verify type is in allowed list
@@ -416,8 +403,7 @@ let FileValidator = class FileValidator {
416
403
  return this.failureResult(`File type "${detectedType}" is not allowed`, detectedType, declaredMimeType);
417
404
  }
418
405
  return this.successResult(detectedType, declaredMimeType);
419
- } catch (error) {
420
- this.logger.error('File validation error:', error);
406
+ } catch {
421
407
  return this.failureResult('File validation failed');
422
408
  }
423
409
  }
@@ -428,10 +414,8 @@ let FileValidator = class FileValidator {
428
414
  if (this.isDangerousTextType(declaredMimeType)) {
429
415
  const explicitlyAllowed = allowedTypes.some((t)=>t === declaredMimeType && t !== '*/*' && !t.endsWith('/*'));
430
416
  if (!explicitlyAllowed) {
431
- this.logger.warn(`Blocked dangerous file type: ${declaredMimeType} - requires explicit allowlisting`);
432
417
  return this.failureResult(`File type "${declaredMimeType}" is potentially dangerous and not explicitly allowed`, declaredMimeType, declaredMimeType);
433
418
  }
434
- this.logger.warn(`Allowing explicitly permitted dangerous file type: ${declaredMimeType}`);
435
419
  }
436
420
  // Safe text-based files don't have magic bytes, trust declared type
437
421
  if (this.isTextBasedType(declaredMimeType)) {
@@ -439,7 +423,6 @@ let FileValidator = class FileValidator {
439
423
  return isAllowed ? this.successResult(declaredMimeType, declaredMimeType) : this.failureResult(`File type "${declaredMimeType}" is not allowed`, declaredMimeType, declaredMimeType);
440
424
  }
441
425
  // Binary files without recognized signatures - be cautious
442
- this.logger.warn(`Unable to detect file type for declared type: ${declaredMimeType}`);
443
426
  return this.failureResult('Unable to verify file type. File may be corrupted or unsupported.', undefined, declaredMimeType);
444
427
  }
445
428
  /**
@@ -455,4 +438,3 @@ let FileValidator = class FileValidator {
455
438
  .substring(0, 255);
456
439
  }
457
440
  };
458
- _define_property(FileValidator, "logger", new _common.Logger(FileValidator.name));