@flusys/nestjs-storage 5.1.1 → 5.1.3

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.
@@ -21,6 +21,7 @@ function _ts_metadata(k, v) {
21
21
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
22
22
  }
23
23
  import { ApiProperty, ApiPropertyOptional, PartialType } from '@nestjs/swagger';
24
+ import { Expose } from 'class-transformer';
24
25
  import { IsBoolean, IsEnum, IsNotEmpty, IsOptional, IsString, IsUUID } from 'class-validator';
25
26
  import { FileLocationEnum } from '../enums';
26
27
  export class CreateFileManagerDto {
@@ -137,44 +138,82 @@ export class FileManagerResponseDto {
137
138
  }
138
139
  }
139
140
  _ts_decorate([
140
- ApiProperty(),
141
+ Expose(),
142
+ ApiProperty({
143
+ description: 'Unique identifier of the file',
144
+ example: 'f2e9c8d0-7a2a-11eb-9439-0242ac130002'
145
+ }),
141
146
  _ts_metadata("design:type", String)
142
147
  ], FileManagerResponseDto.prototype, "id", void 0);
143
148
  _ts_decorate([
144
- ApiProperty(),
149
+ Expose(),
150
+ ApiProperty({
151
+ description: 'Original filename as uploaded',
152
+ example: 'profile-photo.png'
153
+ }),
145
154
  _ts_metadata("design:type", String)
146
155
  ], FileManagerResponseDto.prototype, "name", void 0);
147
156
  _ts_decorate([
148
- ApiProperty(),
157
+ Expose(),
158
+ ApiProperty({
159
+ description: 'Storage key / path used to retrieve the file from the provider',
160
+ example: 'uploads/users/abc123/profile-photo.png'
161
+ }),
149
162
  _ts_metadata("design:type", String)
150
163
  ], FileManagerResponseDto.prototype, "key", void 0);
151
164
  _ts_decorate([
152
- ApiProperty(),
165
+ Expose(),
166
+ ApiProperty({
167
+ description: 'File size in bytes',
168
+ example: '204800'
169
+ }),
153
170
  _ts_metadata("design:type", String)
154
171
  ], FileManagerResponseDto.prototype, "size", void 0);
155
172
  _ts_decorate([
156
- ApiProperty(),
173
+ Expose(),
174
+ ApiProperty({
175
+ description: 'MIME type of the file',
176
+ example: 'image/png'
177
+ }),
157
178
  _ts_metadata("design:type", String)
158
179
  ], FileManagerResponseDto.prototype, "contentType", void 0);
159
180
  _ts_decorate([
160
- ApiProperty(),
181
+ Expose(),
182
+ ApiProperty({
183
+ description: 'Whether the file is private and requires signed URL access',
184
+ example: false
185
+ }),
161
186
  _ts_metadata("design:type", Boolean)
162
187
  ], FileManagerResponseDto.prototype, "isPrivate", void 0);
163
188
  _ts_decorate([
164
- ApiPropertyOptional(),
189
+ Expose(),
190
+ ApiPropertyOptional({
191
+ description: 'ID of the folder this file belongs to; absent if in root',
192
+ example: 'a1b2c3d4-0000-0000-0000-000000000001'
193
+ }),
165
194
  _ts_metadata("design:type", String)
166
195
  ], FileManagerResponseDto.prototype, "folderId", void 0);
167
196
  _ts_decorate([
168
- ApiPropertyOptional(),
197
+ Expose(),
198
+ ApiPropertyOptional({
199
+ description: 'ID of the storage configuration used to store this file',
200
+ example: 'a1b2c3d4-0000-0000-0000-000000000002'
201
+ }),
169
202
  _ts_metadata("design:type", String)
170
203
  ], FileManagerResponseDto.prototype, "storageConfigId", void 0);
171
204
  _ts_decorate([
172
- ApiPropertyOptional(),
205
+ Expose(),
206
+ ApiPropertyOptional({
207
+ description: 'Storage provider type (e.g. s3, gcs, local)',
208
+ example: 's3'
209
+ }),
173
210
  _ts_metadata("design:type", String)
174
211
  ], FileManagerResponseDto.prototype, "location", void 0);
