@flusys/nestjs-storage 0.1.0-beta.3 → 1.1.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.
package/README.md CHANGED
@@ -412,6 +412,12 @@ export class StorageConfig extends Identity {
412
412
  @Column({ type: 'json' })
413
413
  config: Record<string, any>; // Provider-specific config
414
414
 
415
+ @Column({ default: true })
416
+ isActive: boolean; // Whether config is active and usable
417
+
418
+ @Column({ default: false })
419
+ isDefault: boolean; // Set as default configuration
420
+
415
421
  @Column({ nullable: true })
416
422
  companyId: string; // Company scope (when enabled)
417
423
  }
@@ -438,6 +444,8 @@ export class SetupService {
438
444
  accessKeyId: process.env.AWS_ACCESS_KEY,
439
445
  secretAccessKey: process.env.AWS_SECRET_KEY,
440
446
  },
447
+ isActive: true,
448
+ isDefault: true, // Set as default configuration
441
449
  },
442
450
  user,
443
451
  );
@@ -453,6 +461,8 @@ export class SetupService {
453
461
  privateKey: process.env.SFTP_PRIVATE_KEY,
454
462
  basePath: '/backups',
455
463
  },
464
+ isActive: true,
465
+ isDefault: false, // Not the default
456
466
  },
457
467
  user,
458
468
  );
