@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.
- package/cjs/controllers/upload.controller.js +1 -0
- package/cjs/dtos/file-manager.dto.js +79 -17
- package/cjs/dtos/folder.dto.js +16 -3
- package/cjs/dtos/storage-config.dto.js +48 -1
- package/cjs/dtos/upload.dto.js +19 -8
- package/cjs/services/file-manager.service.js +3 -6
- package/cjs/services/folder.service.js +2 -5
- package/cjs/services/storage-datasource.provider.js +4 -71
- package/cjs/services/storage-provider-config.service.js +5 -8
- package/controllers/file-manager.controller.d.ts +9 -9
- package/controllers/folder.controller.d.ts +2 -2
- package/controllers/storage-config.controller.d.ts +2 -2
- package/fesm/controllers/upload.controller.js +1 -0
- package/fesm/dtos/file-manager.dto.js +79 -17
- package/fesm/dtos/folder.dto.js +16 -3
- package/fesm/dtos/storage-config.dto.js +48 -1
- package/fesm/dtos/upload.dto.js +19 -8
- package/fesm/services/file-manager.service.js +4 -7
- package/fesm/services/folder.service.js +3 -6
- package/fesm/services/storage-datasource.provider.js +5 -72
- package/fesm/services/storage-provider-config.service.js +6 -9
- package/package.json +20 -20
- package/services/file-manager.service.d.ts +2 -3
- package/services/folder.service.d.ts +2 -4
- package/services/storage-datasource.provider.d.ts +0 -2
- package/services/storage-provider-config.service.d.ts +2 -4
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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: '
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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: '
|
|
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: '
|
|
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);
|
package/fesm/dtos/folder.dto.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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);
|
package/fesm/dtos/upload.dto.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
224
|
-
|
|
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
|
-
|
|
231
|
-
|
|
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,
|
|
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
|
|
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.
|
|
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',
|
|
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,
|
|
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
|
|
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',
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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',
|
|
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.
|
|
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": "^
|
|
89
|
-
"@nestjs/config": "^
|
|
90
|
-
"@nestjs/core": "^
|
|
91
|
-
"@nestjs/platform-express": "^
|
|
92
|
-
"@nestjs/swagger": "^
|
|
93
|
-
"@nestjs/typeorm": "^
|
|
94
|
-
"class-transformer": "^0.5.
|
|
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.
|
|
97
|
+
"multer": "^2.1.0",
|
|
98
98
|
"sharp": "^0.34.0",
|
|
99
|
-
"mime-types": "^2.1.0",
|
|
100
|
-
"uuid": "^
|
|
101
|
-
"@aws-sdk/client-s3": "^3.
|
|
102
|
-
"@aws-sdk/lib-storage": "^3.
|
|
103
|
-
"@aws-sdk/s3-request-presigner": "^3.
|
|
104
|
-
"@azure/storage-blob": "^12.
|
|
105
|
-
"ssh2-sftp-client": "^12.
|
|
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.
|
|
133
|
-
"@flusys/nestjs-shared": "5.1.
|
|
132
|
+
"@flusys/nestjs-core": "5.1.3",
|
|
133
|
+
"@flusys/nestjs-shared": "5.1.3"
|
|
134
134
|
}
|
|
135
135
|
}
|