@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
@@ -90,6 +90,13 @@ const USER_ACTION_PERMISSION_CACHE_KEY = 'user_action_permission';
90
90
  const SHOW_PRIVATE_FILE_ACTION = 'storage.file.viewPrivate';
91
91
  let FileManagerService = class FileManagerService extends _classes.RequestScopedApiService {
92
92
  /**
93
+ * Get base URL for file serving
94
+ * Priority: appUrl config > request headers
95
+ */ getFileBaseUrl(protocol, host) {
96
+ const appUrl = this.storageConfig.getAppUrl();
97
+ return appUrl?.replace(/\/$/, '') ?? `${protocol}://${host}`;
98
+ }
99
+ /**
93
100
  * Resolve entity class for this service
94
101
  * @returns FileManager or FileManagerWithCompany based on configuration
95
102
  */ resolveEntity() {
@@ -188,6 +195,7 @@ let FileManagerService = class FileManagerService extends _classes.RequestScoped
188
195
  'key',
189
196
  'url',
190
197
  'location',
198
+ 'storageConfigId',
191
199
  'isPrivate',
192
200
  'createdAt',
193
201
  'deletedAt'
@@ -198,6 +206,7 @@ let FileManagerService = class FileManagerService extends _classes.RequestScoped
198
206
  if (this.storageConfig.isCompanyFeatureEnabled()) {
199
207
  selectFields.push('file_manager.companyId');
200
208
  }
209
+ // Join folder
201
210
  selectFields.push('folder.id');
202
211
  selectFields.push('folder.name');
203
212
  query.leftJoinAndSelect('file_manager.folder', 'folder');
@@ -207,6 +216,57 @@ let FileManagerService = class FileManagerService extends _classes.RequestScoped
207
216
  isRaw: false
208
217
  };
209
218
  }