175
212
  _ts_decorate([
213
+ Expose(),
176
214
  ApiPropertyOptional({
177
- description: 'Name of the storage provider'
215
+ description: 'Human-readable name of the storage provider',
216
+ example: 'AWS S3 — Production Bucket'
178
217
  }),
179
218
  _ts_metadata("design:type", String)
180
219
  ], FileManagerResponseDto.prototype, "providerName", void 0);
@@ -185,6 +224,7 @@ export class GetFilesRequestDto {
185
224
  }
186
225
  _ts_decorate([
187
226
  ApiProperty({
227
+ description: 'Unique identifier of the file to retrieve',
188
228
  example: 'f2e9c8d0-7a2a-11eb-9439-0242ac130002'
189
229
  }),
190
230
  IsUUID(),
@@ -203,36 +243,58 @@ export class FilesResponseDto {
203
243
  }
204
244
  }
205
245
  _ts_decorate([
206
- ApiProperty(),
246
+ Expose(),
247
+ ApiProperty({
248
+ description: 'Unique identifier of the file',
249
+ example: 'f2e9c8d0-7a2a-11eb-9439-0242ac130002'
250
+ }),
207
251
  _ts_metadata("design:type", String)
208
252
  ], FilesResponseDto.prototype, "id", void 0);
209
253
  _ts_decorate([
210
- ApiProperty(),
254
+ Expose(),
255
+ ApiProperty({
256
+ description: 'Original filename as uploaded',
257
+ example: 'quarterly-report.pdf'
258
+ }),
211
259
  _ts_metadata("design:type", String)
212
260
  ], FilesResponseDto.prototype, "name", void 0);
213
261
  _ts_decorate([
214
- ApiProperty(),
262
+ Expose(),
263
+ ApiProperty({
264
+ description: 'MIME type of the file',
265
+ example: 'application/pdf'
266
+ }),
215
267
  _ts_metadata("design:type", String)
216
268
  ], FilesResponseDto.prototype, "contentType", void 0);
217
269
  _ts_decorate([
218
- ApiProperty(),
270
+ Expose(),
271
+ ApiProperty({
272
+ description: 'Publicly accessible or pre-signed URL to download the file',
273
+ example: 'https://cdn.example.com/uploads/quarterly-report.pdf'
274
+ }),
219
275
  _ts_metadata("design:type", String)
220
276
  ], FilesResponseDto.prototype, "url", void 0);
221
277
  _ts_decorate([
278
+ Expose(),
222
279
  ApiPropertyOptional({
223
- description: 'Storage provider type'
280
+ description: 'Storage provider type (e.g. s3, gcs, local)',
281
+ example: 'gcs'
224
282
  }),
225
283
  _ts_metadata("design:type", String)
226
284
  ], FilesResponseDto.prototype, "location", void 0);
227
285
  _ts_decorate([
286
+ Expose(),
228
287
  ApiPropertyOptional({
229
- description: 'Storage configuration ID'
288
+ description: 'ID of the storage configuration used to store this file',
289
+ example: 'a1b2c3d4-0000-0000-0000-000000000002'
230
290
  }),
231
291
  _ts_metadata("design:type", String)
232
292
  ], FilesResponseDto.prototype, "storageConfigId", void 0);
233
293
  _ts_decorate([
294
+ Expose(),
234
295
  ApiPropertyOptional({
235
- description: 'Name of the storage provider'
296
+ description: 'Human-readable name of the storage provider',
297
+ example: 'Google Cloud Storage — EU'
236
298
  }),
237
299
  _ts_metadata("design:type", String)
238
300
  ], FilesResponseDto.prototype, "providerName", void 0);
@@ -21,6 +21,7 @@ function _ts_metadata(k, v) {
21
21
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
22
22
  }
23
23
  import { ApiProperty, PartialType } from '@nestjs/swagger';
24
+ import { Expose } from 'class-transformer';
24
25
  import { IsNotEmpty, IsString, IsUUID } from 'class-validator';
25
26
  export class CreateFolderDto {
26
27
  constructor(){
@@ -58,14 +59,26 @@ export class FolderResponseDto {
58
59
  }
59
60
  }
60
61
  _ts_decorate([
61
- ApiProperty(),
62
+ Expose(),
63
+ ApiProperty({
64
+ description: 'Unique identifier of the folder',
65
+ example: 'f2e9c8d0-7a2a-11eb-9439-0242ac130002'
66
+ }),
62
67
  _ts_metadata("design:type", String)
63
68
  ], FolderResponseDto.prototype, "id", void 0);
64
69
  _ts_decorate([
65
- ApiProperty(),
70
+ Expose(),
71
+ ApiProperty({
72
+ description: 'Human-readable name of the folder',
73
+ example: 'Profile Photos'
74
+ }),
66
75
  _ts_metadata("design:type", String)
67
76
  ], FolderResponseDto.prototype, "name", void 0);
68
77
  _ts_decorate([
69
- ApiProperty(),
78
+ Expose(),
79
+ ApiProperty({
80
+ description: 'URL-friendly unique identifier derived from the folder name',
81
+ example: 'profile-photos'
82
+ }),
70
83
  _ts_metadata("design:type", String)
71
84
  ], FolderResponseDto.prototype, "slug", void 0);
@@ -21,9 +21,10 @@ function _ts_metadata(k, v) {
21
21
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
22
22
  }
23
23
  import { IdentityResponseDto } from '@flusys/nestjs-shared/dtos';
24
- import { FileLocationEnum } from '../enums';
25
24
  import { ApiProperty, ApiPropertyOptional, OmitType, PartialType } from '@nestjs/swagger';
25
+ import { Expose } from 'class-transformer';
26
26
  import { IsBoolean, IsEnum, IsNotEmpty, IsObject, IsOptional, IsString, IsUUID, MaxLength } from 'class-validator';
27
+ import { FileLocationEnum } from '../enums';
27
28
  export class CreateStorageConfigDto {
28
29
  constructor(){
29
30
  _define_property(this, "name", void 0);
@@ -100,3 +101,49 @@ export class StorageConfigResponseDto extends IdentityResponseDto {
100
101
  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);
101
102
  }
102
103
  }
104
+ _ts_decorate([
105
+ Expose(),
106
+ ApiProperty({
107
+ description: 'Human-readable name for this storage configuration',
108
+ example: 'AWS S3 — Production Bucket'
109
+ }),
110
+ _ts_metadata("design:type", String)
111
+ ], StorageConfigResponseDto.prototype, "name", void 0);
112
+ _ts_decorate([
113
+ Expose(),
114
+ ApiProperty({
115
+ description: 'Storage provider type',
116
+ example: 's3'
117
+ }),
118
+ _ts_metadata("design:type", String)
119
+ ], StorageConfigResponseDto.prototype, "storage", void 0);
120
+ _ts_decorate([
121
+ Expose(),
122
+ ApiProperty({
123
+ description: 'Provider-specific configuration key-value pairs',
124
+ type: 'object',
125
+ additionalProperties: true,
126
+ example: {
127
+ bucket: 'my-production-bucket',
128
+ region: 'us-east-1',
129
+ endpoint: 'https://s3.amazonaws.com'
130
+ }
131
+ }),
132
+ _ts_metadata("design:type", typeof Record === "undefined" ? Object : Record)
133
+ ], StorageConfigResponseDto.prototype, "config", void 0);
134
+ _ts_decorate([
135
+ Expose(),
136
+ ApiProperty({
137
+ description: 'Whether this storage configuration is currently active',
138
+ example: true
139
+ }),
140
+ _ts_metadata("design:type", Boolean)
141
+ ], StorageConfigResponseDto.prototype, "isActive", void 0);
142
+ _ts_decorate([
143
+ Expose(),
144
+ ApiProperty({
145
+ description: 'Whether this is the default storage configuration',
146
+ example: false
147
+ }),
148
+ _ts_metadata("design:type", Boolean)
149
+ ], StorageConfigResponseDto.prototype, "isDefault", void 0);
@@ -21,8 +21,8 @@ function _ts_metadata(k, v) {
21
21
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
22
22
  }
23
23
  import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
24
+ import { Expose, Type } from 'class-transformer';
24
25
  import { IsArray, IsBoolean, IsEnum, IsInt, IsNotEmpty, IsOptional, IsString, IsUUID, Matches, Max, Min } from 'class-validator';
25
- import { Type } from 'class-transformer';
26
26
  export var ImageFormat = /*#__PURE__*/ function(ImageFormat) {
27
27
  ImageFormat["ORIGINAL"] = "original";
28
28
  ImageFormat["JPEG"] = "jpeg";
@@ -195,40 +195,51 @@ export class FileUploadResponsePayloadDto {
195
195
  }
196
196
  }
197
197
  _ts_decorate([
198
+ Expose(),
198
199
  ApiProperty({
199
- example: 'file123.jpg'
200
+ description: 'Original filename of the uploaded file',
201
+ example: 'profile-photo.jpg'
200
202
  }),
201
203
  _ts_metadata("design:type", String)
202
204
  ], FileUploadResponsePayloadDto.prototype, "name", void 0);
203
205
  _ts_decorate([
206
+ Expose(),
204
207
  ApiProperty({
208
+ description: 'MIME type of the uploaded file',
205
209
  example: 'image/jpeg'
206
210
  }),
207
211
  _ts_metadata("design:type", String)
208
212
  ], FileUploadResponsePayloadDto.prototype, "contentType", void 0);
209
213
  _ts_decorate([
214
+ Expose(),
210
215
  ApiProperty({
211
- example: 2048
216
+ description: 'File size in bytes',
217
+ example: 204800,
218
+ minimum: 0
212
219
  }),
213
220
  _ts_metadata("design:type", Number)
214
221
  ], FileUploadResponsePayloadDto.prototype, "size", void 0);
215
222
  _ts_decorate([
223
+ Expose(),
216
224
  ApiProperty({
217
- example: 'uploads/file123.jpg'
225
+ description: 'Storage key / path used to retrieve the file from the provider',
226
+ example: 'uploads/users/abc123/profile-photo.jpg'
218
227
  }),
219
228
  _ts_metadata("design:type", String)
220
229
  ], FileUploadResponsePayloadDto.prototype, "key", void 0);
221
230
  _ts_decorate([
231
+ Expose(),
222
232
  ApiProperty({
223
- example: 'local',
224
- description: 'Storage provider type'
233
+ description: 'Storage provider type (e.g. s3, gcs, local)',
234
+ example: 's3'
225
235
  }),
226
236
  _ts_metadata("design:type", String)
227
237
  ], FileUploadResponsePayloadDto.prototype, "location", void 0);
228
238
  _ts_decorate([
239
+ Expose(),
229
240
  ApiProperty({
230
- example: '123e4567-e89b-12d3-a456-426614174000',
231
- description: 'Storage configuration ID'
241
+ description: 'ID of the storage configuration used to store this file',
242
+ example: '123e4567-e89b-12d3-a456-426614174000'
232
243
  }),
233
244
  _ts_metadata("design:type", String)
234
245
  ], FileUploadResponsePayloadDto.prototype, "storageConfigId", void 0);
@@ -25,7 +25,7 @@ function _ts_param(paramIndex, decorator) {
25
25
  decorator(target, key, paramIndex);
26
26
  };
27
27
  }
28
- import { HybridCache, RequestScopedApiService } from '@flusys/nestjs-shared/classes';
28
+ import { HybridCache, ApiService } from '@flusys/nestjs-shared/classes';
29
29
  import { UtilsService } from '@flusys/nestjs-shared/modules';
30
30
  import { applyCompanyFilter } from '@flusys/nestjs-shared/utils';
31
31
  import { BadRequestException, Inject, Injectable, NotFoundException, Scope } from '@nestjs/common';
@@ -52,13 +52,10 @@ const DEFAULT_SELECT_FIELDS = [
52
52
  'createdAt',
53
53
  'deletedAt'
54
54
  ];
55
- export class FileManagerService extends RequestScopedApiService {
55
+ export class FileManagerService extends ApiService {
56
56
  resolveEntity() {
57
57
  return this.storageConfig.isCompanyFeatureEnabled() ? FileManagerWithCompany : FileManager;
58
58
  }
59
- getDataSourceProvider() {
60
- return this.dataSourceProvider;
61
- }
62
59
  // ─── Override Methods ───────────────────────────────────────────────────────
63
60
  async convertSingleDtoToEntity(dto, user) {
64
61
  let entity = {};
@@ -145,7 +142,7 @@ export class FileManagerService extends RequestScopedApiService {
145
142
  }
146
143
  // ─── Public Methods ─────────────────────────────────────────────────────────
147
144
  async getFiles(dtos, protocol, host, user) {
148
- await this.ensureRepositoryInitialized();
145
+ await this.ensureDataSourceRepository();
149
146
  const ids = dtos.map((d)=>d.id).filter(Boolean);
150
147
  if (!ids.length) throw new BadRequestException({
151
148
  message: 'No valid file IDs provided',
@@ -313,7 +310,7 @@ export class FileManagerService extends RequestScopedApiService {
313
310
  };
314
311
  }
315
312
  constructor(cacheManager, utilsService, uploadService, storageConfig, dataSourceProvider){
316
- 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;
313
+ super('file_manager', cacheManager, utilsService, FileManagerService.name, true, 'storage', undefined, dataSourceProvider), _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;
317
314
  }
318
315
  }
319
316
  FileManagerService = _ts_decorate([
@@ -25,20 +25,17 @@ function _ts_param(paramIndex, decorator) {
25
25
  decorator(target, key, paramIndex);
26
26
  };
27
27
  }
28
- import { HybridCache, RequestScopedApiService } from '@flusys/nestjs-shared/classes';
28
+ import { HybridCache, ApiService } from '@flusys/nestjs-shared/classes';
29
29
  import { UtilsService } from '@flusys/nestjs-shared/modules';
30
30
  import { applyCompanyFilter } from '@flusys/nestjs-shared/utils';
31
31
  import { Inject, Injectable, Scope } from '@nestjs/common';
32
32
  import { Folder, FolderWithCompany } from '../entities';
33
33
  import { StorageConfigService } from './storage-config.service';
34
34
  import { StorageDataSourceProvider } from './storage-datasource.provider';
35
- export class FolderService extends RequestScopedApiService {
35
+ export class FolderService extends ApiService {
36
36
  resolveEntity() {
37
37
  return this.storageConfig.isCompanyFeatureEnabled() ? FolderWithCompany : Folder;
38
38
  }
39
- getDataSourceProvider() {
40
- return this.dataSourceProvider;
41
- }
42
39
  // ─── Override Methods ───────────────────────────────────────────────────────
43
40
  async convertSingleDtoToEntity(dto, user) {
44
41
  const entity = await super.convertSingleDtoToEntity(dto, user);
@@ -75,7 +72,7 @@ export class FolderService extends RequestScopedApiService {
75
72
  return result;
76
73
  }
77
74
  constructor(cacheManager, utilsService, storageConfig, dataSourceProvider){
78
- 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;
75
+ super('folder', cacheManager, utilsService, FolderService.name, true, 'storage', undefined, dataSourceProvider), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "storageConfig", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.storageConfig = storageConfig;
79
76
  }
80
77
  }
81
78
  FolderService = _ts_decorate([
@@ -25,15 +25,13 @@ function _ts_param(paramIndex, decorator) {
25
25
  decorator(target, key, paramIndex);
26
26
  };
27
27
  }
28
- import { SYSTEM_MESSAGES } from '@flusys/nestjs-shared/constants';
29
28
  import { MultiTenantDataSourceService } from '@flusys/nestjs-shared/modules';
30
- import { Inject, Injectable, InternalServerErrorException, Optional, Scope } from '@nestjs/common';
29
+ import { Inject, Injectable, Optional, Scope } from '@nestjs/common';
31
30
  import { REQUEST } from '@nestjs/core';
32
31
  import { Request } from 'express';
33
32
  import { StorageConfigService } from './storage-config.service';
34
33
  export class StorageDataSourceProvider extends MultiTenantDataSourceService {
35
- // Factory Methods
36
- /** Build parent options from StorageModuleOptions */ static buildParentOptions(options) {
34
+ static buildParentOptions(options) {
37
35
  return {
38
36
  bootstrapAppConfig: options.bootstrapAppConfig,
39
37
  defaultDatabaseConfig: options.config?.defaultDatabaseConfig,
@@ -41,19 +39,10 @@ export class StorageDataSourceProvider extends MultiTenantDataSourceService {
41
39
  tenants: options.config?.tenants
42
40
  };
43
41
  }
44
- // Feature Flags
45
- /**
46
- * Get enable company feature for specific tenant
47
- * Falls back to global setting if not specified per-tenant
48
- */ getEnableCompanyFeatureForTenant(tenant) {
42
+ getEnableCompanyFeatureForTenant(tenant) {
49
43
  return tenant?.enableCompanyFeature ?? this.configService.isCompanyFeatureEnabled();
50
44
  }
51
- // Entity Management
52
- /**
53
- * Get storage entities for migrations based on company feature flag.
54
- * For TypeORM repositories, we always use the base entities (FileManager, etc.)
55
- * but for migrations, we need the correct entity based on the feature flag.
56
- */ async getStorageEntities(enableCompanyFeature) {
45
+ async getStorageEntities(enableCompanyFeature) {
57
46
  const enable = enableCompanyFeature ?? this.configService.isCompanyFeatureEnabled();
58
47
  const entities = await import('../entities');
59
48
  return enable ? [
@@ -66,72 +55,16 @@ export class StorageDataSourceProvider extends MultiTenantDataSourceService {
66
55
  entities.StorageConfig
67
56
  ];
68
57
  }
69
- // Overrides
70
- /** Override to dynamically set entities based on tenant config */ async createDataSourceFromConfig(config) {
58
+ async createDataSourceFromConfig(config) {
71
59
  const currentTenant = this.getCurrentTenant();
72
60
  const enableCompanyFeature = this.getEnableCompanyFeatureForTenant(currentTenant ?? undefined);
73
61
  const entities = await this.getStorageEntities(enableCompanyFeature);
74
62
  return super.createDataSourceFromConfig(config, entities);
75
63
  }
76
- /**
77
- * Override to use Storage-specific static cache
78
- */ async getSingleDataSource() {
79
- // Return existing initialized connection from Storage-specific cache
80
- if (StorageDataSourceProvider.singleDataSource?.isInitialized) {
81
- return StorageDataSourceProvider.singleDataSource;
82
- }
83
- // If another request is creating the connection, wait for it
84
- if (StorageDataSourceProvider.singleConnectionLock) {
85
- return StorageDataSourceProvider.singleConnectionLock;
86
- }
87
- const config = this.getDefaultDatabaseConfig();
88
- if (!config) {
89
- throw new InternalServerErrorException({
90
- message: 'No database config available. Provide defaultDatabaseConfig or tenantDefaultDatabaseConfig.',
91
- messageKey: SYSTEM_MESSAGES.DATABASE_CONFIG_NOT_AVAILABLE
92
- });
93
- }
94
- // Create connection with lock to prevent race conditions
95
- const connectionPromise = this.createDataSourceFromConfig(config);
96
- StorageDataSourceProvider.singleConnectionLock = connectionPromise;
97
- try {
98
- const dataSource = await connectionPromise;
99
- StorageDataSourceProvider.singleDataSource = dataSource;
100
- return dataSource;
101
- } finally{
102
- StorageDataSourceProvider.singleConnectionLock = null;
103
- }
104
- }
105
- /**
106
- * Override to use Storage-specific static cache for tenant connections
107
- */ async getOrCreateTenantConnection(tenant) {
108
- // Return existing initialized connection from Storage-specific cache
109
- const existing = StorageDataSourceProvider.tenantConnections.get(tenant.id);
110
- if (existing?.isInitialized) {
111
- return existing;
112
- }
113
- // If another request is creating this tenant's connection, wait for it
114
- const pendingConnection = StorageDataSourceProvider.connectionLocks.get(tenant.id);
115
- if (pendingConnection) {
116
- return pendingConnection;
117
- }
118
- // Create connection with lock to prevent race conditions
119
- const config = this.buildTenantDatabaseConfig(tenant);
120
- const connectionPromise = this.createDataSourceFromConfig(config);
121
- StorageDataSourceProvider.connectionLocks.set(tenant.id, connectionPromise);
122
- try {
123
- const dataSource = await connectionPromise;
124
- StorageDataSourceProvider.tenantConnections.set(tenant.id, dataSource);
125
- return dataSource;
126
- } finally{
127
- StorageDataSourceProvider.connectionLocks.delete(tenant.id);
128
- }
129
- }
130
64
  constructor(configService, request){
131
65
  super(StorageDataSourceProvider.buildParentOptions(configService.getOptions()), request), _define_property(this, "configService", void 0), this.configService = configService;
132
66
  }
133
67
  }
134
- // Override parent's static properties to have Storage-specific cache
135
68
  _define_property(StorageDataSourceProvider, "tenantConnections", new Map());
136
69
  _define_property(StorageDataSourceProvider, "singleDataSource", null);
137
70
  _define_property(StorageDataSourceProvider, "tenantsRegistry", new Map());
@@ -25,20 +25,17 @@ function _ts_param(paramIndex, decorator) {
25
25
  decorator(target, key, paramIndex);
26
26
  };
27
27
  }
28
- import { HybridCache, RequestScopedApiService } from '@flusys/nestjs-shared/classes';
28
+ import { HybridCache, ApiService } from '@flusys/nestjs-shared/classes';
29
29
  import { UtilsService } from '@flusys/nestjs-shared/modules';
30
30
  import { applyCompanyFilter, buildCompanyWhereCondition } from '@flusys/nestjs-shared/utils';
31
31
  import { Inject, Injectable, Scope } from '@nestjs/common';
32
32
  import { StorageConfig, StorageConfigWithCompany } from '../entities';
33
33
  import { StorageConfigService } from './storage-config.service';
34
34
  import { StorageDataSourceProvider } from './storage-datasource.provider';
35
- export class StorageProviderConfigService extends RequestScopedApiService {
35
+ export class StorageProviderConfigService extends ApiService {
36
36
  resolveEntity() {
37
37
  return this.storageConfig.isCompanyFeatureEnabled() ? StorageConfigWithCompany : StorageConfig;
38
38
  }
39
- getDataSourceProvider() {
40
- return this.dataSourceProvider;
41
- }
42
39
  // ─── Override Methods ───────────────────────────────────────────────────────
43
40
  async convertSingleDtoToEntity(dto, user) {
44
41
  const entity = await super.convertSingleDtoToEntity(dto, user);
@@ -79,7 +76,7 @@ export class StorageProviderConfigService extends RequestScopedApiService {
79
76
  return result;
80
77
  }
81
78
  async findByIdDirect(id) {
82
- await this.ensureRepositoryInitialized();
79
+ await this.ensureDataSourceRepository();
83
80
  return await this.repository.findOne({
84
81
  where: {
85
82
  id
@@ -87,7 +84,7 @@ export class StorageProviderConfigService extends RequestScopedApiService {
87
84
  });
88
85
  }
89
86
  async getDefaultConfig(user) {
90
- await this.ensureRepositoryInitialized();
87
+ await this.ensureDataSourceRepository();
91
88
  const baseWhere = buildCompanyWhereCondition({
92
89
  isActive: true
93
90
  }, this.storageConfig.isCompanyFeatureEnabled(), user);
@@ -111,7 +108,7 @@ export class StorageProviderConfigService extends RequestScopedApiService {
111
108
  });
112
109
  }
113
110
  async getConfigByType(storage, user) {
114
- await this.ensureRepositoryInitialized();
111
+ await this.ensureDataSourceRepository();
115
112
  const where = buildCompanyWhereCondition({
116
113
  storage,
117
114
  isActive: true
@@ -121,7 +118,7 @@ export class StorageProviderConfigService extends RequestScopedApiService {
121
118
  });
122
119
  }
123
120
  constructor(cacheManager, utilsService, storageConfig, dataSourceProvider){
124
- 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;
121
+ super('storageConfig', cacheManager, utilsService, StorageProviderConfigService.name, true, 'storage', undefined, dataSourceProvider), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "storageConfig", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.storageConfig = storageConfig;
125
122
  }
126
123
  }
127
124
  StorageProviderConfigService = _ts_decorate([
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flusys/nestjs-storage",
3
- "version": "5.1.1",
3
+ "version": "5.1.3",
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",
@@ -85,25 +85,25 @@
85
85
  }
86
86
  },
87
87
  "peerDependencies": {
88
- "@nestjs/common": "^10.0.0 || ^11.0.0",
89
- "@nestjs/config": "^3.0.0 || ^4.0.0",
90
- "@nestjs/core": "^10.0.0 || ^11.0.0",
91
- "@nestjs/platform-express": "^10.0.0 || ^11.0.0",
92
- "@nestjs/swagger": "^7.0.0 || ^11.0.0",
93
- "@nestjs/typeorm": "^10.0.0 || ^11.0.0",
94
- "class-transformer": "^0.5.0",
95
- "class-validator": "^0.14.0",
88
+ "@nestjs/common": "^11.0.0",
89
+ "@nestjs/config": "^4.0.0",
90
+ "@nestjs/core": "^11.0.0",
91
+ "@nestjs/platform-express": "^11.0.0",
92
+ "@nestjs/swagger": "^11.0.0",
93
+ "@nestjs/typeorm": "^11.0.0",
94
+ "class-transformer": "^0.5.1",
95
+ "class-validator": "^0.14.0 || ^0.15.0",
96
96
  "typeorm": "^0.3.0",
97
- "multer": "^2.0.0",
97
+ "multer": "^2.1.0",
98
98
  "sharp": "^0.34.0",
99
- "mime-types": "^2.1.0",
100
- "uuid": "^9.0.0 || ^11.0.0",
101
- "@aws-sdk/client-s3": "^3.400.0",
102
- "@aws-sdk/lib-storage": "^3.400.0",
103
- "@aws-sdk/s3-request-presigner": "^3.400.0",
104
- "@azure/storage-blob": "^12.15.0",
105
- "ssh2-sftp-client": "^12.0.0",
106
- "express": "^4.18.0"
99
+ "mime-types": "^2.1.0 || ^3.0.0",
100
+ "uuid": "^11.0.0",
101
+ "@aws-sdk/client-s3": "^3.900.0",
102
+ "@aws-sdk/lib-storage": "^3.900.0",
103
+ "@aws-sdk/s3-request-presigner": "^3.900.0",
104
+ "@azure/storage-blob": "^12.29.0",
105
+ "ssh2-sftp-client": "^12.1.0",
106
+ "express": "^4.18.0 || ^5.0.0"
107
107
  },
108
108
  "peerDependenciesMeta": {
109
109
  "sharp": {
@@ -129,7 +129,7 @@
129
129
  }
130
130
  },
131
131
  "dependencies": {
132
- "@flusys/nestjs-core": "5.1.1",
133
- "@flusys/nestjs-shared": "5.1.1"
132
+ "@flusys/nestjs-core": "5.1.3",
133
+ "@flusys/nestjs-shared": "5.1.3"
134
134
  }
135
135
  }