@flusys/nestjs-shared 1.1.0-beta → 2.0.0
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 +501 -720
- package/cjs/classes/api-controller.class.js +9 -24
- package/cjs/classes/api-service.class.js +59 -92
- package/cjs/classes/index.js +1 -0
- package/cjs/classes/winston-logger-adapter.class.js +23 -40
- package/cjs/constants/index.js +14 -0
- package/cjs/constants/permissions.js +184 -0
- package/cjs/decorators/api-response.decorator.js +1 -1
- package/cjs/decorators/index.js +1 -0
- package/cjs/decorators/sanitize-html.decorator.js +36 -0
- package/cjs/dtos/delete.dto.js +10 -0
- package/cjs/dtos/filter-and-pagination.dto.js +24 -34
- package/cjs/dtos/pagination.dto.js +4 -8
- package/cjs/dtos/response-payload.dto.js +0 -116
- package/cjs/entities/identity.js +4 -4
- package/cjs/entities/user-root.js +13 -14
- package/cjs/guards/permission.guard.js +51 -105
- package/cjs/interceptors/index.js +1 -3
- package/cjs/interceptors/set-user-field-on-body.interceptor.js +60 -0
- package/cjs/interceptors/slug.interceptor.js +30 -9
- package/cjs/interfaces/datasource.interface.js +4 -0
- package/cjs/interfaces/index.js +2 -1
- package/cjs/interfaces/module-config.interface.js +4 -0
- package/cjs/middlewares/logger.middleware.js +50 -89
- package/cjs/modules/cache/cache.module.js +3 -3
- package/cjs/modules/datasource/datasource.module.js +11 -14
- package/cjs/modules/datasource/multi-tenant-datasource.service.js +29 -113
- package/cjs/modules/utils/utils.service.js +40 -203
- package/cjs/utils/error-handler.util.js +35 -12
- package/cjs/utils/html-sanitizer.util.js +64 -0
- package/cjs/utils/index.js +4 -0
- package/cjs/utils/query-helpers.util.js +53 -0
- package/cjs/utils/request.util.js +70 -0
- package/cjs/utils/string.util.js +63 -0
- package/classes/api-controller.class.d.ts +5 -5
- package/classes/api-service.class.d.ts +7 -5
- package/classes/index.d.ts +1 -0
- package/classes/request-scoped-api.service.d.ts +3 -2
- package/classes/winston-logger-adapter.class.d.ts +2 -0
- package/constants/index.d.ts +1 -0
- package/constants/permissions.d.ts +179 -0
- package/decorators/index.d.ts +1 -0
- package/decorators/sanitize-html.decorator.d.ts +2 -0
- package/dtos/delete.dto.d.ts +1 -0
- package/dtos/filter-and-pagination.dto.d.ts +0 -2
- package/dtos/response-payload.dto.d.ts +0 -20
- package/fesm/classes/api-controller.class.js +9 -24
- package/fesm/classes/api-service.class.js +59 -92
- package/fesm/classes/index.js +2 -0
- package/fesm/classes/winston-logger-adapter.class.js +23 -40
- package/fesm/constants/index.js +2 -0
- package/fesm/constants/permissions.js +128 -0
- package/fesm/decorators/api-response.decorator.js +1 -1
- package/fesm/decorators/index.js +1 -0
- package/fesm/decorators/sanitize-html.decorator.js +45 -0
- package/fesm/dtos/delete.dto.js +12 -2
- package/fesm/dtos/filter-and-pagination.dto.js +26 -47
- package/fesm/dtos/pagination.dto.js +4 -8
- package/fesm/dtos/response-payload.dto.js +0 -107
- package/fesm/entities/identity.js +4 -4
- package/fesm/entities/user-root.js +13 -14
- package/fesm/guards/permission.guard.js +51 -105
- package/fesm/interceptors/index.js +1 -3
- package/fesm/interceptors/set-user-field-on-body.interceptor.js +39 -0
- package/fesm/interceptors/slug.interceptor.js +31 -10
- package/fesm/interfaces/datasource.interface.js +20 -0
- package/fesm/interfaces/index.js +2 -1
- package/fesm/interfaces/module-config.interface.js +5 -0
- package/fesm/middlewares/logger.middleware.js +50 -83
- package/fesm/modules/cache/cache.module.js +2 -2
- package/fesm/modules/datasource/datasource.module.js +11 -14
- package/fesm/modules/datasource/multi-tenant-datasource.service.js +29 -113
- package/fesm/modules/utils/utils.service.js +41 -204
- package/fesm/utils/error-handler.util.js +36 -13
- package/fesm/utils/html-sanitizer.util.js +69 -0
- package/fesm/utils/index.js +4 -0
- package/fesm/utils/query-helpers.util.js +78 -0
- package/fesm/utils/request.util.js +58 -0
- package/fesm/utils/string.util.js +71 -0
- package/guards/permission.guard.d.ts +2 -0
- package/interceptors/index.d.ts +1 -3
- package/interceptors/set-user-field-on-body.interceptor.d.ts +5 -0
- package/interceptors/slug.interceptor.d.ts +2 -1
- package/interfaces/api.interface.d.ts +2 -2
- package/interfaces/datasource.interface.d.ts +5 -0
- package/interfaces/identity.interface.d.ts +4 -4
- package/interfaces/index.d.ts +2 -1
- package/interfaces/logged-user-info.interface.d.ts +0 -2
- package/interfaces/module-config.interface.d.ts +6 -0
- package/interfaces/permission.interface.d.ts +0 -1
- package/middlewares/logger.middleware.d.ts +2 -2
- package/modules/datasource/datasource.module.d.ts +1 -0
- package/modules/datasource/multi-tenant-datasource.service.d.ts +0 -1
- package/modules/utils/utils.service.d.ts +4 -14
- package/package.json +4 -4
- package/utils/error-handler.util.d.ts +14 -19
- package/utils/html-sanitizer.util.d.ts +2 -0
- package/utils/index.d.ts +4 -0
- package/utils/query-helpers.util.d.ts +16 -0
- package/utils/request.util.d.ts +4 -0
- package/utils/string.util.d.ts +2 -0
- package/cjs/interceptors/set-create-by-on-body.interceptor.js +0 -40
- package/cjs/interceptors/set-delete-by-on-body.interceptor.js +0 -40
- package/cjs/interceptors/set-update-by-on-body.interceptor.js +0 -40
- package/cjs/interfaces/base-query.interface.js +0 -6
- package/fesm/interceptors/set-create-by-on-body.interceptor.js +0 -30
- package/fesm/interceptors/set-delete-by-on-body.interceptor.js +0 -30
- package/fesm/interceptors/set-update-by-on-body.interceptor.js +0 -30
- package/fesm/interfaces/base-query.interface.js +0 -3
- package/interceptors/set-create-by-on-body.interceptor.d.ts +0 -5
- package/interceptors/set-delete-by-on-body.interceptor.d.ts +0 -5
- package/interceptors/set-update-by-on-body.interceptor.d.ts +0 -5
- package/interfaces/base-query.interface.d.ts +0 -7
|
@@ -49,7 +49,6 @@ let FilterAndPaginationDto = class FilterAndPaginationDto {
|
|
|
49
49
|
_define_property(this, "sort", void 0);
|
|
50
50
|
_define_property(this, "select", void 0);
|
|
51
51
|
_define_property(this, "withDeleted", void 0);
|
|
52
|
-
_define_property(this, "extraKey", void 0);
|
|
53
52
|
}
|
|
54
53
|
};
|
|
55
54
|
_ts_decorate([
|
|
@@ -97,7 +96,7 @@ _ts_decorate([
|
|
|
97
96
|
type: [
|
|
98
97
|
String
|
|
99
98
|
],
|
|
100
|
-
description: 'Fields to return. If empty, returns all fields.',
|
|
99
|
+
description: 'Fields to return. Must be valid field names (alphanumeric, underscores only). If empty, returns all fields.',
|
|
101
100
|
example: [
|
|
102
101
|
'id',
|
|
103
102
|
'name',
|
|
@@ -107,6 +106,17 @@ _ts_decorate([
|
|
|
107
106
|
}),
|
|
108
107
|
(0, _classvalidator.IsOptional)(),
|
|
109
108
|
(0, _classvalidator.IsArray)(),
|
|
109
|
+
(0, _classvalidator.IsString)({
|
|
110
|
+
each: true
|
|
111
|
+
}),
|
|
112
|
+
(0, _classvalidator.Matches)(/^[a-zA-Z_][a-zA-Z0-9_]*$/, {
|
|
113
|
+
each: true,
|
|
114
|
+
message: 'Select fields must be valid identifiers (alphanumeric and underscores only)'
|
|
115
|
+
}),
|
|
116
|
+
(0, _classvalidator.MaxLength)(64, {
|
|
117
|
+
each: true,
|
|
118
|
+
message: 'Field names must be 64 characters or less'
|
|
119
|
+
}),
|
|
110
120
|
_ts_metadata("design:type", Array)
|
|
111
121
|
], FilterAndPaginationDto.prototype, "select", void 0);
|
|
112
122
|
_ts_decorate([
|
|
@@ -119,25 +129,9 @@ _ts_decorate([
|
|
|
119
129
|
(0, _classvalidator.IsBoolean)(),
|
|
120
130
|
_ts_metadata("design:type", Boolean)
|
|
121
131
|
], FilterAndPaginationDto.prototype, "withDeleted", void 0);
|
|
122
|
-
_ts_decorate([
|
|
123
|
-
(0, _swagger.ApiPropertyOptional)({
|
|
124
|
-
type: [
|
|
125
|
-
String
|
|
126
|
-
],
|
|
127
|
-
description: 'Additional relation keys to include',
|
|
128
|
-
example: [
|
|
129
|
-
'category',
|
|
130
|
-
'createdBy'
|
|
131
|
-
]
|
|
132
|
-
}),
|
|
133
|
-
(0, _classvalidator.IsOptional)(),
|
|
134
|
-
(0, _classvalidator.IsArray)(),
|
|
135
|
-
_ts_metadata("design:type", Array)
|
|
136
|
-
], FilterAndPaginationDto.prototype, "extraKey", void 0);
|
|
137
132
|
let GetByIdBodyDto = class GetByIdBodyDto {
|
|
138
133
|
constructor(){
|
|
139
134
|
_define_property(this, "select", void 0);
|
|
140
|
-
_define_property(this, "extraKey", void 0);
|
|
141
135
|
}
|
|
142
136
|
};
|
|
143
137
|
_ts_decorate([
|
|
@@ -145,7 +139,7 @@ _ts_decorate([
|
|
|
145
139
|
type: [
|
|
146
140
|
String
|
|
147
141
|
],
|
|
148
|
-
description: 'Fields to return. If empty, returns all fields.',
|
|
142
|
+
description: 'Fields to return. Must be valid field names (alphanumeric, underscores only). If empty, returns all fields.',
|
|
149
143
|
example: [
|
|
150
144
|
'id',
|
|
151
145
|
'name',
|
|
@@ -155,20 +149,16 @@ _ts_decorate([
|
|
|
155
149
|
}),
|
|
156
150
|
(0, _classvalidator.IsOptional)(),
|
|
157
151
|
(0, _classvalidator.IsArray)(),
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
(0,
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
'createdBy'
|
|
169
|
-
]
|
|
152
|
+
(0, _classvalidator.IsString)({
|
|
153
|
+
each: true
|
|
154
|
+
}),
|
|
155
|
+
(0, _classvalidator.Matches)(/^[a-zA-Z_][a-zA-Z0-9_]*$/, {
|
|
156
|
+
each: true,
|
|
157
|
+
message: 'Select fields must be valid identifiers (alphanumeric and underscores only)'
|
|
158
|
+
}),
|
|
159
|
+
(0, _classvalidator.MaxLength)(64, {
|
|
160
|
+
each: true,
|
|
161
|
+
message: 'Field names must be 64 characters or less'
|
|
170
162
|
}),
|
|
171
|
-
(0, _classvalidator.IsOptional)(),
|
|
172
|
-
(0, _classvalidator.IsArray)(),
|
|
173
163
|
_ts_metadata("design:type", Array)
|
|
174
|
-
], GetByIdBodyDto.prototype, "
|
|
164
|
+
], GetByIdBodyDto.prototype, "select", void 0);
|
|
@@ -35,17 +35,13 @@ function _ts_metadata(k, v) {
|
|
|
35
35
|
}
|
|
36
36
|
let PaginationDto = class PaginationDto {
|
|
37
37
|
constructor(){
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
*/ _define_property(this, "pageSize", 10);
|
|
41
|
-
/**
|
|
42
|
-
* Zero-based page index. Defaults to 0 when not provided or invalid.
|
|
43
|
-
*/ _define_property(this, "currentPage", 0);
|
|
38
|
+
_define_property(this, "pageSize", 10);
|
|
39
|
+
_define_property(this, "currentPage", 0);
|
|
44
40
|
}
|
|
45
41
|
};
|
|
46
42
|
_ts_decorate([
|
|
47
43
|
(0, _swagger.ApiPropertyOptional)({
|
|
48
|
-
description: 'Number of items per page
|
|
44
|
+
description: 'Number of items per page (default: 10)',
|
|
49
45
|
example: 10
|
|
50
46
|
}),
|
|
51
47
|
(0, _classvalidator.IsOptional)(),
|
|
@@ -58,7 +54,7 @@ _ts_decorate([
|
|
|
58
54
|
], PaginationDto.prototype, "pageSize", void 0);
|
|
59
55
|
_ts_decorate([
|
|
60
56
|
(0, _swagger.ApiPropertyOptional)({
|
|
61
|
-
description: 'Zero-based page index
|
|
57
|
+
description: 'Zero-based page index (default: 0)',
|
|
62
58
|
example: 0
|
|
63
59
|
}),
|
|
64
60
|
(0, _classvalidator.IsOptional)(),
|
|
@@ -15,9 +15,6 @@ _export(exports, {
|
|
|
15
15
|
get BulkResponseDto () {
|
|
16
16
|
return BulkResponseDto;
|
|
17
17
|
},
|
|
18
|
-
get ErrorResponseDto () {
|
|
19
|
-
return ErrorResponseDto;
|
|
20
|
-
},
|
|
21
18
|
get ListResponseDto () {
|
|
22
19
|
return ListResponseDto;
|
|
23
20
|
},
|
|
@@ -30,14 +27,8 @@ _export(exports, {
|
|
|
30
27
|
get RequestMetaDto () {
|
|
31
28
|
return RequestMetaDto;
|
|
32
29
|
},
|
|
33
|
-
get ResponsePayloadDto () {
|
|
34
|
-
return ResponsePayloadDto;
|
|
35
|
-
},
|
|
36
30
|
get SingleResponseDto () {
|
|
37
31
|
return SingleResponseDto;
|
|
38
|
-
},
|
|
39
|
-
get ValidationErrorDto () {
|
|
40
|
-
return ValidationErrorDto;
|
|
41
32
|
}
|
|
42
33
|
});
|
|
43
34
|
const _swagger = require("@nestjs/swagger");
|
|
@@ -304,110 +295,3 @@ _ts_decorate([
|
|
|
304
295
|
MessageResponseDto = _ts_decorate([
|
|
305
296
|
(0, _swagger.ApiExtraModels)()
|
|
306
297
|
], MessageResponseDto);
|
|
307
|
-
let ValidationErrorDto = class ValidationErrorDto {
|
|
308
|
-
constructor(){
|
|
309
|
-
_define_property(this, "field", void 0);
|
|
310
|
-
_define_property(this, "message", void 0);
|
|
311
|
-
_define_property(this, "constraint", void 0);
|
|
312
|
-
}
|
|
313
|
-
};
|
|
314
|
-
_ts_decorate([
|
|
315
|
-
(0, _swagger.ApiProperty)({
|
|
316
|
-
example: 'email'
|
|
317
|
-
}),
|
|
318
|
-
_ts_metadata("design:type", String)
|
|
319
|
-
], ValidationErrorDto.prototype, "field", void 0);
|
|
320
|
-
_ts_decorate([
|
|
321
|
-
(0, _swagger.ApiProperty)({
|
|
322
|
-
example: 'Invalid email format'
|
|
323
|
-
}),
|
|
324
|
-
_ts_metadata("design:type", String)
|
|
325
|
-
], ValidationErrorDto.prototype, "message", void 0);
|
|
326
|
-
_ts_decorate([
|
|
327
|
-
(0, _swagger.ApiPropertyOptional)({
|
|
328
|
-
example: 'isEmail'
|
|
329
|
-
}),
|
|
330
|
-
_ts_metadata("design:type", String)
|
|
331
|
-
], ValidationErrorDto.prototype, "constraint", void 0);
|
|
332
|
-
let ErrorResponseDto = class ErrorResponseDto {
|
|
333
|
-
constructor(){
|
|
334
|
-
_define_property(this, "success", void 0);
|
|
335
|
-
_define_property(this, "message", void 0);
|
|
336
|
-
_define_property(this, "code", void 0);
|
|
337
|
-
_define_property(this, "errors", void 0);
|
|
338
|
-
_define_property(this, "_meta", void 0);
|
|
339
|
-
}
|
|
340
|
-
};
|
|
341
|
-
_ts_decorate([
|
|
342
|
-
(0, _swagger.ApiProperty)({
|
|
343
|
-
example: false
|
|
344
|
-
}),
|
|
345
|
-
_ts_metadata("design:type", Boolean)
|
|
346
|
-
], ErrorResponseDto.prototype, "success", void 0);
|
|
347
|
-
_ts_decorate([
|
|
348
|
-
(0, _swagger.ApiProperty)({
|
|
349
|
-
example: 'Validation failed'
|
|
350
|
-
}),
|
|
351
|
-
_ts_metadata("design:type", String)
|
|
352
|
-
], ErrorResponseDto.prototype, "message", void 0);
|
|
353
|
-
_ts_decorate([
|
|
354
|
-
(0, _swagger.ApiPropertyOptional)({
|
|
355
|
-
example: 'VALIDATION_ERROR'
|
|
356
|
-
}),
|
|
357
|
-
_ts_metadata("design:type", String)
|
|
358
|
-
], ErrorResponseDto.prototype, "code", void 0);
|
|
359
|
-
_ts_decorate([
|
|
360
|
-
(0, _swagger.ApiPropertyOptional)({
|
|
361
|
-
type: [
|
|
362
|
-
ValidationErrorDto
|
|
363
|
-
]
|
|
364
|
-
}),
|
|
365
|
-
_ts_metadata("design:type", Array)
|
|
366
|
-
], ErrorResponseDto.prototype, "errors", void 0);
|
|
367
|
-
_ts_decorate([
|
|
368
|
-
(0, _swagger.ApiPropertyOptional)({
|
|
369
|
-
type: RequestMetaDto
|
|
370
|
-
}),
|
|
371
|
-
_ts_metadata("design:type", typeof RequestMetaDto === "undefined" ? Object : RequestMetaDto)
|
|
372
|
-
], ErrorResponseDto.prototype, "_meta", void 0);
|
|
373
|
-
ErrorResponseDto = _ts_decorate([
|
|
374
|
-
(0, _swagger.ApiExtraModels)()
|
|
375
|
-
], ErrorResponseDto);
|
|
376
|
-
let ResponsePayloadDto = class ResponsePayloadDto {
|
|
377
|
-
constructor(){
|
|
378
|
-
_define_property(this, "success", void 0);
|
|
379
|
-
_define_property(this, "message", void 0);
|
|
380
|
-
_define_property(this, "data", void 0);
|
|
381
|
-
_define_property(this, "meta", void 0);
|
|
382
|
-
_define_property(this, "_meta", void 0);
|
|
383
|
-
}
|
|
384
|
-
};
|
|
385
|
-
_ts_decorate([
|
|
386
|
-
(0, _swagger.ApiProperty)({
|
|
387
|
-
example: true
|
|
388
|
-
}),
|
|
389
|
-
_ts_metadata("design:type", Boolean)
|
|
390
|
-
], ResponsePayloadDto.prototype, "success", void 0);
|
|
391
|
-
_ts_decorate([
|
|
392
|
-
(0, _swagger.ApiProperty)({
|
|
393
|
-
example: 'Operation successful'
|
|
394
|
-
}),
|
|
395
|
-
_ts_metadata("design:type", String)
|
|
396
|
-
], ResponsePayloadDto.prototype, "message", void 0);
|
|
397
|
-
_ts_decorate([
|
|
398
|
-
(0, _swagger.ApiPropertyOptional)(),
|
|
399
|
-
_ts_metadata("design:type", typeof T === "undefined" ? Object : T)
|
|
400
|
-
], ResponsePayloadDto.prototype, "data", void 0);
|
|
401
|
-
_ts_decorate([
|
|
402
|
-
(0, _swagger.ApiPropertyOptional)(),
|
|
403
|
-
_ts_metadata("design:type", Object)
|
|
404
|
-
], ResponsePayloadDto.prototype, "meta", void 0);
|
|
405
|
-
_ts_decorate([
|
|
406
|
-
(0, _swagger.ApiPropertyOptional)({
|
|
407
|
-
type: RequestMetaDto
|
|
408
|
-
}),
|
|
409
|
-
_ts_metadata("design:type", typeof RequestMetaDto === "undefined" ? Object : RequestMetaDto)
|
|
410
|
-
], ResponsePayloadDto.prototype, "_meta", void 0);
|
|
411
|
-
ResponsePayloadDto = _ts_decorate([
|
|
412
|
-
(0, _swagger.ApiExtraModels)()
|
|
413
|
-
], ResponsePayloadDto);
|
package/cjs/entities/identity.js
CHANGED
|
@@ -36,10 +36,10 @@ let Identity = class Identity {
|
|
|
36
36
|
_define_property(this, "id", void 0);
|
|
37
37
|
_define_property(this, "createdAt", void 0);
|
|
38
38
|
_define_property(this, "updatedAt", void 0);
|
|
39
|
-
_define_property(this, "deletedAt",
|
|
40
|
-
_define_property(this, "createdById",
|
|
41
|
-
_define_property(this, "updatedById",
|
|
42
|
-
_define_property(this, "deletedById",
|
|
39
|
+
_define_property(this, "deletedAt", null);
|
|
40
|
+
_define_property(this, "createdById", null);
|
|
41
|
+
_define_property(this, "updatedById", null);
|
|
42
|
+
_define_property(this, "deletedById", null);
|
|
43
43
|
}
|
|
44
44
|
};
|
|
45
45
|
_ts_decorate([
|
|
@@ -34,23 +34,22 @@ function _ts_metadata(k, v) {
|
|
|
34
34
|
let UserRoot = class UserRoot {
|
|
35
35
|
constructor(){
|
|
36
36
|
_define_property(this, "id", void 0);
|
|
37
|
-
_define_property(this, "name",
|
|
38
|
-
_define_property(this, "password",
|
|
37
|
+
_define_property(this, "name", null);
|
|
38
|
+
_define_property(this, "password", null);
|
|
39
39
|
_define_property(this, "email", void 0);
|
|
40
|
-
_define_property(this, "phone",
|
|
41
|
-
_define_property(this, "isActive",
|
|
42
|
-
_define_property(this, "emailVerified",
|
|
43
|
-
_define_property(this, "phoneVerified",
|
|
44
|
-
_define_property(this, "profilePictureId",
|
|
45
|
-
_define_property(this, "lastLoginAt",
|
|
46
|
-
_define_property(this, "additionalFields",
|
|
40
|
+
_define_property(this, "phone", null);
|
|
41
|
+
_define_property(this, "isActive", true);
|
|
42
|
+
_define_property(this, "emailVerified", false);
|
|
43
|
+
_define_property(this, "phoneVerified", false);
|
|
44
|
+
_define_property(this, "profilePictureId", null);
|
|
45
|
+
_define_property(this, "lastLoginAt", null);
|
|
46
|
+
_define_property(this, "additionalFields", null);
|
|
47
47
|
_define_property(this, "createdAt", void 0);
|
|
48
48
|
_define_property(this, "updatedAt", void 0);
|
|
49
|
-
_define_property(this, "deletedAt",
|
|
50
|
-
|
|
51
|
-
_define_property(this, "
|
|
52
|
-
_define_property(this, "
|
|
53
|
-
_define_property(this, "deletedById", void 0);
|
|
49
|
+
_define_property(this, "deletedAt", null);
|
|
50
|
+
_define_property(this, "createdById", null);
|
|
51
|
+
_define_property(this, "updatedById", null);
|
|
52
|
+
_define_property(this, "deletedById", null);
|
|
54
53
|
}
|
|
55
54
|
};
|
|
56
55
|
_ts_decorate([
|
|
@@ -45,106 +45,81 @@ function _ts_param(paramIndex, decorator) {
|
|
|
45
45
|
}
|
|
46
46
|
let PermissionGuard = class PermissionGuard {
|
|
47
47
|
async canActivate(context) {
|
|
48
|
-
// Check if route is marked as public
|
|
49
48
|
const isPublic = this.reflector.getAllAndOverride(_constants.IS_PUBLIC_KEY, [
|
|
50
49
|
context.getHandler(),
|
|
51
50
|
context.getClass()
|
|
52
51
|
]);
|
|
53
52
|
if (isPublic) return true;
|
|
54
|
-
// Get required permissions from decorator
|
|
55
53
|
const permissionConfig = this.reflector.getAllAndOverride(_constants.PERMISSIONS_KEY, [
|
|
56
54
|
context.getHandler(),
|
|
57
55
|
context.getClass()
|
|
58
56
|
]);
|
|
59
|
-
|
|
60
|
-
if (!permissionConfig) {
|
|
61
|
-
return true;
|
|
62
|
-
}
|
|
63
|
-
// Normalize permission config (support old format: string[])
|
|
57
|
+
if (!permissionConfig) return true;
|
|
64
58
|
const { permissions: requiredPermissions, operator } = this.normalizePermissionConfig(permissionConfig);
|
|
65
|
-
|
|
66
|
-
if (!requiredPermissions || requiredPermissions.length === 0) {
|
|
67
|
-
return true;
|
|
68
|
-
}
|
|
59
|
+
if (!requiredPermissions || requiredPermissions.length === 0) return true;
|
|
69
60
|
const request = context.switchToHttp().getRequest();
|
|
70
61
|
const user = request.user;
|
|
71
|
-
|
|
72
|
-
if (!user) {
|
|
73
|
-
throw new _common.UnauthorizedException('Authentication required');
|
|
74
|
-
}
|
|
75
|
-
// Cache is required for permission checks - fail securely if unavailable
|
|
62
|
+
if (!user) throw new _common.UnauthorizedException('Authentication required');
|
|
76
63
|
if (!this.cache) {
|
|
77
|
-
|
|
78
|
-
this.logger.error(`Cache not available - permission system unavailable (userId: ${user.id})`, undefined, 'PermissionGuard');
|
|
79
|
-
// Fail securely - deny access rather than allowing without permission check
|
|
64
|
+
this.logger.error(`Cache not available (userId: ${user.id})`, undefined, 'PermissionGuard');
|
|
80
65
|
throw new _permissionexception.PermissionSystemUnavailableException();
|
|
81
66
|
}
|
|
82
|
-
// Get user's permissions from cache
|
|
83
67
|
const userPermissions = await this.getUserPermissions(user);
|
|
84
|
-
// If no permissions found in cache, deny access
|
|
85
68
|
if (!userPermissions || userPermissions.length === 0) {
|
|
86
|
-
this.logger.warn(`No permissions found
|
|
69
|
+
this.logger.warn(`No permissions found (userId: ${user.id})`, 'PermissionGuard');
|
|
87
70
|
throw new _permissionexception.NoPermissionsFoundException();
|
|
88
71
|
}
|
|
89
|
-
// Check if this is a nested condition or simple permission list
|
|
90
72
|
if (this.isNestedCondition(permissionConfig)) {
|
|
91
|
-
// Complex nested permission check
|
|
92
73
|
const result = this.evaluateCondition(permissionConfig, userPermissions);
|
|
93
74
|
if (!result.passed) {
|
|
94
|
-
this.logger.warn(`Permission
|
|
75
|
+
this.logger.warn(`Permission denied (userId: ${user.id})`, 'PermissionGuard');
|
|
95
76
|
throw new _permissionexception.InsufficientPermissionsException(result.missingPermissions, result.operator);
|
|
96
77
|
}
|
|
97
78
|
} else {
|
|
98
|
-
|
|
99
|
-
let hasRequiredPermissions;
|
|
100
|
-
if (operator === 'or') {
|
|
101
|
-
// OR: User must have at least ONE permission
|
|
102
|
-
hasRequiredPermissions = requiredPermissions.some((permission)=>this.hasPermission(userPermissions, permission));
|
|
103
|
-
if (!hasRequiredPermissions) {
|
|
104
|
-
throw new _permissionexception.InsufficientPermissionsException(requiredPermissions, 'or');
|
|
105
|
-
}
|
|
106
|
-
} else {
|
|
107
|
-
// AND (default): User must have ALL permissions
|
|
108
|
-
hasRequiredPermissions = requiredPermissions.every((permission)=>this.hasPermission(userPermissions, permission));
|
|
109
|
-
if (!hasRequiredPermissions) {
|
|
110
|
-
const missing = requiredPermissions.filter((permission)=>!this.hasPermission(userPermissions, permission));
|
|
111
|
-
throw new _permissionexception.InsufficientPermissionsException(missing, 'and');
|
|
112
|
-
}
|
|
113
|
-
}
|
|
79
|
+
this.validateSimplePermissions(requiredPermissions, userPermissions, operator);
|
|
114
80
|
}
|
|
115
81
|
return true;
|
|
116
82
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
return {
|
|
123
|
-
permissions: config,
|
|
124
|
-
operator: 'and'
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
// New format: PermissionConfig
|
|
83
|
+
normalizePermissionConfig(config) {
|
|
84
|
+
if (Array.isArray(config)) return {
|
|
85
|
+
permissions: config,
|
|
86
|
+
operator: 'and'
|
|
87
|
+
};
|
|
128
88
|
return {
|
|
129
89
|
permissions: config.permissions || [],
|
|
130
90
|
operator: config.operator || 'and'
|
|
131
91
|
};
|
|
132
92
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
93
|
+
validateSimplePermissions(requiredPermissions, userPermissions, operator) {
|
|
94
|
+
if (operator === 'or') {
|
|
95
|
+
const hasAny = requiredPermissions.some((p)=>this.hasPermission(userPermissions, p));
|
|
96
|
+
if (!hasAny) throw new _permissionexception.InsufficientPermissionsException(requiredPermissions, 'or');
|
|
97
|
+
} else {
|
|
98
|
+
const hasAll = requiredPermissions.every((p)=>this.hasPermission(userPermissions, p));
|
|
99
|
+
if (!hasAll) {
|
|
100
|
+
const missing = requiredPermissions.filter((p)=>!this.hasPermission(userPermissions, p));
|
|
101
|
+
throw new _permissionexception.InsufficientPermissionsException(missing, 'and');
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
isNestedCondition(config) {
|
|
136
106
|
if (Array.isArray(config)) return false;
|
|
137
107
|
return 'children' in config && Array.isArray(config.children) && config.children.length > 0;
|
|
138
108
|
}
|
|
139
|
-
|
|
140
|
-
* Evaluate a nested permission condition recursively
|
|
141
|
-
*/ evaluateCondition(condition, userPermissions) {
|
|
109
|
+
evaluateCondition(condition, userPermissions) {
|
|
142
110
|
const { permissions = [], operator, children = [] } = condition;
|
|
143
|
-
//
|
|
111
|
+
// SECURITY: Fail-closed - deny access when no permissions configured (empty condition)
|
|
112
|
+
if (permissions.length === 0 && children.length === 0) {
|
|
113
|
+
return {
|
|
114
|
+
passed: false,
|
|
115
|
+
message: 'No permissions configured - access denied by default',
|
|
116
|
+
missingPermissions: [],
|
|
117
|
+
operator
|
|
118
|
+
};
|
|
119
|
+
}
|
|
144
120
|
const results = [];
|
|
145
121
|
const failureDetails = [];
|
|
146
122
|
const missingPermissions = [];
|
|
147
|
-
// Check permissions at this level
|
|
148
123
|
if (permissions.length > 0) {
|
|
149
124
|
if (operator === 'or') {
|
|
150
125
|
const hasAny = permissions.some((p)=>this.hasPermission(userPermissions, p));
|
|
@@ -163,7 +138,6 @@ let PermissionGuard = class PermissionGuard {
|
|
|
163
138
|
}
|
|
164
139
|
}
|
|
165
140
|
}
|
|
166
|
-
// Evaluate children recursively
|
|
167
141
|
for (const child of children){
|
|
168
142
|
const childResult = this.evaluateCondition(child, userPermissions);
|
|
169
143
|
results.push(childResult.passed);
|
|
@@ -172,14 +146,9 @@ let PermissionGuard = class PermissionGuard {
|
|
|
172
146
|
missingPermissions.push(...childResult.missingPermissions);
|
|
173
147
|
}
|
|
174
148
|
}
|
|
175
|
-
//
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
passed = results.length === 0 || results.some((r)=>r);
|
|
179
|
-
} else {
|
|
180
|
-
passed = results.length === 0 || results.every((r)=>r);
|
|
181
|
-
}
|
|
182
|
-
const message = passed ? 'Permission granted' : `Permission denied: ${failureDetails.join(` ${operator.toUpperCase()} `)}`;
|
|
149
|
+
// Evaluate based on operator - empty results already handled above
|
|
150
|
+
const passed = operator === 'or' ? results.some((r)=>r) : results.every((r)=>r);
|
|
151
|
+
const message = passed ? 'OK' : `Denied: ${failureDetails.join(` ${operator.toUpperCase()} `)}`;
|
|
183
152
|
return {
|
|
184
153
|
passed,
|
|
185
154
|
message,
|
|
@@ -187,48 +156,27 @@ let PermissionGuard = class PermissionGuard {
|
|
|
187
156
|
operator
|
|
188
157
|
};
|
|
189
158
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
let cacheKey;
|
|
159
|
+
async getUserPermissions(user) {
|
|
160
|
+
if (!this.cache) throw new _permissionexception.PermissionSystemUnavailableException();
|
|
161
|
+
const cacheKey = this.buildPermissionCacheKey(user);
|
|
162
|
+
return await this.cache.get(cacheKey) || [];
|
|
163
|
+
}
|
|
164
|
+
buildPermissionCacheKey(user) {
|
|
197
165
|
if (this.config.enableCompanyFeature && user.companyId) {
|
|
198
|
-
|
|
199
|
-
const format = this.config.companyPermissionKeyFormat || `${_constants.PERMISSIONS_CACHE_PREFIX}:company:{companyId}:branch:{branchId}:user:{userId}`;
|
|
200
|
-
cacheKey = format.replace('{userId}', user.id).replace('{companyId}', user.companyId).replace('{branchId}', user.branchId || 'null');
|
|
201
|
-
} else {
|
|
202
|
-
// User-based permissions
|
|
203
|
-
const format = this.config.userPermissionKeyFormat || `${_constants.PERMISSIONS_CACHE_PREFIX}:user:{userId}`;
|
|
204
|
-
cacheKey = format.replace('{userId}', user.id);
|
|
166
|
+
return (this.config.companyPermissionKeyFormat || `${_constants.PERMISSIONS_CACHE_PREFIX}:company:{companyId}:branch:{branchId}:user:{userId}`).replace('{userId}', user.id).replace('{companyId}', user.companyId).replace('{branchId}', user.branchId || 'null');
|
|
205
167
|
}
|
|
206
|
-
|
|
207
|
-
return permissions || [];
|
|
168
|
+
return (this.config.userPermissionKeyFormat || `${_constants.PERMISSIONS_CACHE_PREFIX}:user:{userId}`).replace('{userId}', user.id);
|
|
208
169
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
* Supports wildcard matching (e.g., 'admin.*' matches 'admin.users.read')
|
|
212
|
-
*/ hasPermission(userPermissions, requiredPermission) {
|
|
213
|
-
// Direct match
|
|
214
|
-
if (userPermissions.includes(requiredPermission)) {
|
|
215
|
-
return true;
|
|
216
|
-
}
|
|
217
|
-
// Wildcard match (e.g., '*' or 'admin.*')
|
|
170
|
+
hasPermission(userPermissions, requiredPermission) {
|
|
171
|
+
if (userPermissions.includes(requiredPermission)) return true;
|
|
218
172
|
for (const permission of userPermissions){
|
|
219
|
-
if (permission === '*')
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
if (permission.endsWith('.*')) {
|
|
223
|
-
const prefix = permission.slice(0, -1); // Remove '*'
|
|
224
|
-
if (requiredPermission.startsWith(prefix)) {
|
|
225
|
-
return true;
|
|
226
|
-
}
|
|
173
|
+
if (permission === '*') return true;
|
|
174
|
+
if (permission.endsWith('.*') && requiredPermission.startsWith(permission.slice(0, -1))) {
|
|
175
|
+
return true;
|
|
227
176
|
}
|
|
228
177
|
}
|
|
229
178
|
return false;
|
|
230
179
|
}
|
|
231
|
-
// NOTE: @Inject(Reflector) required for bundled code - external classes need explicit injection
|
|
232
180
|
constructor(reflector, cache, config, logger){
|
|
233
181
|
_define_property(this, "reflector", void 0);
|
|
234
182
|
_define_property(this, "cache", void 0);
|
|
@@ -238,12 +186,10 @@ let PermissionGuard = class PermissionGuard {
|
|
|
238
186
|
this.cache = cache;
|
|
239
187
|
this.config = {
|
|
240
188
|
enableCompanyFeature: false,
|
|
241
|
-
cacheKeyPrefix: _constants.PERMISSIONS_CACHE_PREFIX,
|
|
242
189
|
userPermissionKeyFormat: `${_constants.PERMISSIONS_CACHE_PREFIX}:user:{userId}`,
|
|
243
190
|
companyPermissionKeyFormat: `${_constants.PERMISSIONS_CACHE_PREFIX}:company:{companyId}:branch:{branchId}:user:{userId}`,
|
|
244
191
|
...config
|
|
245
192
|
};
|
|
246
|
-
// Use provided logger or fallback to NestJS Logger wrapped in adapter
|
|
247
193
|
this.logger = logger || new _winstonloggeradapterclass.NestLoggerAdapter(new _common.Logger(PermissionGuard.name));
|
|
248
194
|
}
|
|
249
195
|
};
|
|
@@ -6,9 +6,7 @@ _export_star(require("./delete-empty-id-from-body.interceptor"), exports);
|
|
|
6
6
|
_export_star(require("./idempotency.interceptor"), exports);
|
|
7
7
|
_export_star(require("./query-performance.interceptor"), exports);
|
|
8
8
|
_export_star(require("./response-meta.interceptor"), exports);
|
|
9
|
-
_export_star(require("./set-
|
|
10
|
-
_export_star(require("./set-delete-by-on-body.interceptor"), exports);
|
|
11
|
-
_export_star(require("./set-update-by-on-body.interceptor"), exports);
|
|
9
|
+
_export_star(require("./set-user-field-on-body.interceptor"), exports);
|
|
12
10
|
_export_star(require("./slug.interceptor"), exports);
|
|
13
11
|
function _export_star(from, to) {
|
|
14
12
|
Object.keys(from).forEach(function(k) {
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
function _export(target, all) {
|
|
6
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
_export(exports, {
|
|
12
|
+
get SetCreatedByOnBody () {
|
|
13
|
+
return SetCreatedByOnBody;
|
|
14
|
+
},
|
|
15
|
+
get SetDeletedByOnBody () {
|
|
16
|
+
return SetDeletedByOnBody;
|
|
17
|
+
},
|
|
18
|
+
get SetUpdateByOnBody () {
|
|
19
|
+
return SetUpdateByOnBody;
|
|
20
|
+
},
|
|
21
|
+
get createSetUserFieldInterceptor () {
|
|
22
|
+
return createSetUserFieldInterceptor;
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
const _common = require("@nestjs/common");
|
|
26
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
27
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
28
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
29
|
+
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;
|
|
30
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
31
|
+
}
|
|
32
|
+
function createSetUserFieldInterceptor(fieldName) {
|
|
33
|
+
let SetUserFieldOnBody = class SetUserFieldOnBody {
|
|
34
|
+
intercept(context, next) {
|
|
35
|
+
const request = context.switchToHttp().getRequest();
|
|
36
|
+
const user = request?.user;
|
|
37
|
+
if (user) {
|
|
38
|
+
if (Array.isArray(request.body)) {
|
|
39
|
+
request.body = request.body.map((item)=>({
|
|
40
|
+
...item,
|
|
41
|
+
[fieldName]: user.id
|
|
42
|
+
}));
|
|
43
|
+
} else if (typeof request.body === 'object' && request.body !== null) {
|
|
44
|
+
request.body = {
|
|
45
|
+
...request.body,
|
|
46
|
+
[fieldName]: user.id
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return next.handle();
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
SetUserFieldOnBody = _ts_decorate([
|
|
54
|
+
(0, _common.Injectable)()
|
|
55
|
+
], SetUserFieldOnBody);
|
|
56
|
+
return SetUserFieldOnBody;
|
|
57
|
+
}
|
|
58
|
+
const SetCreatedByOnBody = createSetUserFieldInterceptor('createdById');
|
|
59
|
+
const SetUpdateByOnBody = createSetUserFieldInterceptor('updatedById');
|
|
60
|
+
const SetDeletedByOnBody = createSetUserFieldInterceptor('deletedById');
|