219
+ /**
220
+ * Enrich file list with provider names from storage_config table
221
+ * Call this method to add providerName to the list results
222
+ */ async enrichWithProviderNames(items) {
223
+ // Get unique storage config IDs
224
+ const configIds = [
225
+ ...new Set(items.map((item)=>item.storageConfigId).filter(Boolean))
226
+ ];
227
+ this.logger.debug(`enrichWithProviderNames: items count=${items.length}, configIds=${JSON.stringify(configIds)}`);
228
+ if (configIds.length === 0) {
229
+ this.logger.debug('enrichWithProviderNames: No storage config IDs found in items');
230
+ return items.map((item)=>({
231
+ ...item,
232
+ providerName: undefined
233
+ }));
234
+ }
235
+ // Fetch storage config names
236
+ try {
237
+ const enableCompanyFeature = this.storageConfig.isCompanyFeatureEnabled();
238
+ const storageConfigEntity = enableCompanyFeature ? (await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("../entities")))).StorageConfigWithCompany : (await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("../entities")))).StorageConfig;
239
+ const storageConfigRepository = await this.dataSourceProvider.getRepository(storageConfigEntity);
240
+ const configs = await storageConfigRepository.find({
241
+ where: {
242
+ id: (0, _typeorm.In)(configIds)
243
+ },
244
+ select: [
245
+ 'id',
246
+ 'name'
247
+ ]
248
+ });
249
+ this.logger.debug(`enrichWithProviderNames: Found ${configs.length} configs: ${JSON.stringify(configs)}`);
250
+ // Create a map for quick lookup
251
+ const configNameMap = new Map(configs.map((c)=>[
252
+ c.id,
253
+ c.name
254
+ ]));
255
+ // Enrich items with provider names
256
+ const enrichedItems = items.map((item)=>({
257
+ ...item,
258
+ providerName: item.storageConfigId ? configNameMap.get(item.storageConfigId) : undefined
259
+ }));
260
+ this.logger.debug(`enrichWithProviderNames: First enriched item: ${JSON.stringify(enrichedItems[0])}`);
261
+ return enrichedItems;
262
+ } catch (error) {
263
+ this.logger.warn(`Failed to fetch provider names: ${error}`);
264
+ return items.map((item)=>({
265
+ ...item,
266
+ providerName: undefined
267
+ }));
268
+ }
269
+ }
210
270
  async getFilterQuery(query, filter, _user) {
211
271
  Object.entries(filter).forEach(([key, value])=>{
212
272
  if (key === 'contentType') {
@@ -269,7 +329,7 @@ let FileManagerService = class FileManagerService extends _classes.RequestScoped
269
329
  }
270
330
  return result;
271
331
  }
272
- async beforeDeleteOperation(dto, _user, _queryRunner) {
332
+ async beforeDeleteOperation(dto, user, _queryRunner) {
273
333
  if (dto.type === 'permanent') {
274
334
  const ids = Array.isArray(dto.id) ? dto.id : [
275
335
  dto.id
@@ -282,13 +342,46 @@ let FileManagerService = class FileManagerService extends _classes.RequestScoped
282
342
  fileManager.forEach((file)=>{
283
343
  const configId = file.storageConfigId || 'default';
284
344
  if (!filesByConfig.has(configId)) {
285
- filesByConfig.set(configId, []);
345
+ filesByConfig.set(configId, {
346
+ keys: [],
347
+ location: file.location
348
+ });
286
349
  }
287
- filesByConfig.get(configId).push(file.key);
350
+ filesByConfig.get(configId).keys.push(file.key);
288
351
  });
289
352
  // Delete files from each storage config
290
- for (const [configId, keys] of filesByConfig){
291
- await this.uploadService.deleteMultipleFile(keys, configId === 'default' ? undefined : configId);
353
+ for (const [configId, { keys, location }] of filesByConfig){
354
+ // For local files with old key format (no '/'), we need to prefix with basePath
355
+ let deleteKeys = keys;
356
+ if (location === _filelocationenum.FileLocationEnum.LOCAL && configId !== 'default') {
357
+ try {
358
+ const enableCompanyFeature = this.storageConfig.isCompanyFeatureEnabled();
359
+ const storageConfigEntity = enableCompanyFeature ? (await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("../entities")))).StorageConfigWithCompany : (await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("../entities")))).StorageConfig;
360
+ const storageConfigRepository = await this.dataSourceProvider.getRepository(storageConfigEntity);
361
+ const config = await storageConfigRepository.findOne({
362
+ where: {
363
+ id: configId
364
+ },
365
+ select: [
366
+ 'id',
367
+ 'config'
368
+ ]
369
+ });
370
+ if (config && config.config?.basePath) {
371
+ const basePath = config.config.basePath.replace(/^\.\//, '');
372
+ // Convert old keys to new format
373
+ deleteKeys = keys.map((key)=>{
374
+ if (!key.includes('/')) {
375
+ return `${basePath}/${key}`;
376
+ }
377
+ return key;
378
+ });
379
+ }
380
+ } catch (error) {
381
+ this.logger.warn(`Failed to get basePath for delete: ${error}`);
382
+ }
383
+ }
384
+ await this.uploadService.deleteMultipleFile(deleteKeys, configId === 'default' ? undefined : configId, user ?? undefined, location);
292
385
  }
293
386
  }
294
387
  }
@@ -320,13 +413,43 @@ let FileManagerService = class FileManagerService extends _classes.RequestScoped
320
413
  shouldUpdate = true;
321
414
  } catch (error) {
322
415
  this.logger.error(`Failed to generate URL for file ${file.id}: ${error?.message || 'Unknown error'}`);
323
- // Use fallback URL
324
- file.url = `${protocol}://${host}/storage/upload/file/${file.key}`;
416
+ // Use fallback URL with appUrl from config
417
+ const baseUrl = this.getFileBaseUrl(protocol, host);
418
+ file.url = `${baseUrl}/storage/upload/file/${file.key}`;
325
419
  }
326
420
  }
327
421
  // SFTP/Local files - always construct full URL (no expiry, but need full path)
