@flusys/nestjs-shared 0.1.0-beta.2 → 1.0.0-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -29
- package/cjs/classes/api-controller.class.js +0 -21
- package/cjs/classes/api-service.class.js +4 -41
- package/cjs/classes/request-scoped-api.service.js +4 -53
- package/cjs/classes/winston.logger.class.js +5 -15
- package/cjs/constants/index.js +2 -11
- package/cjs/dtos/response-payload.dto.js +40 -85
- package/cjs/interfaces/logged-user-info.interface.js +1 -2
- package/cjs/interfaces/permission.interface.js +1 -10
- package/cjs/middlewares/logger.middleware.js +2 -6
- package/cjs/modules/datasource/multi-tenant-datasource.service.js +9 -9
- package/fesm/classes/api-controller.class.js +1 -69
- package/fesm/classes/api-service.class.js +5 -46
- package/fesm/classes/request-scoped-api.service.js +4 -53
- package/fesm/classes/winston.logger.class.js +6 -18
- package/fesm/constants/index.js +14 -29
- package/fesm/dtos/response-payload.dto.js +44 -109
- package/fesm/interfaces/logged-user-info.interface.js +1 -2
- package/fesm/interfaces/permission.interface.js +0 -12
- package/fesm/middlewares/logger.middleware.js +2 -6
- package/fesm/modules/datasource/multi-tenant-datasource.service.js +9 -9
- package/package.json +2 -2
|
@@ -72,22 +72,19 @@ let RequestMetaDto = class RequestMetaDto {
|
|
|
72
72
|
};
|
|
73
73
|
_ts_decorate([
|
|
74
74
|
(0, _swagger.ApiPropertyOptional)({
|
|
75
|
-
example: 'req_abc123'
|
|
76
|
-
description: 'Unique request ID'
|
|
75
|
+
example: 'req_abc123'
|
|
77
76
|
}),
|
|
78
77
|
_ts_metadata("design:type", String)
|
|
79
78
|
], RequestMetaDto.prototype, "requestId", void 0);
|
|
80
79
|
_ts_decorate([
|
|
81
80
|
(0, _swagger.ApiPropertyOptional)({
|
|
82
|
-
example: '2024-12-19T10:30:00.000Z'
|
|
83
|
-
description: 'Request timestamp'
|
|
81
|
+
example: '2024-12-19T10:30:00.000Z'
|
|
84
82
|
}),
|
|
85
83
|
_ts_metadata("design:type", String)
|
|
86
84
|
], RequestMetaDto.prototype, "timestamp", void 0);
|
|
87
85
|
_ts_decorate([
|
|
88
86
|
(0, _swagger.ApiPropertyOptional)({
|
|
89
|
-
example: 45
|
|
90
|
-
description: 'Response time in milliseconds'
|
|
87
|
+
example: 45
|
|
91
88
|
}),
|
|
92
89
|
_ts_metadata("design:type", Number)
|
|
93
90
|
], RequestMetaDto.prototype, "responseTime", void 0);
|
|
@@ -101,28 +98,23 @@ let SingleResponseDto = class SingleResponseDto {
|
|
|
101
98
|
};
|
|
102
99
|
_ts_decorate([
|
|
103
100
|
(0, _swagger.ApiProperty)({
|
|
104
|
-
example: true
|
|
105
|
-
description: 'Whether the operation was successful'
|
|
101
|
+
example: true
|
|
106
102
|
}),
|
|
107
103
|
_ts_metadata("design:type", Boolean)
|
|
108
104
|
], SingleResponseDto.prototype, "success", void 0);
|
|
109
105
|
_ts_decorate([
|
|
110
106
|
(0, _swagger.ApiProperty)({
|
|
111
|
-
example: 'Operation successful'
|
|
112
|
-
description: 'Response message'
|
|
107
|
+
example: 'Operation successful'
|
|
113
108
|
}),
|
|
114
109
|
_ts_metadata("design:type", String)
|
|
115
110
|
], SingleResponseDto.prototype, "message", void 0);
|
|
116
111
|
_ts_decorate([
|
|
117
|
-
(0, _swagger.ApiPropertyOptional)(
|
|
118
|
-
description: 'Response data (single item)'
|
|
119
|
-
}),
|
|
112
|
+
(0, _swagger.ApiPropertyOptional)(),
|
|
120
113
|
_ts_metadata("design:type", typeof T === "undefined" ? Object : T)
|
|
121
114
|
], SingleResponseDto.prototype, "data", void 0);
|
|
122
115
|
_ts_decorate([
|
|
123
116
|
(0, _swagger.ApiPropertyOptional)({
|
|
124
|
-
type: RequestMetaDto
|
|
125
|
-
description: 'Request metadata'
|
|
117
|
+
type: RequestMetaDto
|
|
126
118
|
}),
|
|
127
119
|
_ts_metadata("design:type", typeof RequestMetaDto === "undefined" ? Object : RequestMetaDto)
|
|
128
120
|
], SingleResponseDto.prototype, "_meta", void 0);
|
|
@@ -141,43 +133,37 @@ let PaginationMetaDto = class PaginationMetaDto {
|
|
|
141
133
|
};
|
|
142
134
|
_ts_decorate([
|
|
143
135
|
(0, _swagger.ApiProperty)({
|
|
144
|
-
example: 100
|
|
145
|
-
description: 'Total records in database'
|
|
136
|
+
example: 100
|
|
146
137
|
}),
|
|
147
138
|
_ts_metadata("design:type", Number)
|
|
148
139
|
], PaginationMetaDto.prototype, "total", void 0);
|
|
149
140
|
_ts_decorate([
|
|
150
141
|
(0, _swagger.ApiProperty)({
|
|
151
|
-
example: 0
|
|
152
|
-
description: 'Current page number (0-based)'
|
|
142
|
+
example: 0
|
|
153
143
|
}),
|
|
154
144
|
_ts_metadata("design:type", Number)
|
|
155
145
|
], PaginationMetaDto.prototype, "page", void 0);
|
|
156
146
|
_ts_decorate([
|
|
157
147
|
(0, _swagger.ApiProperty)({
|
|
158
|
-
example: 10
|
|
159
|
-
description: 'Items per page'
|
|
148
|
+
example: 10
|
|
160
149
|
}),
|
|
161
150
|
_ts_metadata("design:type", Number)
|
|
162
151
|
], PaginationMetaDto.prototype, "pageSize", void 0);
|
|
163
152
|
_ts_decorate([
|
|
164
153
|
(0, _swagger.ApiProperty)({
|
|
165
|
-
example: 10
|
|
166
|
-
description: 'Number of items in current response'
|
|
154
|
+
example: 10
|
|
167
155
|
}),
|
|
168
156
|
_ts_metadata("design:type", Number)
|
|
169
157
|
], PaginationMetaDto.prototype, "count", void 0);
|
|
170
158
|
_ts_decorate([
|
|
171
159
|
(0, _swagger.ApiPropertyOptional)({
|
|
172
|
-
example: true
|
|
173
|
-
description: 'Whether there are more pages'
|
|
160
|
+
example: true
|
|
174
161
|
}),
|
|
175
162
|
_ts_metadata("design:type", Boolean)
|
|
176
163
|
], PaginationMetaDto.prototype, "hasMore", void 0);
|
|
177
164
|
_ts_decorate([
|
|
178
165
|
(0, _swagger.ApiPropertyOptional)({
|
|
179
|
-
example: 10
|
|
180
|
-
description: 'Total number of pages'
|
|
166
|
+
example: 10
|
|
181
167
|
}),
|
|
182
168
|
_ts_metadata("design:type", Number)
|
|
183
169
|
], PaginationMetaDto.prototype, "totalPages", void 0);
|
|
@@ -192,36 +178,31 @@ let ListResponseDto = class ListResponseDto {
|
|
|
192
178
|
};
|
|
193
179
|
_ts_decorate([
|
|
194
180
|
(0, _swagger.ApiProperty)({
|
|
195
|
-
example: true
|
|
196
|
-
description: 'Whether the operation was successful'
|
|
181
|
+
example: true
|
|
197
182
|
}),
|
|
198
183
|
_ts_metadata("design:type", Boolean)
|
|
199
184
|
], ListResponseDto.prototype, "success", void 0);
|
|
200
185
|
_ts_decorate([
|
|
201
186
|
(0, _swagger.ApiProperty)({
|
|
202
|
-
example: 'Data retrieved successfully'
|
|
203
|
-
description: 'Response message'
|
|
187
|
+
example: 'Data retrieved successfully'
|
|
204
188
|
}),
|
|
205
189
|
_ts_metadata("design:type", String)
|
|
206
190
|
], ListResponseDto.prototype, "message", void 0);
|
|
207
191
|
_ts_decorate([
|
|
208
192
|
(0, _swagger.ApiPropertyOptional)({
|
|
209
|
-
description: 'Response data (array of items)',
|
|
210
193
|
isArray: true
|
|
211
194
|
}),
|
|
212
195
|
_ts_metadata("design:type", Array)
|
|
213
196
|
], ListResponseDto.prototype, "data", void 0);
|
|
214
197
|
_ts_decorate([
|
|
215
198
|
(0, _swagger.ApiProperty)({
|
|
216
|
-
type: PaginationMetaDto
|
|
217
|
-
description: 'Pagination metadata'
|
|
199
|
+
type: PaginationMetaDto
|
|
218
200
|
}),
|
|
219
201
|
_ts_metadata("design:type", typeof PaginationMetaDto === "undefined" ? Object : PaginationMetaDto)
|
|
220
202
|
], ListResponseDto.prototype, "meta", void 0);
|
|
221
203
|
_ts_decorate([
|
|
222
204
|
(0, _swagger.ApiPropertyOptional)({
|
|
223
|
-
type: RequestMetaDto
|
|
224
|
-
description: 'Request metadata'
|
|
205
|
+
type: RequestMetaDto
|
|
225
206
|
}),
|
|
226
207
|
_ts_metadata("design:type", typeof RequestMetaDto === "undefined" ? Object : RequestMetaDto)
|
|
227
208
|
], ListResponseDto.prototype, "_meta", void 0);
|
|
@@ -237,22 +218,19 @@ let BulkMetaDto = class BulkMetaDto {
|
|
|
237
218
|
};
|
|
238
219
|
_ts_decorate([
|
|
239
220
|
(0, _swagger.ApiProperty)({
|
|
240
|
-
example: 5
|
|
241
|
-
description: 'Number of items successfully processed'
|
|
221
|
+
example: 5
|
|
242
222
|
}),
|
|
243
223
|
_ts_metadata("design:type", Number)
|
|
244
224
|
], BulkMetaDto.prototype, "count", void 0);
|
|
245
225
|
_ts_decorate([
|
|
246
226
|
(0, _swagger.ApiPropertyOptional)({
|
|
247
|
-
example: 0
|
|
248
|
-
description: 'Number of items that failed'
|
|
227
|
+
example: 0
|
|
249
228
|
}),
|
|
250
229
|
_ts_metadata("design:type", Number)
|
|
251
230
|
], BulkMetaDto.prototype, "failed", void 0);
|
|
252
231
|
_ts_decorate([
|
|
253
232
|
(0, _swagger.ApiPropertyOptional)({
|
|
254
|
-
example: 5
|
|
255
|
-
description: 'Total items in request'
|
|
233
|
+
example: 5
|
|
256
234
|
}),
|
|
257
235
|
_ts_metadata("design:type", Number)
|
|
258
236
|
], BulkMetaDto.prototype, "total", void 0);
|
|
@@ -267,36 +245,31 @@ let BulkResponseDto = class BulkResponseDto {
|
|
|
267
245
|
};
|
|
268
246
|
_ts_decorate([
|
|
269
247
|
(0, _swagger.ApiProperty)({
|
|
270
|
-
example: true
|
|
271
|
-
description: 'Whether the operation was successful'
|
|
248
|
+
example: true
|
|
272
249
|
}),
|
|
273
250
|
_ts_metadata("design:type", Boolean)
|
|
274
251
|
], BulkResponseDto.prototype, "success", void 0);
|
|
275
252
|
_ts_decorate([
|
|
276
253
|
(0, _swagger.ApiProperty)({
|
|
277
|
-
example: 'Items processed successfully'
|
|
278
|
-
description: 'Response message'
|
|
254
|
+
example: 'Items processed successfully'
|
|
279
255
|
}),
|
|
280
256
|
_ts_metadata("design:type", String)
|
|
281
257
|
], BulkResponseDto.prototype, "message", void 0);
|
|
282
258
|
_ts_decorate([
|
|
283
259
|
(0, _swagger.ApiPropertyOptional)({
|
|
284
|
-
description: 'Response data (array of items)',
|
|
285
260
|
isArray: true
|
|
286
261
|
}),
|
|
287
262
|
_ts_metadata("design:type", Array)
|
|
288
263
|
], BulkResponseDto.prototype, "data", void 0);
|
|
289
264
|
_ts_decorate([
|
|
290
265
|
(0, _swagger.ApiProperty)({
|
|
291
|
-
type: BulkMetaDto
|
|
292
|
-
description: 'Bulk operation metadata'
|
|
266
|
+
type: BulkMetaDto
|
|
293
267
|
}),
|
|
294
268
|
_ts_metadata("design:type", typeof BulkMetaDto === "undefined" ? Object : BulkMetaDto)
|
|
295
269
|
], BulkResponseDto.prototype, "meta", void 0);
|
|
296
270
|
_ts_decorate([
|
|
297
271
|
(0, _swagger.ApiPropertyOptional)({
|
|
298
|
-
type: RequestMetaDto
|
|
299
|
-
description: 'Request metadata'
|
|
272
|
+
type: RequestMetaDto
|
|
300
273
|
}),
|
|
301
274
|
_ts_metadata("design:type", typeof RequestMetaDto === "undefined" ? Object : RequestMetaDto)
|
|
302
275
|
], BulkResponseDto.prototype, "_meta", void 0);
|
|
@@ -312,22 +285,19 @@ let MessageResponseDto = class MessageResponseDto {
|
|
|
312
285
|
};
|
|
313
286
|
_ts_decorate([
|
|
314
287
|
(0, _swagger.ApiProperty)({
|
|
315
|
-
example: true
|
|
316
|
-
description: 'Whether the operation was successful'
|
|
288
|
+
example: true
|
|
317
289
|
}),
|
|
318
290
|
_ts_metadata("design:type", Boolean)
|
|
319
291
|
], MessageResponseDto.prototype, "success", void 0);
|
|
320
292
|
_ts_decorate([
|
|
321
293
|
(0, _swagger.ApiProperty)({
|
|
322
|
-
example: 'Operation completed successfully'
|
|
323
|
-
description: 'Response message'
|
|
294
|
+
example: 'Operation completed successfully'
|
|
324
295
|
}),
|
|
325
296
|
_ts_metadata("design:type", String)
|
|
326
297
|
], MessageResponseDto.prototype, "message", void 0);
|
|
327
298
|
_ts_decorate([
|
|
328
299
|
(0, _swagger.ApiPropertyOptional)({
|
|
329
|
-
type: RequestMetaDto
|
|
330
|
-
description: 'Request metadata'
|
|
300
|
+
type: RequestMetaDto
|
|
331
301
|
}),
|
|
332
302
|
_ts_metadata("design:type", typeof RequestMetaDto === "undefined" ? Object : RequestMetaDto)
|
|
333
303
|
], MessageResponseDto.prototype, "_meta", void 0);
|
|
@@ -343,22 +313,19 @@ let ValidationErrorDto = class ValidationErrorDto {
|
|
|
343
313
|
};
|
|
344
314
|
_ts_decorate([
|
|
345
315
|
(0, _swagger.ApiProperty)({
|
|
346
|
-
example: 'email'
|
|
347
|
-
description: 'Field that failed validation'
|
|
316
|
+
example: 'email'
|
|
348
317
|
}),
|
|
349
318
|
_ts_metadata("design:type", String)
|
|
350
319
|
], ValidationErrorDto.prototype, "field", void 0);
|
|
351
320
|
_ts_decorate([
|
|
352
321
|
(0, _swagger.ApiProperty)({
|
|
353
|
-
example: 'Invalid email format'
|
|
354
|
-
description: 'Error message'
|
|
322
|
+
example: 'Invalid email format'
|
|
355
323
|
}),
|
|
356
324
|
_ts_metadata("design:type", String)
|
|
357
325
|
], ValidationErrorDto.prototype, "message", void 0);
|
|
358
326
|
_ts_decorate([
|
|
359
327
|
(0, _swagger.ApiPropertyOptional)({
|
|
360
|
-
example: 'isEmail'
|
|
361
|
-
description: 'Validation constraint that failed'
|
|
328
|
+
example: 'isEmail'
|
|
362
329
|
}),
|
|
363
330
|
_ts_metadata("design:type", String)
|
|
364
331
|
], ValidationErrorDto.prototype, "constraint", void 0);
|
|
@@ -373,22 +340,19 @@ let ErrorResponseDto = class ErrorResponseDto {
|
|
|
373
340
|
};
|
|
374
341
|
_ts_decorate([
|
|
375
342
|
(0, _swagger.ApiProperty)({
|
|
376
|
-
example: false
|
|
377
|
-
description: 'Always false for errors'
|
|
343
|
+
example: false
|
|
378
344
|
}),
|
|
379
345
|
_ts_metadata("design:type", Boolean)
|
|
380
346
|
], ErrorResponseDto.prototype, "success", void 0);
|
|
381
347
|
_ts_decorate([
|
|
382
348
|
(0, _swagger.ApiProperty)({
|
|
383
|
-
example: 'Validation failed'
|
|
384
|
-
description: 'Error message'
|
|
349
|
+
example: 'Validation failed'
|
|
385
350
|
}),
|
|
386
351
|
_ts_metadata("design:type", String)
|
|
387
352
|
], ErrorResponseDto.prototype, "message", void 0);
|
|
388
353
|
_ts_decorate([
|
|
389
354
|
(0, _swagger.ApiPropertyOptional)({
|
|
390
|
-
example: 'VALIDATION_ERROR'
|
|
391
|
-
description: 'Error code'
|
|
355
|
+
example: 'VALIDATION_ERROR'
|
|
392
356
|
}),
|
|
393
357
|
_ts_metadata("design:type", String)
|
|
394
358
|
], ErrorResponseDto.prototype, "code", void 0);
|
|
@@ -396,15 +360,13 @@ _ts_decorate([
|
|
|
396
360
|
(0, _swagger.ApiPropertyOptional)({
|
|
397
361
|
type: [
|
|
398
362
|
ValidationErrorDto
|
|
399
|
-
]
|
|
400
|
-
description: 'Validation errors (for 400 responses)'
|
|
363
|
+
]
|
|
401
364
|
}),
|
|
402
365
|
_ts_metadata("design:type", Array)
|
|
403
366
|
], ErrorResponseDto.prototype, "errors", void 0);
|
|
404
367
|
_ts_decorate([
|
|
405
368
|
(0, _swagger.ApiPropertyOptional)({
|
|
406
|
-
type: RequestMetaDto
|
|
407
|
-
description: 'Request metadata'
|
|
369
|
+
type: RequestMetaDto
|
|
408
370
|
}),
|
|
409
371
|
_ts_metadata("design:type", typeof RequestMetaDto === "undefined" ? Object : RequestMetaDto)
|
|
410
372
|
], ErrorResponseDto.prototype, "_meta", void 0);
|
|
@@ -422,34 +384,27 @@ let ResponsePayloadDto = class ResponsePayloadDto {
|
|
|
422
384
|
};
|
|
423
385
|
_ts_decorate([
|
|
424
386
|
(0, _swagger.ApiProperty)({
|
|
425
|
-
example: true
|
|
426
|
-
description: 'Whether the operation was successful'
|
|
387
|
+
example: true
|
|
427
388
|
}),
|
|
428
389
|
_ts_metadata("design:type", Boolean)
|
|
429
390
|
], ResponsePayloadDto.prototype, "success", void 0);
|
|
430
391
|
_ts_decorate([
|
|
431
392
|
(0, _swagger.ApiProperty)({
|
|
432
|
-
example: 'Operation successful'
|
|
433
|
-
description: 'Response message'
|
|
393
|
+
example: 'Operation successful'
|
|
434
394
|
}),
|
|
435
395
|
_ts_metadata("design:type", String)
|
|
436
396
|
], ResponsePayloadDto.prototype, "message", void 0);
|
|
437
397
|
_ts_decorate([
|
|
438
|
-
(0, _swagger.ApiPropertyOptional)(
|
|
439
|
-
description: 'Response data'
|
|
440
|
-
}),
|
|
398
|
+
(0, _swagger.ApiPropertyOptional)(),
|
|
441
399
|
_ts_metadata("design:type", typeof T === "undefined" ? Object : T)
|
|
442
400
|
], ResponsePayloadDto.prototype, "data", void 0);
|
|
443
401
|
_ts_decorate([
|
|
444
|
-
(0, _swagger.ApiPropertyOptional)(
|
|
445
|
-
description: 'Response metadata'
|
|
446
|
-
}),
|
|
402
|
+
(0, _swagger.ApiPropertyOptional)(),
|
|
447
403
|
_ts_metadata("design:type", Object)
|
|
448
404
|
], ResponsePayloadDto.prototype, "meta", void 0);
|
|
449
405
|
_ts_decorate([
|
|
450
406
|
(0, _swagger.ApiPropertyOptional)({
|
|
451
|
-
type: RequestMetaDto
|
|
452
|
-
description: 'Request metadata'
|
|
407
|
+
type: RequestMetaDto
|
|
453
408
|
}),
|
|
454
409
|
_ts_metadata("design:type", typeof RequestMetaDto === "undefined" ? Object : RequestMetaDto)
|
|
455
410
|
], ResponsePayloadDto.prototype, "_meta", void 0);
|
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Permission-related interfaces and types
|
|
3
|
-
*/ // =============================================================================
|
|
4
|
-
// PERMISSION OPERATORS
|
|
5
|
-
// =============================================================================
|
|
6
|
-
/**
|
|
7
|
-
* Permission operator type
|
|
8
|
-
* - 'and': User must have ALL permissions
|
|
9
|
-
* - 'or': User must have at least ONE permission
|
|
10
|
-
*/ "use strict";
|
|
1
|
+
"use strict";
|
|
11
2
|
Object.defineProperty(exports, "__esModule", {
|
|
12
3
|
value: true
|
|
13
4
|
});
|
|
@@ -59,9 +59,7 @@ function _ts_decorate(decorators, target, key, desc) {
|
|
|
59
59
|
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
60
60
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
61
61
|
}
|
|
62
|
-
//
|
|
63
|
-
// CONFIGURATION
|
|
64
|
-
// =============================================================================
|
|
62
|
+
// Configuration
|
|
65
63
|
const IS_DEBUG = _config.envConfig.getLogConfig().level === 'debug';
|
|
66
64
|
const TENANT_ID_HEADER = 'x-tenant-id';
|
|
67
65
|
const EXCLUDED_PATHS = [
|
|
@@ -88,9 +86,7 @@ const setCompanyId = (companyId)=>{
|
|
|
88
86
|
const store = requestContext.getStore();
|
|
89
87
|
if (store) store.companyId = companyId;
|
|
90
88
|
};
|
|
91
|
-
//
|
|
92
|
-
// HELPER FUNCTIONS
|
|
93
|
-
// =============================================================================
|
|
89
|
+
// Helper Functions
|
|
94
90
|
function sanitizeHeaders(headers) {
|
|
95
91
|
const sanitized = {};
|
|
96
92
|
for (const [key, value] of Object.entries(headers)){
|
|
@@ -42,7 +42,7 @@ function _ts_param(paramIndex, decorator) {
|
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
44
|
let MultiTenantDataSourceService = class MultiTenantDataSourceService {
|
|
45
|
-
//
|
|
45
|
+
// Initialization
|
|
46
46
|
initializeFromOptions() {
|
|
47
47
|
if (!this.options) return;
|
|
48
48
|
if (!MultiTenantDataSourceService.initialized) {
|
|
@@ -55,7 +55,7 @@ let MultiTenantDataSourceService = class MultiTenantDataSourceService {
|
|
|
55
55
|
MultiTenantDataSourceService.tenantsRegistry.set(tenant.id, tenant);
|
|
56
56
|
});
|
|
57
57
|
}
|
|
58
|
-
//
|
|
58
|
+
// Public API
|
|
59
59
|
/**
|
|
60
60
|
* Set custom tenant header name
|
|
61
61
|
*/ setTenantHeader(header) {
|
|
@@ -71,7 +71,7 @@ let MultiTenantDataSourceService = class MultiTenantDataSourceService {
|
|
|
71
71
|
*/ isMultiTenant() {
|
|
72
72
|
return this.getDatabaseMode() === 'multi-tenant';
|
|
73
73
|
}
|
|
74
|
-
//
|
|
74
|
+
// Tenant Resolution
|
|
75
75
|
/**
|
|
76
76
|
* Get current tenant ID from request header
|
|
77
77
|
* Validates tenant ID format for security (alphanumeric, hyphens, underscores only)
|
|
@@ -106,7 +106,7 @@ let MultiTenantDataSourceService = class MultiTenantDataSourceService {
|
|
|
106
106
|
*/ getActiveTenants() {
|
|
107
107
|
return this.getAllTenants();
|
|
108
108
|
}
|
|
109
|
-
//
|
|
109
|
+
// DataSource Access
|
|
110
110
|
/**
|
|
111
111
|
* Get DataSource for current context (tenant or single)
|
|
112
112
|
*/ async getDataSource() {
|
|
@@ -126,7 +126,7 @@ let MultiTenantDataSourceService = class MultiTenantDataSourceService {
|
|
|
126
126
|
*/ setDataSource(dataSource) {
|
|
127
127
|
MultiTenantDataSourceService.singleDataSource = dataSource;
|
|
128
128
|
}
|
|
129
|
-
//
|
|
129
|
+
// Repository Access
|
|
130
130
|
/**
|
|
131
131
|
* Get repository for entity in current context
|
|
132
132
|
*/ async getRepository(entity) {
|
|
@@ -139,7 +139,7 @@ let MultiTenantDataSourceService = class MultiTenantDataSourceService {
|
|
|
139
139
|
const dataSource = await this.getDataSourceForTenant(tenantId);
|
|
140
140
|
return dataSource.getRepository(entity);
|
|
141
141
|
}
|
|
142
|
-
//
|
|
142
|
+
// Multi-Tenant Operations
|
|
143
143
|
/**
|
|
144
144
|
* Execute callback with specific tenant's DataSource
|
|
145
145
|
*/ async withTenant(tenantId, callback) {
|
|
@@ -160,7 +160,7 @@ let MultiTenantDataSourceService = class MultiTenantDataSourceService {
|
|
|
160
160
|
}
|
|
161
161
|
return results;
|
|
162
162
|
}
|
|
163
|
-
//
|
|
163
|
+
// Tenant Management
|
|
164
164
|
/**
|
|
165
165
|
* Register a new tenant at runtime
|
|
166
166
|
*/ registerTenant(tenant) {
|
|
@@ -172,7 +172,7 @@ let MultiTenantDataSourceService = class MultiTenantDataSourceService {
|
|
|
172
172
|
await this.closeTenantConnection(tenantId);
|
|
173
173
|
MultiTenantDataSourceService.tenantsRegistry.delete(tenantId);
|
|
174
174
|
}
|
|
175
|
-
//
|
|
175
|
+
// Connection Lifecycle
|
|
176
176
|
/**
|
|
177
177
|
* Close specific tenant connection
|
|
178
178
|
*/ async closeTenantConnection(tenantId) {
|
|
@@ -200,7 +200,7 @@ let MultiTenantDataSourceService = class MultiTenantDataSourceService {
|
|
|
200
200
|
MultiTenantDataSourceService.tenantsRegistry.clear();
|
|
201
201
|
MultiTenantDataSourceService.connectionLocks.clear();
|
|
202
202
|
}
|
|
203
|
-
//
|
|
203
|
+
// Protected Methods (for subclasses)
|
|
204
204
|
/**
|
|
205
205
|
* Get single database DataSource with connection locking to prevent race conditions
|
|
206
206
|
*/ async getSingleDataSource() {
|
|
@@ -77,54 +77,7 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
77
77
|
}
|
|
78
78
|
return applyDecorators(...decorators);
|
|
79
79
|
}
|
|
80
|
-
|
|
81
|
-
// API CONTROLLER FACTORY
|
|
82
|
-
// =============================================================================
|
|
83
|
-
/**
|
|
84
|
-
* Creates an API Controller with standardized CRUD endpoints
|
|
85
|
-
*
|
|
86
|
-
* Features:
|
|
87
|
-
* - POST-only API pattern (RPC-style)
|
|
88
|
-
* - Configurable security per endpoint
|
|
89
|
-
* - Consistent response format
|
|
90
|
-
* - Idempotency support for inserts
|
|
91
|
-
* - Pagination with metadata
|
|
92
|
-
* - Bulk operations with counts
|
|
93
|
-
* - Soft delete/restore/permanent delete
|
|
94
|
-
*
|
|
95
|
-
* @param createDtoClass - DTO for create operations
|
|
96
|
-
* @param updateDtoClass - DTO for update operations
|
|
97
|
-
* @param responseDtoClass - DTO for response serialization
|
|
98
|
-
* @param options - Controller options including security configuration
|
|
99
|
-
*
|
|
100
|
-
* @example
|
|
101
|
-
* // All public (default)
|
|
102
|
-
* class ProductController extends createApiController(CreateDto, UpdateDto, ResponseDto) {}
|
|
103
|
-
*
|
|
104
|
-
* @example
|
|
105
|
-
* // Option 1: Apply one security config to ALL endpoints
|
|
106
|
-
* class ProductController extends createApiController(CreateDto, UpdateDto, ResponseDto, {
|
|
107
|
-
* security: 'jwt' // All endpoints require JWT
|
|
108
|
-
* })
|
|
109
|
-
*
|
|
110
|
-
* @example
|
|
111
|
-
* // Option 1: Global permission for all endpoints
|
|
112
|
-
* class ProductController extends createApiController(CreateDto, UpdateDto, ResponseDto, {
|
|
113
|
-
* security: { level: 'permission', permissions: ['products.*'] }
|
|
114
|
-
* })
|
|
115
|
-
*
|
|
116
|
-
* @example
|
|
117
|
-
* // Option 2: Configure security per endpoint
|
|
118
|
-
* class ProductController extends createApiController(CreateDto, UpdateDto, ResponseDto, {
|
|
119
|
-
* security: {
|
|
120
|
-
* getAll: 'public',
|
|
121
|
-
* getById: 'jwt',
|
|
122
|
-
* insert: { level: 'permission', permissions: ['products.create'] },
|
|
123
|
-
* update: { level: 'permission', permissions: ['products.update'] },
|
|
124
|
-
* delete: { level: 'permission', permissions: ['products.delete'] },
|
|
125
|
-
* }
|
|
126
|
-
* }) {}
|
|
127
|
-
*/ export function createApiController(createDtoClass, updateDtoClass, responseDtoClass, options = {}) {
|
|
80
|
+
/** Creates an API Controller with standardized CRUD endpoints (POST-only RPC pattern) */ export function createApiController(createDtoClass, updateDtoClass, responseDtoClass, options = {}) {
|
|
128
81
|
// Determine if security is global (applies to all) or per-endpoint
|
|
129
82
|
const securityConfig = options.security;
|
|
130
83
|
const endpointKeys = [
|
|
@@ -154,9 +107,6 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
154
107
|
delete: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.delete)
|
|
155
108
|
};
|
|
156
109
|
let ApiController = class ApiController {
|
|
157
|
-
// =========================================================================
|
|
158
|
-
// INSERT (Single Item) - With Idempotency Support
|
|
159
|
-
// =========================================================================
|
|
160
110
|
async insert(addDto, user) {
|
|
161
111
|
const entity = await this.service.insert(addDto, user);
|
|
162
112
|
const data = plainToInstance(responseDtoClass, entity);
|
|
@@ -166,9 +116,6 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
166
116
|
data
|
|
167
117
|
};
|
|
168
118
|
}
|
|
169
|
-
// =========================================================================
|
|
170
|
-
// INSERT MANY (Bulk) - With Idempotency Support
|
|
171
|
-
// =========================================================================
|
|
172
119
|
async insertMany(addDto, user) {
|
|
173
120
|
const entities = await this.service.insertMany(addDto, user);
|
|
174
121
|
const data = entities.map((item)=>plainToInstance(responseDtoClass, item));
|
|
@@ -183,9 +130,6 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
183
130
|
}
|
|
184
131
|
};
|
|
185
132
|
}
|
|
186
|
-
// =========================================================================
|
|
187
|
-
// GET BY ID (Single Item)
|
|
188
|
-
// =========================================================================
|
|
189
133
|
async getById(id, body, user) {
|
|
190
134
|
const entity = await this.service.findById(id, user, body?.select);
|
|
191
135
|
const data = plainToInstance(responseDtoClass, entity);
|
|
@@ -195,9 +139,6 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
195
139
|
data
|
|
196
140
|
};
|
|
197
141
|
}
|
|
198
|
-
// =========================================================================
|
|
199
|
-
// UPDATE (Single Item)
|
|
200
|
-
// =========================================================================
|
|
201
142
|
async update(updateDto, user) {
|
|
202
143
|
const entity = await this.service.update(updateDto, user);
|
|
203
144
|
const data = plainToInstance(responseDtoClass, entity);
|
|
@@ -207,9 +148,6 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
207
148
|
data
|
|
208
149
|
};
|
|
209
150
|
}
|
|
210
|
-
// =========================================================================
|
|
211
|
-
// UPDATE MANY (Bulk)
|
|
212
|
-
// =========================================================================
|
|
213
151
|
async updateMany(updateDtos, user) {
|
|
214
152
|
const entities = await this.service.updateMany(updateDtos, user);
|
|
215
153
|
const data = plainToInstance(responseDtoClass, entities);
|
|
@@ -224,9 +162,6 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
224
162
|
}
|
|
225
163
|
};
|
|
226
164
|
}
|
|
227
|
-
// =========================================================================
|
|
228
|
-
// GET ALL (Paginated List)
|
|
229
|
-
// =========================================================================
|
|
230
165
|
async getAll(filterAndPaginationDto, user, search) {
|
|
231
166
|
const result = await this.service.getAll(search ?? '', filterAndPaginationDto, user);
|
|
232
167
|
const data = plainToInstance(responseDtoClass, result.data);
|
|
@@ -247,9 +182,6 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
247
182
|
}
|
|
248
183
|
};
|
|
249
184
|
}
|
|
250
|
-
// =========================================================================
|
|
251
|
-
// DELETE (Soft/Restore/Permanent)
|
|
252
|
-
// =========================================================================
|
|
253
185
|
async delete(deleteDto, user) {
|
|
254
186
|
await this.service.delete(deleteDto, user);
|
|
255
187
|
const count = Array.isArray(deleteDto.id) ? deleteDto.id.length : 1;
|