@@ -462,25 +472,34 @@ export class SetupService {
462
472
 
463
473
  ### Default Configuration Resolution
464
474
 
475
+ When `storageConfigId` is not provided for uploads, the system automatically resolves the default config:
476
+
477
+ 1. **Priority 1:** Find config with `isDefault: true` and `isActive: true`
478
+ 2. **Priority 2:** Fall back to oldest active config
479
+
465
480
  ```typescript
466
481
  // StorageProviderConfigService
467
482
  async getDefaultConfig(user?: ILoggedUserInfo): Promise<StorageConfigBase | null> {
483
+ await this.ensureRepositoryInitialized();
484
+
485
+ const baseWhere: any = { isActive: true };
486
+
468
487
  // Filter by company if enabled
469
- const where: any = {};
470
- if (this.isCompanyFeatureEnabled() && user?.companyId) {
471
- where.companyId = user.companyId;
488
+ if (this.storageConfig.isCompanyFeatureEnabled() && user?.companyId) {
489
+ baseWhere.companyId = user.companyId;
472
490
  }
473
491
 
474
- // First try 'default' named config
492
+ // First try to find config marked as default
475
493
  const defaultConfig = await this.repository.findOne({
476
- where: { ...where, name: 'default' },
494
+ where: { ...baseWhere, isDefault: true },
495
+ order: { createdAt: 'ASC' },
477
496
  });
478
497
 
479
498
  if (defaultConfig) return defaultConfig;
480
499
 
481
- // Fall back to any available config
500
+ // Fall back to oldest active config
482
501
  return await this.repository.findOne({
483
- where,
502
+ where: baseWhere,
484
503
  order: { createdAt: 'ASC' },
485
504
  });
486
505
  }
@@ -1279,10 +1298,4 @@ The `@flusys/nestjs-storage` package provides:
1279
1298
 
1280
1299
  ---
1281
1300
 
1282
- **Last Updated:** 2026-02-09
1283
-
1284
- **Recent Changes:**
1285
- - 2026-02-09: Added explicit `@Inject()` decorators to all services for esbuild bundling compatibility
1286
- - 2026-02-09: Fixed `instanceof` checks in FolderService and FileManagerService - replaced with `'id' in dto` pattern
1287
- - 2026-02-07: Condensed DataSource Provider pattern documentation
1288
- - 2026-02-07: Simplified provider registration section
1301
+ **Last Updated:** 2026-02-16
@@ -9,11 +9,11 @@ Object.defineProperty(exports, "FileManagerController", {
9
9
  }
10
10
  });
11
11
  const _common = require("@nestjs/common");
12
- const _guards = require("@flusys/nestjs-shared/guards");
13
12
  const _classes = require("@flusys/nestjs-shared/classes");
14
13
  const _decorators = require("@flusys/nestjs-shared/decorators");
15
- const _interfaces = require("@flusys/nestjs-shared/interfaces");
16
14
  const _dtos = require("@flusys/nestjs-shared/dtos");
15
+ const _guards = require("@flusys/nestjs-shared/guards");
16
+ const _interfaces = require("@flusys/nestjs-shared/interfaces");
17
17
  const _dtos1 = require("../dtos");
18
18
  const _swagger = require("@nestjs/swagger");
19
19
  const _express = require("express");
@@ -45,7 +45,9 @@ function _ts_param(paramIndex, decorator) {
45
45
  decorator(target, key, paramIndex);
46
46
  };
47
47
  }
48
- let FileManagerController = class FileManagerController extends (0, _classes.createApiController)(_dtos1.CreateFileManagerDto, _dtos1.UpdateFileManagerDto, _dtos1.FileManagerResponseDto) {
48
+ let FileManagerController = class FileManagerController extends (0, _classes.createApiController)(_dtos1.CreateFileManagerDto, _dtos1.UpdateFileManagerDto, _dtos1.FileManagerResponseDto, {
49
+ security: 'jwt'
50
+ }) {
49
51
  async getFiles(dto, req, user) {
50
52
  if (!dto || !dto.length) {
51
53
  throw new _common.BadRequestException('No files provided');
@@ -64,6 +66,8 @@ let FileManagerController = class FileManagerController extends (0, _classes.cre
64
66
  };
65
67
  _ts_decorate([
66
68
  (0, _common.Post)('get-files'),
69
+ (0, _common.UseGuards)(_guards.JwtAuthGuard),
70
+ (0, _swagger.ApiBearerAuth)(),
67
71
  (0, _common.HttpCode)(200),
68
72
  (0, _swagger.ApiBody)({
69
73
  type: _dtos1.GetFilesRequestDto,
@@ -90,9 +94,7 @@ _ts_decorate([
90
94
  ], FileManagerController.prototype, "getFiles", null);
91
95
  FileManagerController = _ts_decorate([
92
96
  (0, _swagger.ApiTags)('Files'),
93
- (0, _swagger.ApiBearerAuth)(),
94
97
  (0, _common.Controller)('storage/file-manager'),
95
- (0, _common.UseGuards)(_guards.JwtAuthGuard),
96
98
  _ts_param(0, (0, _common.Inject)(_filemanagerservice.FileManagerService)),
97
99
  _ts_metadata("design:type", Function),
98
100
  _ts_metadata("design:paramtypes", [
@@ -8,7 +8,6 @@ Object.defineProperty(exports, "FolderController", {
8
8
  return FolderController;
9
9
  }
10
10
  });
11
- const _guards = require("@flusys/nestjs-shared/guards");
12
11
  const _classes = require("@flusys/nestjs-shared/classes");
13
12
  const _dtos = require("../dtos");
14
13
  const _common = require("@nestjs/common");
@@ -41,16 +40,16 @@ function _ts_param(paramIndex, decorator) {
41
40
  decorator(target, key, paramIndex);
42
41
  };
43
42
  }
44
- let FolderController = class FolderController extends (0, _classes.createApiController)(_dtos.CreateFolderDto, _dtos.UpdateFolderDto, _dtos.FolderResponseDto) {
43
+ let FolderController = class FolderController extends (0, _classes.createApiController)(_dtos.CreateFolderDto, _dtos.UpdateFolderDto, _dtos.FolderResponseDto, {
44
+ security: 'jwt'
45
+ }) {
45
46
  constructor(folderService){
46
47
  super(folderService), _define_property(this, "folderService", void 0), this.folderService = folderService;
47
48
  }
48
49
  };
49
50
  FolderController = _ts_decorate([
50
51
  (0, _swagger.ApiTags)('Folders'),
51
- (0, _swagger.ApiBearerAuth)(),
52
52
  (0, _common.Controller)('storage/folder'),
53
- (0, _common.UseGuards)(_guards.JwtAuthGuard),
54
53
  _ts_param(0, (0, _common.Inject)(_folderservice.FolderService)),
55
54
  _ts_metadata("design:type", Function),
56
55
  _ts_metadata("design:paramtypes", [
@@ -8,7 +8,6 @@ Object.defineProperty(exports, "StorageConfigController", {
8
8
  return StorageConfigController;
9
9
  }
10
10
  });
11
- const _guards = require("@flusys/nestjs-shared/guards");
12
11
  const _classes = require("@flusys/nestjs-shared/classes");
13
12
  const _dtos = require("../dtos");
14
13
  const _common = require("@nestjs/common");
@@ -41,16 +40,16 @@ function _ts_param(paramIndex, decorator) {
41
40
  decorator(target, key, paramIndex);
42
41
  };
43
42
  }
44
- let StorageConfigController = class StorageConfigController extends (0, _classes.createApiController)(_dtos.CreateStorageConfigDto, _dtos.UpdateStorageConfigDto, _dtos.StorageConfigResponseDto) {
43
+ let StorageConfigController = class StorageConfigController extends (0, _classes.createApiController)(_dtos.CreateStorageConfigDto, _dtos.UpdateStorageConfigDto, _dtos.StorageConfigResponseDto, {
44
+ security: 'jwt'
45
+ }) {
45
46
  constructor(storageConfigService){
46
47
  super(storageConfigService), _define_property(this, "storageConfigService", void 0), this.storageConfigService = storageConfigService;
47
48
  }
48
49
  };
49
50
  StorageConfigController = _ts_decorate([
50
51
  (0, _swagger.ApiTags)('Storage Config'),
51
- (0, _swagger.ApiBearerAuth)(),
52
52
  (0, _common.Controller)('storage/storage-config'),
53
- (0, _common.UseGuards)(_guards.JwtAuthGuard),
54
53
  _ts_param(0, (0, _common.Inject)(_storageproviderconfigservice.StorageProviderConfigService)),
55
54
  _ts_metadata("design:type", Function),
56
55
  _ts_metadata("design:paramtypes", [
@@ -50,6 +50,8 @@ let CreateStorageConfigDto = class CreateStorageConfigDto {
50
50
  _define_property(this, "name", void 0);
51
51
  _define_property(this, "storage", void 0);
52
52
  _define_property(this, "config", void 0);
53
+ _define_property(this, "isActive", void 0);
54
+ _define_property(this, "isDefault", void 0);
53
55
  }
54
56
  };
55
57
  _ts_decorate([
@@ -97,12 +99,32 @@ _ts_decorate([
97
99
  (0, _classvalidator.IsNotEmpty)(),
98
100
  _ts_metadata("design:type", typeof Record === "undefined" ? Object : Record)
99
101
  ], CreateStorageConfigDto.prototype, "config", void 0);
102
+ _ts_decorate([
103
+ (0, _swagger.ApiPropertyOptional)({
104
+ example: true,
105
+ description: 'Whether storage configuration is active'
106
+ }),
107
+ (0, _classvalidator.IsBoolean)(),
108
+ (0, _classvalidator.IsOptional)(),
109
+ _ts_metadata("design:type", Boolean)
110
+ ], CreateStorageConfigDto.prototype, "isActive", void 0);
111
+ _ts_decorate([
112
+ (0, _swagger.ApiPropertyOptional)({
113
+ example: false,
114
+ description: 'Set as default storage configuration'
115
+ }),
116
+ (0, _classvalidator.IsBoolean)(),
117
+ (0, _classvalidator.IsOptional)(),
118
+ _ts_metadata("design:type", Boolean)
119
+ ], CreateStorageConfigDto.prototype, "isDefault", void 0);
100
120
  let UpdateStorageConfigDto = class UpdateStorageConfigDto {
101
121
  constructor(){
102
122
  _define_property(this, "id", void 0);
103
123
  _define_property(this, "name", void 0);
104
124
  _define_property(this, "storage", void 0);
105
125
  _define_property(this, "config", void 0);
126
+ _define_property(this, "isActive", void 0);
127
+ _define_property(this, "isDefault", void 0);
106
128
  }
107
129
  };
108
130
  _ts_decorate([
@@ -157,8 +179,26 @@ _ts_decorate([
157
179
  (0, _classvalidator.IsOptional)(),
158
180
  _ts_metadata("design:type", typeof Record === "undefined" ? Object : Record)
159
181
  ], UpdateStorageConfigDto.prototype, "config", void 0);
182
+ _ts_decorate([
183
+ (0, _swagger.ApiPropertyOptional)({
184
+ example: true,
185
+ description: 'Whether storage configuration is active'
186
+ }),
187
+ (0, _classvalidator.IsBoolean)(),
188
+ (0, _classvalidator.IsOptional)(),
189
+ _ts_metadata("design:type", Boolean)
190
+ ], UpdateStorageConfigDto.prototype, "isActive", void 0);
191
+ _ts_decorate([
192
+ (0, _swagger.ApiPropertyOptional)({
193
+ example: false,
194
+ description: 'Set as default storage configuration'
195
+ }),
196
+ (0, _classvalidator.IsBoolean)(),
197
+ (0, _classvalidator.IsOptional)(),
198
+ _ts_metadata("design:type", Boolean)
199
+ ], UpdateStorageConfigDto.prototype, "isDefault", void 0);
160
200
  let StorageConfigResponseDto = class StorageConfigResponseDto extends _dtos.IdentityResponseDto {
161
201
  constructor(...args){
162
- super(...args), _define_property(this, "name", void 0), _define_property(this, "storage", void 0), _define_property(this, "config", void 0);
202
+ super(...args), _define_property(this, "name", void 0), _define_property(this, "storage", void 0), _define_property(this, "config", void 0), _define_property(this, "isActive", void 0), _define_property(this, "isDefault", void 0);
163
203
  }
164
204
  };
@@ -35,8 +35,7 @@ function _ts_metadata(k, v) {
35
35
  }
36
36
  let StorageConfigBase = class StorageConfigBase extends _entities.Identity {
37
37
  constructor(...args){
38
- super(...args), _define_property(this, "name", void 0), _define_property(this, "storage", void 0), // add config for storage
39
- _define_property(this, "config", void 0);
38
+ super(...args), _define_property(this, "name", void 0), _define_property(this, "storage", void 0), _define_property(this, "config", void 0), _define_property(this, "isActive", void 0), _define_property(this, "isDefault", void 0);
40
39
  }
41
40
  };
42
41
  _ts_decorate([
@@ -62,3 +61,33 @@ _ts_decorate([
62
61
  }),
63
62
  _ts_metadata("design:type", typeof Record === "undefined" ? Object : Record)
64
63
  ], StorageConfigBase.prototype, "config", void 0);
64
+ _ts_decorate([
65
+ (0, _typeorm.Column)({
66
+ type: 'boolean',
67
+ default: true,
68
+ name: 'is_active'
69
+ }),
70
+ _ts_metadata("design:type", Boolean)
71
+ ], StorageConfigBase.prototype, "isActive", void 0);
72
+ _ts_decorate([
73
+ (0, _typeorm.Column)({
74
+ type: 'boolean',
75
+ default: false,
76
+ name: 'is_default'
77
+ }),
78
+ _ts_metadata("design:type", Boolean)
79
+ ], StorageConfigBase.prototype, "isDefault", void 0);
80
+ StorageConfigBase = _ts_decorate([
81
+ (0, _typeorm.Index)([
82
+ 'name'
83
+ ]),
84
+ (0, _typeorm.Index)([
85
+ 'storage'
86
+ ]),
87
+ (0, _typeorm.Index)([
88
+ 'isActive'
89
+ ]),
90
+ (0, _typeorm.Index)([
91
+ 'isDefault'
92
+ ])
93
+ ], StorageConfigBase);
@@ -117,10 +117,8 @@ let StorageModule = class StorageModule {
117
117
  ]
118
118
  };
119
119
  }
120
- // ==================== Private Helper Methods ====================
121
- /**
122
- * Get controllers (all controllers always loaded)
123
- */ static getControllers(options) {
120
+ // Private Helper Methods
121
+ /** Get controllers (all controllers always loaded) */ static getControllers(options) {
124
122
  return [
125
123
  _controllers.FileManagerController,
126
124
  _controllers.FolderController,
@@ -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,61 +42,47 @@ 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',
76
66
  'storage',
77
67
  'config',
68
+ 'isActive',
69
+ 'isDefault',
78
70
  'createdAt',
79
71
  'updatedAt'
80
72
  ];
81
- // Add company fields if company feature is enabled
82
73
  if (this.storageConfig.isCompanyFeatureEnabled()) {
83
74
  select.push('companyId');
84
75
  }
85
76
  }
86
- const selectFields = select.map((field)=>`${this.entityName}.${field}`);
87
- query.select(selectFields);
77
+ query.select(select.map((f)=>`${this.entityName}.${f}`));
88
78
  return {
89
79
  query,
90
80
  isRaw: false
91
81
  };
92
82
  }
93
- /**
94
- * Override: Extra query manipulation - Auto-filter by user's company
95
- */ async getExtraManipulateQuery(query, filterDto, user) {
83
+ async getExtraManipulateQuery(query, filterDto, user) {
96
84
  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) {
85
+ if (this.storageConfig.isCompanyFeatureEnabled() && user?.companyId) {
100
86
  query.andWhere('storageConfig.companyId = :companyId', {
101
87
  companyId: user.companyId
102
88
  });
@@ -118,19 +104,21 @@ let StorageProviderConfigService = class StorageProviderConfigService extends _c
118
104
  }
119
105
  /**
120
106
  * Get default storage configuration (scoped to user's company if enabled)
121
- * Falls back to any available config if 'default' not found
107
+ * Priority: isDefault=true > any active config (oldest first)
122
108
  */ async getDefaultConfig(user) {
123
109
  await this.ensureRepositoryInitialized();
124
- const baseWhere = {};
110
+ const baseWhere = {
111
+ isActive: true
112
+ };
125
113
  // Filter by company only if company feature is enabled and user is provided
126
114
  if (this.storageConfig.isCompanyFeatureEnabled() && user?.companyId) {
127
115
  baseWhere.companyId = user.companyId;
128
116
  }
129
- // First try to find config named 'default'
117
+ // First try to find config marked as default
130
118
  const defaultConfig = await this.repository.findOne({
131
119
  where: {
132
120
  ...baseWhere,
133
- name: 'default'
121
+ isDefault: true
134
122
  },
135
123
  order: {
136
124
  createdAt: 'ASC'
@@ -139,7 +127,7 @@ let StorageProviderConfigService = class StorageProviderConfigService extends _c
139
127
  if (defaultConfig) {
140
128
  return defaultConfig;
141
129
  }
142
- // Fall back to any available config for this company/user
130
+ // Fall back to any available active config (oldest first for consistency)
143
131
  return await this.repository.findOne({
144
132
  where: baseWhere,
145
133
  order: {
@@ -152,7 +140,8 @@ let StorageProviderConfigService = class StorageProviderConfigService extends _c
152
140
  */ async getConfigByType(storage, user) {
153
141
  await this.ensureRepositoryInitialized();
154
142
  const where = {
155
- storage
143
+ storage,
144
+ isActive: true
156
145
  };
157
146
  // Filter by company only if company feature is enabled and user is provided
158
147
  if (this.storageConfig.isCompanyFeatureEnabled() && user?.companyId) {
@@ -162,9 +151,7 @@ let StorageProviderConfigService = class StorageProviderConfigService extends _c
162
151
  where
163
152
  });
164
153
  }
165
- // NOTE: @Inject() required for bundled code - type metadata may be lost during esbuild
166
154
  constructor(cacheManager, utilsService, storageConfig, dataSourceProvider){
167
- // Repository will be set asynchronously by RequestScopedApiService
168
155
  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;
169
156
  }
170
157
  };
@@ -1,5 +1,5 @@
1
- import { ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
2
1
  import { SingleResponseDto } from '@flusys/nestjs-shared/dtos';
2
+ import { ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
3
3
  import { CreateFileManagerDto, FileManagerResponseDto, FilesResponseDto, GetFilesRequestDto, UpdateFileManagerDto } from '../dtos';
4
4
  import { Request } from 'express';
5
5
  import { FileManagerService } from '../services/file-manager.service';
@@ -5,6 +5,8 @@ export declare class CreateStorageConfigDto implements Record<string, unknown> {
5
5
  name: string;
6
6
  storage: FileLocationEnum;
7
7
  config: Record<string, any>;
8
+ isActive?: boolean;
9
+ isDefault?: boolean;
8
10
  }
9
11
  export declare class UpdateStorageConfigDto implements Record<string, unknown> {
10
12
  [key: string]: unknown;
@@ -12,10 +14,14 @@ export declare class UpdateStorageConfigDto implements Record<string, unknown> {
12
14
  name?: string;
13
15
  storage?: FileLocationEnum;
14
16
  config?: Record<string, any>;
17
+ isActive?: boolean;
18
+ isDefault?: boolean;
15
19
  }
16
20
  export declare class StorageConfigResponseDto extends IdentityResponseDto implements Record<string, unknown> {
17
21
  [key: string]: unknown;
18
22
  name: string;
19
23
  storage: FileLocationEnum;
20
24
  config: Record<string, any>;
25
+ isActive: boolean;
26
+ isDefault: boolean;
21
27
  }
@@ -4,4 +4,6 @@ export declare abstract class StorageConfigBase extends Identity {
4
4
  name: string;
5
5
  storage: FileLocationEnum;
6
6
  config: Record<string, any>;
7
+ isActive: boolean;
8
+ isDefault: boolean;
7
9
  }
@@ -26,16 +26,18 @@ function _ts_param(paramIndex, decorator) {
26
26
  };
27
27
  }
28
28
  import { BadRequestException, Body, Controller, HttpCode, Inject, Post, Req, UseGuards } from '@nestjs/common';
29
- import { JwtAuthGuard } from '@flusys/nestjs-shared/guards';
30
29
  import { createApiController } from '@flusys/nestjs-shared/classes';
31
30
  import { CurrentUser } from '@flusys/nestjs-shared/decorators';
32
- import { ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
33
31
  import { SingleResponseDto } from '@flusys/nestjs-shared/dtos';
32
+ import { JwtAuthGuard } from '@flusys/nestjs-shared/guards';
33
+ import { ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
34
34
  import { CreateFileManagerDto, FileManagerResponseDto, GetFilesRequestDto, UpdateFileManagerDto } from '../dtos';
35
35
  import { ApiBearerAuth, ApiBody, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
36
36
  import { Request } from 'express';
37
37
  import { FileManagerService } from '../services/file-manager.service';
38
- export class FileManagerController extends createApiController(CreateFileManagerDto, UpdateFileManagerDto, FileManagerResponseDto) {
38
+ export class FileManagerController extends createApiController(CreateFileManagerDto, UpdateFileManagerDto, FileManagerResponseDto, {
39
+ security: 'jwt'
40
+ }) {
39
41
  async getFiles(dto, req, user) {
40
42
  if (!dto || !dto.length) {
41
43
  throw new BadRequestException('No files provided');
@@ -54,6 +56,8 @@ export class FileManagerController extends createApiController(CreateFileManager
54
56
  }
55
57
  _ts_decorate([
56
58
  Post('get-files'),
59
+ UseGuards(JwtAuthGuard),
60
+ ApiBearerAuth(),
57
61
  HttpCode(200),
58
62
  ApiBody({
59
63
  type: GetFilesRequestDto,
@@ -80,9 +84,7 @@ _ts_decorate([
80
84
  ], FileManagerController.prototype, "getFiles", null);
81
85
  FileManagerController = _ts_decorate([
82
86
  ApiTags('Files'),
83
- ApiBearerAuth(),
84
87
  Controller('storage/file-manager'),
85
- UseGuards(JwtAuthGuard),
86
88
  _ts_param(0, Inject(FileManagerService)),
87
89
  _ts_metadata("design:type", Function),
88
90
  _ts_metadata("design:paramtypes", [
@@ -25,22 +25,21 @@ function _ts_param(paramIndex, decorator) {
25
25
  decorator(target, key, paramIndex);
26
26
  };
27
27
  }
28
- import { JwtAuthGuard } from '@flusys/nestjs-shared/guards';
29
28
  import { createApiController } from '@flusys/nestjs-shared/classes';
30
29
  import { CreateFolderDto, FolderResponseDto, UpdateFolderDto } from '../dtos';
31
- import { Controller, Inject, UseGuards } from '@nestjs/common';
32
- import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
30
+ import { Controller, Inject } from '@nestjs/common';
31
+ import { ApiTags } from '@nestjs/swagger';
33
32
  import { FolderService } from '../services/folder.service';
34
- export class FolderController extends createApiController(CreateFolderDto, UpdateFolderDto, FolderResponseDto) {
33
+ export class FolderController extends createApiController(CreateFolderDto, UpdateFolderDto, FolderResponseDto, {
34
+ security: 'jwt'
35
+ }) {
35
36
  constructor(folderService){
36
37
  super(folderService), _define_property(this, "folderService", void 0), this.folderService = folderService;
37
38
  }
38
39
  }
39
40
  FolderController = _ts_decorate([
40
41
  ApiTags('Folders'),
41
- ApiBearerAuth(),
42
42
  Controller('storage/folder'),
43
- UseGuards(JwtAuthGuard),
44
43
  _ts_param(0, Inject(FolderService)),
45
44
  _ts_metadata("design:type", Function),
46
45
  _ts_metadata("design:paramtypes", [
@@ -25,22 +25,21 @@ function _ts_param(paramIndex, decorator) {
25
25
  decorator(target, key, paramIndex);
26
26
  };
27
27
  }
28
- import { JwtAuthGuard } from '@flusys/nestjs-shared/guards';
29
28
  import { createApiController } from '@flusys/nestjs-shared/classes';
30
29
  import { CreateStorageConfigDto, StorageConfigResponseDto, UpdateStorageConfigDto } from '../dtos';
31
- import { Controller, Inject, UseGuards } from '@nestjs/common';
32
- import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
30
+ import { Controller, Inject } from '@nestjs/common';
31
+ import { ApiTags } from '@nestjs/swagger';
33
32
  import { StorageProviderConfigService } from '../services/storage-provider-config.service';
34
- export class StorageConfigController extends createApiController(CreateStorageConfigDto, UpdateStorageConfigDto, StorageConfigResponseDto) {
33
+ export class StorageConfigController extends createApiController(CreateStorageConfigDto, UpdateStorageConfigDto, StorageConfigResponseDto, {
34
+ security: 'jwt'
35
+ }) {
35
36
  constructor(storageConfigService){
36
37
  super(storageConfigService), _define_property(this, "storageConfigService", void 0), this.storageConfigService = storageConfigService;
37
38
  }
38
39
  }
39
40
  StorageConfigController = _ts_decorate([
40
41
  ApiTags('Storage Config'),
41
- ApiBearerAuth(),
42
42
  Controller('storage/storage-config'),
43
- UseGuards(JwtAuthGuard),
44
43
  _ts_param(0, Inject(StorageProviderConfigService)),
45
44
  _ts_metadata("design:type", Function),
46
45
  _ts_metadata("design:paramtypes", [
@@ -23,7 +23,7 @@ function _ts_metadata(k, v) {
23
23
  import { IdentityResponseDto } from '@flusys/nestjs-shared/dtos';
24
24
  import { FileLocationEnum } from '../enums';
25
25
  import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
26
- import { IsEnum, IsNotEmpty, IsObject, IsOptional, IsString, MaxLength } from 'class-validator';
26
+ import { IsBoolean, IsEnum, IsNotEmpty, IsObject, IsOptional, IsString, MaxLength } from 'class-validator';
27
27
  /**
28
28
  * DTO for creating storage configuration
29
29
  */ export class CreateStorageConfigDto {
@@ -31,6 +31,8 @@ import { IsEnum, IsNotEmpty, IsObject, IsOptional, IsString, MaxLength } from 'c
31
31
  _define_property(this, "name", void 0);
32
32
  _define_property(this, "storage", void 0);
33
33
  _define_property(this, "config", void 0);
34
+ _define_property(this, "isActive", void 0);
35
+ _define_property(this, "isDefault", void 0);
34
36
  }
35
37
  }
36
38
  _ts_decorate([
@@ -78,6 +80,24 @@ _ts_decorate([
78
80
  IsNotEmpty(),
79
81
  _ts_metadata("design:type", typeof Record === "undefined" ? Object : Record)
80
82
  ], CreateStorageConfigDto.prototype, "config", void 0);
83
+ _ts_decorate([
84
+ ApiPropertyOptional({
85
+ example: true,
86
+ description: 'Whether storage configuration is active'
87
+ }),
88
+ IsBoolean(),
89
+ IsOptional(),
90
+ _ts_metadata("design:type", Boolean)
91
+ ], CreateStorageConfigDto.prototype, "isActive", void 0);
92
+ _ts_decorate([
93
+ ApiPropertyOptional({
94
+ example: false,
95
+ description: 'Set as default storage configuration'
96
+ }),
97
+ IsBoolean(),
98
+ IsOptional(),
99
+ _ts_metadata("design:type", Boolean)
100
+ ], CreateStorageConfigDto.prototype, "isDefault", void 0);
81
101
  /**
82
102
  * DTO for updating storage configuration
83
103
  */ export class UpdateStorageConfigDto {
@@ -86,6 +106,8 @@ _ts_decorate([
86
106
  _define_property(this, "name", void 0);
87
107
  _define_property(this, "storage", void 0);
88
108
  _define_property(this, "config", void 0);
109
+ _define_property(this, "isActive", void 0);
110
+ _define_property(this, "isDefault", void 0);
89
111
  }
90
112
  }
91
113
  _ts_decorate([
@@ -140,10 +162,28 @@ _ts_decorate([
140
162
  IsOptional(),
141
163
  _ts_metadata("design:type", typeof Record === "undefined" ? Object : Record)
142
164
  ], UpdateStorageConfigDto.prototype, "config", void 0);
165
+ _ts_decorate([
166
+ ApiPropertyOptional({
167
+ example: true,
168
+ description: 'Whether storage configuration is active'
169
+ }),
170
+ IsBoolean(),
171
+ IsOptional(),
172
+ _ts_metadata("design:type", Boolean)
173
+ ], UpdateStorageConfigDto.prototype, "isActive", void 0);
174
+ _ts_decorate([
175
+ ApiPropertyOptional({
176
+ example: false,
177
+ description: 'Set as default storage configuration'
178
+ }),
179
+ IsBoolean(),
180
+ IsOptional(),
181
+ _ts_metadata("design:type", Boolean)
182
+ ], UpdateStorageConfigDto.prototype, "isDefault", void 0);
143
183
  /**
144
184
  * Response DTO for storage configuration
145
185
  */ export class StorageConfigResponseDto extends IdentityResponseDto {
146
186
  constructor(...args){
147
- super(...args), _define_property(this, "name", void 0), _define_property(this, "storage", void 0), _define_property(this, "config", void 0);
187
+ super(...args), _define_property(this, "name", void 0), _define_property(this, "storage", void 0), _define_property(this, "config", void 0), _define_property(this, "isActive", void 0), _define_property(this, "isDefault", void 0);
148
188
  }
149
189
  }
@@ -22,14 +22,10 @@ function _ts_metadata(k, v) {
22
22
  }
23
23
  import { Identity } from '@flusys/nestjs-shared/entities';
24
24
  import { FileLocationEnum } from '../enums';
25
- import { Column } from 'typeorm';
26
- /**
27
- * Base StorageConfig Entity
28
- * Contains common fields shared by both StorageConfig and StorageConfigWithCompany
29
- */ export class StorageConfigBase extends Identity {
25
+ import { Column, Index } from 'typeorm';
26
+ export class StorageConfigBase extends Identity {
30
27
  constructor(...args){
31
- super(...args), _define_property(this, "name", void 0), _define_property(this, "storage", void 0), // add config for storage
32
- _define_property(this, "config", void 0);
28
+ super(...args), _define_property(this, "name", void 0), _define_property(this, "storage", void 0), _define_property(this, "config", void 0), _define_property(this, "isActive", void 0), _define_property(this, "isDefault", void 0);
33
29
  }
34
30
  }
35
31
  _ts_decorate([
@@ -55,3 +51,33 @@ _ts_decorate([
55
51
  }),
56
52
  _ts_metadata("design:type", typeof Record === "undefined" ? Object : Record)
57
53
  ], StorageConfigBase.prototype, "config", void 0);
54
+ _ts_decorate([
55
+ Column({
56
+ type: 'boolean',
57
+ default: true,
58
+ name: 'is_active'
59
+ }),
60
+ _ts_metadata("design:type", Boolean)
61
+ ], StorageConfigBase.prototype, "isActive", void 0);
62
+ _ts_decorate([
63
+ Column({
64
+ type: 'boolean',
65
+ default: false,
66
+ name: 'is_default'
67
+ }),
68
+ _ts_metadata("design:type", Boolean)
69
+ ], StorageConfigBase.prototype, "isDefault", void 0);
70
+ StorageConfigBase = _ts_decorate([
71
+ Index([
72
+ 'name'
73
+ ]),
74
+ Index([
75
+ 'storage'
76
+ ]),
77
+ Index([
78
+ 'isActive'
79
+ ]),
80
+ Index([
81
+ 'isDefault'
82
+ ])
83
+ ], StorageConfigBase);
@@ -107,10 +107,8 @@ export class StorageModule {
107
107
  ]
108
108
  };
109
109
  }
110
- // ==================== Private Helper Methods ====================
111
- /**
112
- * Get controllers (all controllers always loaded)
113
- */ static getControllers(options) {
110
+ // Private Helper Methods
111
+ /** Get controllers (all controllers always loaded) */ static getControllers(options) {
114
112
  return [
115
113
  FileManagerController,
116
114
  FolderController,
@@ -27,51 +27,29 @@ function _ts_param(paramIndex, decorator) {
27
27
  }
28
28
  import { RequestScopedApiService, HybridCache } from '@flusys/nestjs-shared/classes';
29
29
  import { UtilsService } from '@flusys/nestjs-shared/modules';
30
- import { Inject, Injectable, NotFoundException, Scope } from '@nestjs/common';
30
+ import { Inject, Injectable, Scope } from '@nestjs/common';
31
31
  import { StorageConfigService } from '../config';
32
32
  import { Folder, FolderWithCompany } from '../entities';
33
33
  import { StorageDataSourceProvider } from './storage-datasource.provider';
34
34
  export class FolderService extends RequestScopedApiService {
35
- /**
36
- * Resolve entity class for this service
37
- * @returns Folder or FolderWithCompany based on configuration
38
- */ resolveEntity() {
39
- const enableCompanyFeature = this.storageConfig.isCompanyFeatureEnabled();
40
- return enableCompanyFeature ? FolderWithCompany : Folder;
35
+ resolveEntity() {
36
+ return this.storageConfig.isCompanyFeatureEnabled() ? FolderWithCompany : Folder;
41
37
  }
42
- /**
43
- * Get DataSource provider for this service
44
- * @returns StorageDataSourceProvider instance
45
- */ getDataSourceProvider() {
38
+ getDataSourceProvider() {
46
39
  return this.dataSourceProvider;
47
40
  }
41
+ // Entity Conversion
48
42
  async convertSingleDtoToEntity(dto, user) {
49
- let folder = {};
50
- // NOTE: Using 'id' in dto check instead of instanceof - instanceof may not work after esbuild bundling
51
- if ('id' in dto && dto.id && typeof dto.id === 'string') {
52
- const dbData = await this.repository.findOne({
53
- where: {
54
- id: dto.id
55
- }
56
- });
57
- if (!dbData) {
58
- throw new NotFoundException('No such entity data found for update! Please, Try Again.');
59
- }
60
- folder = dbData;
61
- }
62
- // Set company/branch IDs if company feature is enabled
63
- folder = {
64
- ...folder,
65
- ...dto
66
- };
67
- // Only set company fields if they exist on the entity (when company feature is enabled)
68
- if ('companyId' in folder) {
69
- folder.companyId = user?.companyId ?? null;
43
+ const entity = await super.convertSingleDtoToEntity(dto, user);
44
+ // Set companyId from user context if company feature enabled
45
+ if (this.storageConfig.isCompanyFeatureEnabled()) {
46
+ entity.companyId = user?.companyId ?? null;
70
47
  }
71
- return folder;
48
+ return entity;
72
49
  }
50
+ // Query Customization
73
51
  async getSelectQuery(query, _user, select) {
74
- if (!select || !select.length) {
52
+ if (!select?.length) {
75
53
  select = [
76
54
  'id',
77
55
  'name',
@@ -79,35 +57,26 @@ export class FolderService extends RequestScopedApiService {
79
57
  'createdAt',
80
58
  'deletedAt'
81
59
  ];
60
+ if (this.storageConfig.isCompanyFeatureEnabled()) {
61
+ select.push('companyId');
62
+ }
82
63
  }
83
- const selectFields = select.map((field)=>`${this.entityName}.${field}`);
84
- // Add company context fields if company feature is enabled
85
- // The entity will have these fields only if company feature is enabled
86
- if (this.storageConfig.isCompanyFeatureEnabled()) {
87
- selectFields.push('folder.companyId');
88
- }
89
- query.select(selectFields);
64
+ query.select(select.map((f)=>`${this.entityName}.${f}`));
90
65
  return {
91
66
  query,
92
67
  isRaw: false
93
68
  };
94
69
  }
95
- /**
96
- * Override: Extra query manipulation - Auto-filter by user's company
97
- */ async getExtraManipulateQuery(query, filterDto, user) {
70
+ async getExtraManipulateQuery(query, filterDto, user) {
98
71
  const result = await super.getExtraManipulateQuery(query, filterDto, user);
99
- // If company feature enabled and user has companyId, filter by user's company
100
- const enableCompanyFeature = this.storageConfig.isCompanyFeatureEnabled();
101
- if (enableCompanyFeature && user?.companyId) {
72
+ if (this.storageConfig.isCompanyFeatureEnabled() && user?.companyId) {
102
73
  query.andWhere('folder.companyId = :companyId', {
103
74
  companyId: user.companyId
104
75
  });
105
76
  }
106
77
  return result;
107
78
  }
108
- // NOTE: @Inject() required for bundled code - type metadata may be lost during esbuild
109
79
  constructor(cacheManager, utilsService, storageConfig, dataSourceProvider){
110
- // Repository will be set asynchronously by RequestScopedApiService
111
80
  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;
112
81
  }
113
82
  }
@@ -32,10 +32,8 @@ import { Request } from 'express';
32
32
  import { StorageModuleOptions } from '../interfaces';
33
33
  import { STORAGE_MODULE_OPTIONS } from '../config/storage.constants';
34
34
  export class StorageDataSourceProvider extends MultiTenantDataSourceService {
35
- // ==================== Factory Methods ====================
36
- /**
37
- * Build parent options from StorageModuleOptions
38
- */ static buildParentOptions(options) {
35
+ // Factory Methods
36
+ /** Build parent options from StorageModuleOptions */ static buildParentOptions(options) {
39
37
  return {
40
38
  bootstrapAppConfig: options.bootstrapAppConfig,
41
39
  defaultDatabaseConfig: options.config?.defaultDatabaseConfig,
@@ -43,10 +41,8 @@ export class StorageDataSourceProvider extends MultiTenantDataSourceService {
43
41
  tenants: options.config?.tenants
44
42
  };
45
43
  }
46
- // ==================== Feature Flags ====================
47
- /**
48
- * Get global enable company feature flag
49
- */ getEnableCompanyFeature() {
44
+ // Feature Flags
45
+ /** Get global enable company feature flag */ getEnableCompanyFeature() {
50
46
  return this.storageOptions.bootstrapAppConfig?.enableCompanyFeature ?? false;
51
47
  }
52
48
  /**
@@ -60,11 +56,11 @@ export class StorageDataSourceProvider extends MultiTenantDataSourceService {
60
56
  */ getEnableCompanyFeatureForCurrentTenant() {
61
57
  return this.getEnableCompanyFeatureForTenant(this.getCurrentTenant() ?? undefined);
62
58
  }
63
- // ==================== Entity Management ====================
59
+ // Entity Management
64
60
  /**
65
- * Get storage entities for migrations based on company feature flag
66
- * Note: For TypeORM repositories, we always use the base entities (FileManager, etc.)
67
- * But for migrations, we need the correct entity based on the feature flag
61
+ * Get storage entities for migrations based on company feature flag.
62
+ * For TypeORM repositories, we always use the base entities (FileManager, etc.)
63
+ * but for migrations, we need the correct entity based on the feature flag.
68
64
  */ async getStorageEntities(enableCompanyFeature) {
69
65
  const enable = enableCompanyFeature ?? this.getEnableCompanyFeature();
70
66
  const { FileManager, Folder, StorageConfig } = await import('../entities');
@@ -84,10 +80,8 @@ export class StorageDataSourceProvider extends MultiTenantDataSourceService {
84
80
  StorageConfig
85
81
  ];
86
82
  }
87
- // ==================== Overrides ====================
88
- /**
89
- * Override to dynamically set entities based on tenant config
90
- */ async createDataSourceFromConfig(config) {
83
+ // Overrides
84
+ /** Override to dynamically set entities based on tenant config */ async createDataSourceFromConfig(config) {
91
85
  const currentTenant = this.getCurrentTenant();
92
86
  const enableCompanyFeature = this.getEnableCompanyFeatureForTenant(currentTenant ?? undefined);
93
87
  const entities = await this.getStorageEntities(enableCompanyFeature);
@@ -32,61 +32,47 @@ import { StorageConfigService } from '../config';
32
32
  import { StorageConfig, StorageConfigWithCompany } from '../entities';
33
33
  import { StorageDataSourceProvider } from './storage-datasource.provider';
34
34
  export class StorageProviderConfigService extends RequestScopedApiService {
35
- /**
36
- * Resolve entity class for this service
37
- * @returns StorageConfig or StorageConfigWithCompany based on configuration
38
- */ resolveEntity() {
39
- const enableCompanyFeature = this.storageConfig.isCompanyFeatureEnabled();
40
- return enableCompanyFeature ? StorageConfigWithCompany : StorageConfig;
35
+ resolveEntity() {
36
+ return this.storageConfig.isCompanyFeatureEnabled() ? StorageConfigWithCompany : StorageConfig;
41
37
  }
42
- /**
43
- * Get DataSource provider for this service
44
- * @returns StorageDataSourceProvider instance
45
- */ getDataSourceProvider() {
38
+ getDataSourceProvider() {
46
39
  return this.dataSourceProvider;
47
40
  }
41
+ // Entity Conversion
48
42
  async convertSingleDtoToEntity(dto, user) {
49
- let storageConfig = {};
50
- // Set basic fields
51
- storageConfig = {
52
- ...storageConfig,
53
- ...dto
54
- };
55
- // Only set company fields if they exist on the entity (when company feature is enabled)
56
- if ('companyId' in storageConfig) {
57
- storageConfig.companyId = user?.companyId ?? null;
43
+ const entity = await super.convertSingleDtoToEntity(dto, user);
44
+ // Set companyId from user context if company feature enabled
45
+ if (this.storageConfig.isCompanyFeatureEnabled()) {
46
+ entity.companyId = user?.companyId ?? null;
58
47
  }
59
- return storageConfig;
48
+ return entity;
60
49
  }
50
+ // Query Customization
61
51
  async getSelectQuery(query, _user, select) {
62
- if (!select || !select.length) {
52
+ if (!select?.length) {
63
53
  select = [
64
54
  'id',
65
55
  'name',
66
56
  'storage',
67
57
  'config',
58
+ 'isActive',
59
+ 'isDefault',
68
60
  'createdAt',
69
61
  'updatedAt'
70
62
  ];
71
- // Add company fields if company feature is enabled
72
63
  if (this.storageConfig.isCompanyFeatureEnabled()) {
73
64
  select.push('companyId');
74
65
  }
75
66
  }
76
- const selectFields = select.map((field)=>`${this.entityName}.${field}`);
77
- query.select(selectFields);
67
+ query.select(select.map((f)=>`${this.entityName}.${f}`));
78
68
  return {
79
69
  query,
80
70
  isRaw: false
81
71
  };
82
72
  }
83
- /**
84
- * Override: Extra query manipulation - Auto-filter by user's company
85
- */ async getExtraManipulateQuery(query, filterDto, user) {
73
+ async getExtraManipulateQuery(query, filterDto, user) {
86
74
  const result = await super.getExtraManipulateQuery(query, filterDto, user);
87
- // If company feature enabled and user has companyId, filter by user's company
88
- const enableCompanyFeature = this.storageConfig.isCompanyFeatureEnabled();
89
- if (enableCompanyFeature && user?.companyId) {
75
+ if (this.storageConfig.isCompanyFeatureEnabled() && user?.companyId) {
90
76
  query.andWhere('storageConfig.companyId = :companyId', {
91
77
  companyId: user.companyId
92
78
  });
@@ -108,19 +94,21 @@ export class StorageProviderConfigService extends RequestScopedApiService {
108
94
  }
109
95
  /**
110
96
  * Get default storage configuration (scoped to user's company if enabled)
111
- * Falls back to any available config if 'default' not found
97
+ * Priority: isDefault=true > any active config (oldest first)
112
98
  */ async getDefaultConfig(user) {
113
99
  await this.ensureRepositoryInitialized();
114
- const baseWhere = {};
100
+ const baseWhere = {
101
+ isActive: true
102
+ };
115
103
  // Filter by company only if company feature is enabled and user is provided
116
104
  if (this.storageConfig.isCompanyFeatureEnabled() && user?.companyId) {
117
105
  baseWhere.companyId = user.companyId;
118
106
  }
119
- // First try to find config named 'default'
107
+ // First try to find config marked as default
120
108
  const defaultConfig = await this.repository.findOne({
121
109
  where: {
122
110
  ...baseWhere,
123
- name: 'default'
111
+ isDefault: true
124
112
  },
125
113
  order: {
126
114
  createdAt: 'ASC'
@@ -129,7 +117,7 @@ export class StorageProviderConfigService extends RequestScopedApiService {
129
117
  if (defaultConfig) {
130
118
  return defaultConfig;
131
119
  }
132
- // Fall back to any available config for this company/user
120
+ // Fall back to any available active config (oldest first for consistency)
133
121
  return await this.repository.findOne({
134
122
  where: baseWhere,
135
123
  order: {
@@ -142,7 +130,8 @@ export class StorageProviderConfigService extends RequestScopedApiService {
142
130
  */ async getConfigByType(storage, user) {
143
131
  await this.ensureRepositoryInitialized();
144
132
  const where = {
145
- storage
133
+ storage,
134
+ isActive: true
146
135
  };
147
136
  // Filter by company only if company feature is enabled and user is provided
148
137
  if (this.storageConfig.isCompanyFeatureEnabled() && user?.companyId) {
@@ -152,9 +141,7 @@ export class StorageProviderConfigService extends RequestScopedApiService {
152
141
  where
153
142
  });
154
143
  }
155
- // NOTE: @Inject() required for bundled code - type metadata may be lost during esbuild
156
144
  constructor(cacheManager, utilsService, storageConfig, dataSourceProvider){
157
- // Repository will be set asynchronously by RequestScopedApiService
158
145
  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;
159
146
  }
160
147
  }
@@ -4,6 +4,8 @@ export interface IStorageConfig extends IIdentity {
4
4
  name: string;
5
5
  storage: FileLocationEnum;
6
6
  config: Record<string, any>;
7
+ isActive: boolean;
8
+ isDefault: boolean;
7
9
  companyId?: string | null;
8
10
  }
9
11
  export interface IAwsS3Config {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flusys/nestjs-storage",
3
- "version": "0.1.0-beta.3",
3
+ "version": "1.1.0-beta",
4
4
  "description": "Modular storage package with optional AWS S3, Azure Blob, and SFTP providers",
5
5
  "main": "cjs/index.js",
6
6
  "module": "fesm/index.js",
@@ -128,7 +128,7 @@
128
128
  }
129
129
  },
130
130
  "dependencies": {
131
- "@flusys/nestjs-core": "0.1.0-beta.3",
132
- "@flusys/nestjs-shared": "0.1.0-beta.3"
131
+ "@flusys/nestjs-core": "1.1.0-beta",
132
+ "@flusys/nestjs-shared": "1.1.0-beta"
133
133
  }
134
134
  }
@@ -1,5 +1,4 @@
1
1
  import { RequestScopedApiService, HybridCache } from '@flusys/nestjs-shared/classes';
2
- import { FilterAndPaginationDto } from '@flusys/nestjs-shared/dtos';
3
2
  import { ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
4
3
  import { UtilsService } from '@flusys/nestjs-shared/modules';
5
4
  import { EntityTarget, Repository, SelectQueryBuilder } from 'typeorm';
@@ -21,7 +20,7 @@ export declare class FolderService extends RequestScopedApiService<CreateFolderD
21
20
  query: SelectQueryBuilder<FolderBase>;
22
21
  isRaw: boolean;
23
22
  }>;
24
- protected getExtraManipulateQuery(query: SelectQueryBuilder<FolderBase>, filterDto: FilterAndPaginationDto, user: ILoggedUserInfo | null): Promise<{
23
+ protected getExtraManipulateQuery(query: SelectQueryBuilder<FolderBase>, filterDto: any, user: ILoggedUserInfo | null): Promise<{
25
24
  query: SelectQueryBuilder<FolderBase>;
26
25
  isRaw: boolean;
27
26
  }>;
@@ -1,5 +1,4 @@
1
1
  import { RequestScopedApiService, HybridCache } from '@flusys/nestjs-shared/classes';
2
- import { FilterAndPaginationDto } from '@flusys/nestjs-shared/dtos';
3
2
  import { ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
4
3
  import { UtilsService } from '@flusys/nestjs-shared/modules';
5
4
  import { EntityTarget, Repository, SelectQueryBuilder } from 'typeorm';
@@ -22,7 +21,7 @@ export declare class StorageProviderConfigService extends RequestScopedApiServic
22
21
  query: SelectQueryBuilder<StorageConfigBase>;
23
22
  isRaw: boolean;
24
23
  }>;
25
- protected getExtraManipulateQuery(query: SelectQueryBuilder<StorageConfigBase>, filterDto: FilterAndPaginationDto, user: ILoggedUserInfo | null): Promise<{
24
+ protected getExtraManipulateQuery(query: SelectQueryBuilder<StorageConfigBase>, filterDto: any, user: ILoggedUserInfo | null): Promise<{
26
25
  query: SelectQueryBuilder<StorageConfigBase>;
27
26
  isRaw: boolean;
28
27
  }>;