328
422
  if (file.location === _filelocationenum.FileLocationEnum.SFTP || file.location === _filelocationenum.FileLocationEnum.LOCAL) {
329
- const expectedUrl = `${protocol}://${host}/storage/upload/file/${file.key}`;
423
+ // For backward compatibility: if key doesn't include basePath, look it up from config
424
+ let fileKey = file.key;
425
+ // Check if key looks like it's missing the basePath (old format)
426
+ // Old format: "uuid-filename.png"
427
+ // New format: "uploads/uuid-filename.png" or "uploads/company1/uuid-filename.png"
428
+ if (!fileKey.includes('/') && file.storageConfigId) {
429
+ try {
430
+ const enableCompanyFeature = this.storageConfig.isCompanyFeatureEnabled();
431
+ const storageConfigEntity = enableCompanyFeature ? (await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("../entities")))).StorageConfigWithCompany : (await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("../entities")))).StorageConfig;
432
+ const storageConfigRepository = await this.dataSourceProvider.getRepository(storageConfigEntity);
433
+ const config = await storageConfigRepository.findOne({
434
+ where: {
435
+ id: file.storageConfigId
436
+ },
437
+ select: [
438
+ 'id',
439
+ 'config'
440
+ ]
441
+ });
442
+ if (config && config.config?.basePath) {
443
+ const basePath = config.config.basePath.replace(/^\.\//, ''); // Remove leading ./
444
+ fileKey = `${basePath}/${file.key}`;
445
+ this.logger.debug(`Prefixed old key with basePath: ${fileKey}`);
446
+ }
447
+ } catch (error) {
448
+ this.logger.warn(`Failed to get basePath for file ${file.id}: ${error}`);
449
+ }
450
+ }
451
+ const baseUrl = this.getFileBaseUrl(protocol, host);
452
+ const expectedUrl = `${baseUrl}/storage/upload/file/${fileKey}`;
330
453
  if (file.url !== expectedUrl) {
331
454
  file.url = expectedUrl;
332
455
  shouldUpdate = true;
@@ -337,7 +460,9 @@ let FileManagerService = class FileManagerService extends _classes.RequestScoped
337
460
  id: file.id,
338
461
  name: file.name,
339
462
  contentType: file.contentType,
340
- url: file.url || ''
463
+ url: file.url || '',
464
+ location: file.location,
465
+ storageConfigId: file.storageConfigId || undefined
341
466
  };
342
467
  }));
343
468
  // Save only changed records
@@ -42,46 +42,24 @@ function _ts_param(paramIndex, decorator) {
42
42
  };
43
43
  }
44
44
  let FolderService = class FolderService extends _classes.RequestScopedApiService {
45
- /**
46
- * Resolve entity class for this service
47
- * @returns Folder or FolderWithCompany based on configuration
48
- */ resolveEntity() {
49
- const enableCompanyFeature = this.storageConfig.isCompanyFeatureEnabled();
50
- return enableCompanyFeature ? _entities.FolderWithCompany : _entities.Folder;
45
+ resolveEntity() {
46
+ return this.storageConfig.isCompanyFeatureEnabled() ? _entities.FolderWithCompany : _entities.Folder;
51
47
  }
52
- /**
53
- * Get DataSource provider for this service
54
- * @returns StorageDataSourceProvider instance
55
- */ getDataSourceProvider() {
48
+ getDataSourceProvider() {
56
49
  return this.dataSourceProvider;
57
50
  }
51
+ // Entity Conversion
58
52
  async convertSingleDtoToEntity(dto, user) {
59
- let folder = {};
60
- // NOTE: Using 'id' in dto check instead of instanceof - instanceof may not work after esbuild bundling
61
- if ('id' in dto && dto.id && typeof dto.id === 'string') {
62
- const dbData = await this.repository.findOne({
63
- where: {
64
- id: dto.id
65
- }
66
- });
67
- if (!dbData) {
68
- throw new _common.NotFoundException('No such entity data found for update! Please, Try Again.');
69
- }
70
- folder = dbData;
71
- }
72
- // Set company/branch IDs if company feature is enabled
73
- folder = {
74
- ...folder,
75
- ...dto
76
- };
77
- // Only set company fields if they exist on the entity (when company feature is enabled)
78
- if ('companyId' in folder) {
79
- folder.companyId = user?.companyId ?? null;
53
+ const entity = await super.convertSingleDtoToEntity(dto, user);
54
+ // Set companyId from user context if company feature enabled
55
+ if (this.storageConfig.isCompanyFeatureEnabled()) {
56
+ entity.companyId = user?.companyId ?? null;
80
57
  }
81
- return folder;
58
+ return entity;
82
59
  }
60
+ // Query Customization
83
61
  async getSelectQuery(query, _user, select) {
84
- if (!select || !select.length) {
62
+ if (!select?.length) {
85
63
  select = [
86
64
  'id',
87
65
  'name',
@@ -89,35 +67,26 @@ let FolderService = class FolderService extends _classes.RequestScopedApiService
89
67
  'createdAt',
90
68
  'deletedAt'
91
69
  ];
70
+ if (this.storageConfig.isCompanyFeatureEnabled()) {
71
+ select.push('companyId');
72
+ }
92
73
  }
93
- const selectFields = select.map((field)=>`${this.entityName}.${field}`);
94
- // Add company context fields if company feature is enabled
95
- // The entity will have these fields only if company feature is enabled
96
- if (this.storageConfig.isCompanyFeatureEnabled()) {
97
- selectFields.push('folder.companyId');
98
- }
99
- query.select(selectFields);
74
+ query.select(select.map((f)=>`${this.entityName}.${f}`));
100
75
  return {
101
76
  query,
102
77
  isRaw: false
103
78
  };
104
79
  }
105
- /**
106
- * Override: Extra query manipulation - Auto-filter by user's company
107
- */ async getExtraManipulateQuery(query, filterDto, user) {
80
+ async getExtraManipulateQuery(query, filterDto, user) {
108
81
  const result = await super.getExtraManipulateQuery(query, filterDto, user);
109
- // If company feature enabled and user has companyId, filter by user's company
110
- const enableCompanyFeature = this.storageConfig.isCompanyFeatureEnabled();
111
- if (enableCompanyFeature && user?.companyId) {
82
+ if (this.storageConfig.isCompanyFeatureEnabled() && user?.companyId) {
112
83
  query.andWhere('folder.companyId = :companyId', {
113
84
  companyId: user.companyId
114
85
  });
115
86
  }
116
87
  return result;
117
88
  }
118
- // NOTE: @Inject() required for bundled code - type metadata may be lost during esbuild
119
89
  constructor(cacheManager, utilsService, storageConfig, dataSourceProvider){
120
- // Repository will be set asynchronously by RequestScopedApiService
121
90
  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;
122
91
  }
123
92
  };
@@ -83,10 +83,8 @@ function _ts_param(paramIndex, decorator) {
83
83
  };
84
84
  }
85
85
  let StorageDataSourceProvider = class StorageDataSourceProvider extends _modules.MultiTenantDataSourceService {
86
- // ==================== Factory Methods ====================
87
- /**
88
- * Build parent options from StorageModuleOptions
89
- */ static buildParentOptions(options) {
86
+ // Factory Methods
87
+ /** Build parent options from StorageModuleOptions */ static buildParentOptions(options) {
90
88
  return {
91
89
  bootstrapAppConfig: options.bootstrapAppConfig,
92
90
  defaultDatabaseConfig: options.config?.defaultDatabaseConfig,
@@ -94,10 +92,8 @@ let StorageDataSourceProvider = class StorageDataSourceProvider extends _modules
94
92
  tenants: options.config?.tenants
95
93
  };
96
94
  }
97
- // ==================== Feature Flags ====================
98
- /**
99
- * Get global enable company feature flag
100
- */ getEnableCompanyFeature() {
95
+ // Feature Flags
96
+ /** Get global enable company feature flag */ getEnableCompanyFeature() {
101
97
  return this.storageOptions.bootstrapAppConfig?.enableCompanyFeature ?? false;
102
98
  }
103
99
  /**
@@ -111,11 +107,11 @@ let StorageDataSourceProvider = class StorageDataSourceProvider extends _modules
111
107
  */ getEnableCompanyFeatureForCurrentTenant() {
112
108
  return this.getEnableCompanyFeatureForTenant(this.getCurrentTenant() ?? undefined);
113
109
  }
114
- // ==================== Entity Management ====================
110
+ // Entity Management
115
111
  /**
116
- * Get storage entities for migrations based on company feature flag
117
- * Note: For TypeORM repositories, we always use the base entities (FileManager, etc.)
118
- * But for migrations, we need the correct entity based on the feature flag
112
+ * Get storage entities for migrations based on company feature flag.
113
+ * For TypeORM repositories, we always use the base entities (FileManager, etc.)
114
+ * but for migrations, we need the correct entity based on the feature flag.
119
115
  */ async getStorageEntities(enableCompanyFeature) {
120
116
  const enable = enableCompanyFeature ?? this.getEnableCompanyFeature();
121
117
  const { FileManager, Folder, StorageConfig } = await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("../entities")));
@@ -135,10 +131,8 @@ let StorageDataSourceProvider = class StorageDataSourceProvider extends _modules
135
131
  StorageConfig
136
132
  ];
137
133
  }
138
- // ==================== Overrides ====================
139
- /**
140
- * Override to dynamically set entities based on tenant config
141
- */ async createDataSourceFromConfig(config) {
134
+ // Overrides
135
+ /** Override to dynamically set entities based on tenant config */ async createDataSourceFromConfig(config) {
142
136
  const currentTenant = this.getCurrentTenant();
143
137
  const enableCompanyFeature = this.getEnableCompanyFeatureForTenant(currentTenant ?? undefined);
144
138
  const entities = await this.getStorageEntities(enableCompanyFeature);
@@ -42,34 +42,24 @@ function _ts_param(paramIndex, decorator) {
42
42
  };
43
43
  }
44
44
  let StorageProviderConfigService = class StorageProviderConfigService extends _classes.RequestScopedApiService {
45
- /**
46
- * Resolve entity class for this service
47
- * @returns StorageConfig or StorageConfigWithCompany based on configuration
48
- */ resolveEntity() {
49
- const enableCompanyFeature = this.storageConfig.isCompanyFeatureEnabled();
50
- return enableCompanyFeature ? _entities.StorageConfigWithCompany : _entities.StorageConfig;
45
+ resolveEntity() {
46
+ return this.storageConfig.isCompanyFeatureEnabled() ? _entities.StorageConfigWithCompany : _entities.StorageConfig;
51
47
  }
52
- /**
53
- * Get DataSource provider for this service
54
- * @returns StorageDataSourceProvider instance
55
- */ getDataSourceProvider() {
48
+ getDataSourceProvider() {
56
49
  return this.dataSourceProvider;
57
50
  }
51
+ // Entity Conversion
58
52
  async convertSingleDtoToEntity(dto, user) {
59
- let storageConfig = {};
60
- // Set basic fields
61
- storageConfig = {
62
- ...storageConfig,
63
- ...dto
64
- };
65
- // Only set company fields if they exist on the entity (when company feature is enabled)
66
- if ('companyId' in storageConfig) {
67
- storageConfig.companyId = user?.companyId ?? null;
53
+ const entity = await super.convertSingleDtoToEntity(dto, user);
54
+ // Set companyId from user context if company feature enabled
55
+ if (this.storageConfig.isCompanyFeatureEnabled()) {
56
+ entity.companyId = user?.companyId ?? null;
68
57
  }
69
- return storageConfig;
58
+ return entity;
70
59
  }
60
+ // Query Customization
71
61
  async getSelectQuery(query, _user, select) {
72
- if (!select || !select.length) {
62
+ if (!select?.length) {
73
63
  select = [
74
64
  'id',
75
65
  'name',
@@ -78,25 +68,19 @@ let StorageProviderConfigService = class StorageProviderConfigService extends _c
78
68
  'createdAt',
79
69
  'updatedAt'
80
70
  ];
81
- // Add company fields if company feature is enabled
82
71
  if (this.storageConfig.isCompanyFeatureEnabled()) {
83
72
  select.push('companyId');
84
73
  }
85
74
  }
86
- const selectFields = select.map((field)=>`${this.entityName}.${field}`);
87
- query.select(selectFields);
75
+ query.select(select.map((f)=>`${this.entityName}.${f}`));
88
76
  return {
89
77
  query,
90
78
  isRaw: false
91
79
  };
92
80
  }
93
- /**
94
- * Override: Extra query manipulation - Auto-filter by user's company
95
- */ async getExtraManipulateQuery(query, filterDto, user) {
81
+ async getExtraManipulateQuery(query, filterDto, user) {
96
82
  const result = await super.getExtraManipulateQuery(query, filterDto, user);
97
- // If company feature enabled and user has companyId, filter by user's company
98
- const enableCompanyFeature = this.storageConfig.isCompanyFeatureEnabled();
99
- if (enableCompanyFeature && user?.companyId) {
83
+ if (this.storageConfig.isCompanyFeatureEnabled() && user?.companyId) {
100
84
  query.andWhere('storageConfig.companyId = :companyId', {
101
85
  companyId: user.companyId
102
86
  });
@@ -105,6 +89,18 @@ let StorageProviderConfigService = class StorageProviderConfigService extends _c
105
89
  return result;
106
90
  }
107
91
  /**
92
+ * Find storage config by ID without throwing (returns null if not found)
93
+ * Uses direct repository query - bypasses company filtering
94
+ * Use for internal operations like file deletion where config ID is already known
95
+ */ async findByIdDirect(id) {
96
+ await this.ensureRepositoryInitialized();
97
+ return await this.repository.findOne({
98
+ where: {
99
+ id
100
+ }
101
+ });
102
+ }
103
+ /**
108
104
  * Get default storage configuration (scoped to user's company if enabled)
109
105
  * Falls back to any available config if 'default' not found
110
106
  */ async getDefaultConfig(user) {
@@ -150,9 +146,7 @@ let StorageProviderConfigService = class StorageProviderConfigService extends _c
150
146
  where
151
147
  });
152
148
  }
153
- // NOTE: @Inject() required for bundled code - type metadata may be lost during esbuild
154
149
  constructor(cacheManager, utilsService, storageConfig, dataSourceProvider){
155
- // Repository will be set asynchronously by RequestScopedApiService
156
150
  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;
157
151
  }
158
152
  };