@flusys/nestjs-shared 1.0.0-beta → 1.0.0-rc

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.
Files changed (90) hide show
  1. package/README.md +56 -110
  2. package/cjs/classes/api-controller.class.js +9 -24
  3. package/cjs/classes/index.js +1 -0
  4. package/cjs/constants/index.js +14 -0
  5. package/cjs/constants/permissions.js +174 -0
  6. package/cjs/decorators/api-response.decorator.js +1 -1
  7. package/cjs/decorators/index.js +1 -0
  8. package/cjs/decorators/sanitize-html.decorator.js +36 -0
  9. package/cjs/dtos/filter-and-pagination.dto.js +24 -34
  10. package/cjs/dtos/pagination.dto.js +4 -8
  11. package/cjs/dtos/response-payload.dto.js +0 -41
  12. package/cjs/entities/identity.js +4 -4
  13. package/cjs/entities/user-root.js +13 -14
  14. package/cjs/guards/permission.guard.js +39 -94
  15. package/cjs/interceptors/index.js +1 -0
  16. package/cjs/interceptors/set-create-by-on-body.interceptor.js +2 -30
  17. package/cjs/interceptors/set-delete-by-on-body.interceptor.js +2 -30
  18. package/cjs/interceptors/set-update-by-on-body.interceptor.js +2 -30
  19. package/cjs/interceptors/set-user-field-on-body.interceptor.js +43 -0
  20. package/cjs/interceptors/slug.interceptor.js +30 -9
  21. package/cjs/interfaces/datasource.interface.js +4 -0
  22. package/cjs/interfaces/index.js +2 -1
  23. package/cjs/interfaces/module-config.interface.js +4 -0
  24. package/cjs/modules/cache/cache.module.js +3 -3
  25. package/cjs/modules/datasource/multi-tenant-datasource.service.js +30 -110
  26. package/cjs/modules/utils/utils.service.js +63 -145
  27. package/cjs/utils/error-handler.util.js +91 -13
  28. package/cjs/utils/html-sanitizer.util.js +74 -0
  29. package/cjs/utils/index.js +2 -0
  30. package/cjs/utils/query-helpers.util.js +53 -0
  31. package/classes/api-controller.class.d.ts +5 -5
  32. package/classes/api-service.class.d.ts +5 -5
  33. package/classes/index.d.ts +1 -0
  34. package/classes/request-scoped-api.service.d.ts +3 -2
  35. package/constants/index.d.ts +1 -0
  36. package/constants/permissions.d.ts +167 -0
  37. package/decorators/index.d.ts +1 -0
  38. package/decorators/sanitize-html.decorator.d.ts +2 -0
  39. package/dtos/filter-and-pagination.dto.d.ts +0 -2
  40. package/dtos/response-payload.dto.d.ts +0 -7
  41. package/fesm/classes/api-controller.class.js +9 -24
  42. package/fesm/classes/index.js +2 -0
  43. package/fesm/constants/index.js +2 -0
  44. package/fesm/constants/permissions.js +121 -0
  45. package/fesm/decorators/api-response.decorator.js +1 -1
  46. package/fesm/decorators/index.js +1 -0
  47. package/fesm/decorators/sanitize-html.decorator.js +45 -0
  48. package/fesm/dtos/filter-and-pagination.dto.js +26 -47
  49. package/fesm/dtos/pagination.dto.js +4 -8
  50. package/fesm/dtos/response-payload.dto.js +0 -38
  51. package/fesm/entities/identity.js +4 -4
  52. package/fesm/entities/user-root.js +13 -14
  53. package/fesm/guards/permission.guard.js +39 -94
  54. package/fesm/interceptors/index.js +1 -0
  55. package/fesm/interceptors/set-create-by-on-body.interceptor.js +4 -30
  56. package/fesm/interceptors/set-delete-by-on-body.interceptor.js +4 -30
  57. package/fesm/interceptors/set-update-by-on-body.interceptor.js +4 -30
  58. package/fesm/interceptors/set-user-field-on-body.interceptor.js +36 -0
  59. package/fesm/interceptors/slug.interceptor.js +31 -10
  60. package/fesm/interfaces/datasource.interface.js +20 -0
  61. package/fesm/interfaces/index.js +2 -1
  62. package/fesm/interfaces/module-config.interface.js +5 -0
  63. package/fesm/modules/cache/cache.module.js +2 -2
  64. package/fesm/modules/datasource/multi-tenant-datasource.service.js +30 -110
  65. package/fesm/modules/utils/utils.service.js +50 -143
  66. package/fesm/utils/error-handler.util.js +93 -14
  67. package/fesm/utils/html-sanitizer.util.js +82 -0
  68. package/fesm/utils/index.js +2 -0
  69. package/fesm/utils/query-helpers.util.js +78 -0
  70. package/interceptors/index.d.ts +1 -0
  71. package/interceptors/set-create-by-on-body.interceptor.d.ts +1 -5
  72. package/interceptors/set-delete-by-on-body.interceptor.d.ts +1 -5
  73. package/interceptors/set-update-by-on-body.interceptor.d.ts +1 -5
  74. package/interceptors/set-user-field-on-body.interceptor.d.ts +2 -0
  75. package/interceptors/slug.interceptor.d.ts +2 -1
  76. package/interfaces/api.interface.d.ts +2 -2
  77. package/interfaces/datasource.interface.d.ts +5 -0
  78. package/interfaces/identity.interface.d.ts +4 -4
  79. package/interfaces/index.d.ts +2 -1
  80. package/interfaces/module-config.interface.d.ts +6 -0
  81. package/interfaces/permission.interface.d.ts +0 -1
  82. package/modules/utils/utils.service.d.ts +10 -4
  83. package/package.json +4 -4
  84. package/utils/error-handler.util.d.ts +23 -13
  85. package/utils/html-sanitizer.util.d.ts +3 -0
  86. package/utils/index.d.ts +2 -0
  87. package/utils/query-helpers.util.d.ts +16 -0
  88. package/cjs/interfaces/base-query.interface.js +0 -6
  89. package/fesm/interfaces/base-query.interface.js +0 -3
  90. 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
- _ts_metadata("design:type", Array)
159
- ], GetByIdBodyDto.prototype, "select", void 0);
160
- _ts_decorate([
161
- (0, _swagger.ApiPropertyOptional)({
162
- type: [
163
- String
164
- ],
165
- description: 'Additional relation keys to include',
166
- example: [
167
- 'category',
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, "extraKey", void 0);
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
- * Number of items per page. Defaults to 10 when not provided or invalid.
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. Defaults to 10 when not provided or invalid.',
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. Defaults to 0 when not provided or invalid.',
57
+ description: 'Zero-based page index (default: 0)',
62
58
  example: 0
63
59
  }),
64
60
  (0, _classvalidator.IsOptional)(),
@@ -30,9 +30,6 @@ _export(exports, {
30
30
  get RequestMetaDto () {
31
31
  return RequestMetaDto;
32
32
  },
33
- get ResponsePayloadDto () {
34
- return ResponsePayloadDto;
35
- },
36
33
  get SingleResponseDto () {
37
34
  return SingleResponseDto;
38
35
  },
@@ -373,41 +370,3 @@ _ts_decorate([
373
370
  ErrorResponseDto = _ts_decorate([
374
371
  (0, _swagger.ApiExtraModels)()
375
372
  ], 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);
@@ -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", void 0);
40
- _define_property(this, "createdById", void 0);
41
- _define_property(this, "updatedById", void 0);
42
- _define_property(this, "deletedById", void 0);
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", void 0);
38
- _define_property(this, "password", void 0);
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", void 0);
41
- _define_property(this, "isActive", void 0);
42
- _define_property(this, "emailVerified", void 0);
43
- _define_property(this, "phoneVerified", void 0);
44
- _define_property(this, "profilePictureId", void 0);
45
- _define_property(this, "lastLoginAt", void 0);
46
- _define_property(this, "additionalFields", void 0);
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", void 0);
50
- // Audit columns (for ApiService compatibility)
51
- _define_property(this, "createdById", void 0);
52
- _define_property(this, "updatedById", void 0);
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,79 @@ 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
- // If no permissions required, allow access
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
- // If no permissions required, allow access
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
- // User must be authenticated
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
- // Log error (in production, this should be monitored)
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 for user (userId: ${user.id})`, 'PermissionGuard');
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 check failed (userId: ${user.id}, missing: ${result.missingPermissions.join(', ')})`, 'PermissionGuard');
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
- // Simple permission check (backward compatible)
99
- let hasRequiredPermissions;
79
+ let hasRequired;
100
80
  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
- }
81
+ hasRequired = requiredPermissions.some((p)=>this.hasPermission(userPermissions, p));
82
+ if (!hasRequired) throw new _permissionexception.InsufficientPermissionsException(requiredPermissions, 'or');
106
83
  } 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));
84
+ hasRequired = requiredPermissions.every((p)=>this.hasPermission(userPermissions, p));
85
+ if (!hasRequired) {
86
+ const missing = requiredPermissions.filter((p)=>!this.hasPermission(userPermissions, p));
111
87
  throw new _permissionexception.InsufficientPermissionsException(missing, 'and');
112
88
  }
113
89
  }
114
90
  }
115
91
  return true;
116
92
  }
117
- /**
118
- * Normalize permission config to handle both old and new formats
119
- */ normalizePermissionConfig(config) {
120
- // Old format: string[]
121
- if (Array.isArray(config)) {
122
- return {
123
- permissions: config,
124
- operator: 'and'
125
- };
126
- }
127
- // New format: PermissionConfig
93
+ normalizePermissionConfig(config) {
94
+ if (Array.isArray(config)) return {
95
+ permissions: config,
96
+ operator: 'and'
97
+ };
128
98
  return {
129
99
  permissions: config.permissions || [],
130
100
  operator: config.operator || 'and'
131
101
  };
132
102
  }
133
- /**
134
- * Check if config is a nested condition (has children)
135
- */ isNestedCondition(config) {
103
+ isNestedCondition(config) {
136
104
  if (Array.isArray(config)) return false;
137
105
  return 'children' in config && Array.isArray(config.children) && config.children.length > 0;
138
106
  }
139
- /**
140
- * Evaluate a nested permission condition recursively
141
- */ evaluateCondition(condition, userPermissions) {
107
+ evaluateCondition(condition, userPermissions) {
142
108
  const { permissions = [], operator, children = [] } = condition;
143
- // Results for this level
109
+ // SECURITY: Fail-closed - deny access when no permissions configured (empty condition)
110
+ if (permissions.length === 0 && children.length === 0) {
111
+ return {
112
+ passed: false,
113
+ message: 'No permissions configured - access denied by default',
114
+ missingPermissions: [],
115
+ operator
116
+ };
117
+ }
144
118
  const results = [];
145
119
  const failureDetails = [];
146
120
  const missingPermissions = [];
147
- // Check permissions at this level
148
121
  if (permissions.length > 0) {
149
122
  if (operator === 'or') {
150
123
  const hasAny = permissions.some((p)=>this.hasPermission(userPermissions, p));
@@ -163,7 +136,6 @@ let PermissionGuard = class PermissionGuard {
163
136
  }
164
137
  }
165
138
  }
166
- // Evaluate children recursively
167
139
  for (const child of children){
168
140
  const childResult = this.evaluateCondition(child, userPermissions);
169
141
  results.push(childResult.passed);
@@ -172,14 +144,9 @@ let PermissionGuard = class PermissionGuard {
172
144
  missingPermissions.push(...childResult.missingPermissions);
173
145
  }
174
146
  }
175
- // Combine results based on operator
176
- let passed;
177
- if (operator === 'or') {
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()} `)}`;
147
+ // Evaluate based on operator - empty results already handled above
148
+ const passed = operator === 'or' ? results.some((r)=>r) : results.every((r)=>r);
149
+ const message = passed ? 'OK' : `Denied: ${failureDetails.join(` ${operator.toUpperCase()} `)}`;
183
150
  return {
184
151
  passed,
185
152
  message,
@@ -187,48 +154,28 @@ let PermissionGuard = class PermissionGuard {
187
154
  operator
188
155
  };
189
156
  }
190
- /**
191
- * Get user's permissions from cache
192
- */ async getUserPermissions(user) {
193
- if (!this.cache) {
194
- throw new _permissionexception.PermissionSystemUnavailableException();
195
- }
157
+ async getUserPermissions(user) {
158
+ if (!this.cache) throw new _permissionexception.PermissionSystemUnavailableException();
196
159
  let cacheKey;
197
160
  if (this.config.enableCompanyFeature && user.companyId) {
198
- // Company-based permissions (includes branchId for branch-scoped DIRECT permissions)
199
161
  const format = this.config.companyPermissionKeyFormat || `${_constants.PERMISSIONS_CACHE_PREFIX}:company:{companyId}:branch:{branchId}:user:{userId}`;
200
162
  cacheKey = format.replace('{userId}', user.id).replace('{companyId}', user.companyId).replace('{branchId}', user.branchId || 'null');
201
163
  } else {
202
- // User-based permissions
203
164
  const format = this.config.userPermissionKeyFormat || `${_constants.PERMISSIONS_CACHE_PREFIX}:user:{userId}`;
204
165
  cacheKey = format.replace('{userId}', user.id);
205
166
  }
206
- const permissions = await this.cache.get(cacheKey);
207
- return permissions || [];
167
+ return await this.cache.get(cacheKey) || [];
208
168
  }
209
- /**
210
- * Check if user has a specific permission
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.*')
169
+ hasPermission(userPermissions, requiredPermission) {
170
+ if (userPermissions.includes(requiredPermission)) return true;
218
171
  for (const permission of userPermissions){
219
- if (permission === '*') {
220
- return true; // Super admin
221
- }
222
- if (permission.endsWith('.*')) {
223
- const prefix = permission.slice(0, -1); // Remove '*'
224
- if (requiredPermission.startsWith(prefix)) {
225
- return true;
226
- }
172
+ if (permission === '*') return true;
173
+ if (permission.endsWith('.*') && requiredPermission.startsWith(permission.slice(0, -1))) {
174
+ return true;
227
175
  }
228
176
  }
229
177
  return false;
230
178
  }
231
- // NOTE: @Inject(Reflector) required for bundled code - external classes need explicit injection
232
179
  constructor(reflector, cache, config, logger){
233
180
  _define_property(this, "reflector", void 0);
234
181
  _define_property(this, "cache", void 0);
@@ -238,12 +185,10 @@ let PermissionGuard = class PermissionGuard {
238
185
  this.cache = cache;
239
186
  this.config = {
240
187
  enableCompanyFeature: false,
241
- cacheKeyPrefix: _constants.PERMISSIONS_CACHE_PREFIX,
242
188
  userPermissionKeyFormat: `${_constants.PERMISSIONS_CACHE_PREFIX}:user:{userId}`,
243
189
  companyPermissionKeyFormat: `${_constants.PERMISSIONS_CACHE_PREFIX}:company:{companyId}:branch:{branchId}:user:{userId}`,
244
190
  ...config
245
191
  };
246
- // Use provided logger or fallback to NestJS Logger wrapped in adapter
247
192
  this.logger = logger || new _winstonloggeradapterclass.NestLoggerAdapter(new _common.Logger(PermissionGuard.name));
248
193
  }
249
194
  };
@@ -6,6 +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-user-field-on-body.interceptor"), exports);
9
10
  _export_star(require("./set-create-by-on-body.interceptor"), exports);
10
11
  _export_star(require("./set-delete-by-on-body.interceptor"), exports);
11
12
  _export_star(require("./set-update-by-on-body.interceptor"), exports);
@@ -8,33 +8,5 @@ Object.defineProperty(exports, "SetCreatedByOnBody", {
8
8
  return SetCreatedByOnBody;
9
9
  }
10
10
  });
11
- const _common = require("@nestjs/common");
12
- function _ts_decorate(decorators, target, key, desc) {
13
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
14
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
15
- 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;
16
- return c > 3 && r && Object.defineProperty(target, key, r), r;
17
- }
18
- let SetCreatedByOnBody = class SetCreatedByOnBody {
19
- intercept(context, next) {
20
- const request = context.switchToHttp().getRequest();
21
- const user = request?.user;
22
- if (user) {
23
- if (Array.isArray(request.body)) {
24
- request.body = request.body.map((item)=>({
25
- ...item,
26
- createdById: user.id
27
- }));
28
- } else if (typeof request.body === 'object' && request.body !== null) {
29
- request.body = {
30
- ...request.body,
31
- createdById: user.id
32
- };
33
- }
34
- }
35
- return next.handle();
36
- }
37
- };
38
- SetCreatedByOnBody = _ts_decorate([
39
- (0, _common.Injectable)()
40
- ], SetCreatedByOnBody);
11
+ const _setuserfieldonbodyinterceptor = require("./set-user-field-on-body.interceptor");
12
+ const SetCreatedByOnBody = (0, _setuserfieldonbodyinterceptor.createSetUserFieldInterceptor)('createdById');
@@ -8,33 +8,5 @@ Object.defineProperty(exports, "SetDeletedByOnBody", {
8
8
  return SetDeletedByOnBody;
9
9
  }
10
10
  });
11
- const _common = require("@nestjs/common");
12
- function _ts_decorate(decorators, target, key, desc) {
13
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
14
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
15
- 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;
16
- return c > 3 && r && Object.defineProperty(target, key, r), r;
17
- }
18
- let SetDeletedByOnBody = class SetDeletedByOnBody {
19
- intercept(context, next) {
20
- const request = context.switchToHttp().getRequest();
21
- const user = request?.user;
22
- if (user) {
23
- if (Array.isArray(request.body)) {
24
- request.body = request.body.map((item)=>({
25
- ...item,
26
- deletedById: user.id
27
- }));
28
- } else if (typeof request.body === 'object' && request.body !== null) {
29
- request.body = {
30
- ...request.body,
31
- deletedById: user.id
32
- };
33
- }
34
- }
35
- return next.handle();
36
- }
37
- };
38
- SetDeletedByOnBody = _ts_decorate([
39
- (0, _common.Injectable)()
40
- ], SetDeletedByOnBody);
11
+ const _setuserfieldonbodyinterceptor = require("./set-user-field-on-body.interceptor");
12
+ const SetDeletedByOnBody = (0, _setuserfieldonbodyinterceptor.createSetUserFieldInterceptor)('deletedById');
@@ -8,33 +8,5 @@ Object.defineProperty(exports, "SetUpdateByOnBody", {
8
8
  return SetUpdateByOnBody;
9
9
  }
10
10
  });
11
- const _common = require("@nestjs/common");
12
- function _ts_decorate(decorators, target, key, desc) {
13
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
14
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
15
- 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;
16
- return c > 3 && r && Object.defineProperty(target, key, r), r;
17
- }
18
- let SetUpdateByOnBody = class SetUpdateByOnBody {
19
- intercept(context, next) {
20
- const request = context.switchToHttp().getRequest();
21
- const user = request?.user;
22
- if (user) {
23
- if (Array.isArray(request.body)) {
24
- request.body = request.body.map((item)=>({
25
- ...item,
26
- updatedById: user.id
27
- }));
28
- } else if (typeof request.body === 'object' && request.body !== null) {
29
- request.body = {
30
- ...request.body,
31
- updatedById: user.id
32
- };
33
- }
34
- }
35
- return next.handle();
36
- }
37
- };
38
- SetUpdateByOnBody = _ts_decorate([
39
- (0, _common.Injectable)()
40
- ], SetUpdateByOnBody);
11
+ const _setuserfieldonbodyinterceptor = require("./set-user-field-on-body.interceptor");
12
+ const SetUpdateByOnBody = (0, _setuserfieldonbodyinterceptor.createSetUserFieldInterceptor)('updatedById');
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "createSetUserFieldInterceptor", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return createSetUserFieldInterceptor;
9
+ }
10
+ });
11
+ const _common = require("@nestjs/common");
12
+ function _ts_decorate(decorators, target, key, desc) {
13
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
14
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
15
+ 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;
16
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
17
+ }
18
+ function createSetUserFieldInterceptor(fieldName) {
19
+ let SetUserFieldOnBody = class SetUserFieldOnBody {
20
+ intercept(context, next) {
21
+ const request = context.switchToHttp().getRequest();
22
+ const user = request?.user;
23
+ if (user) {
24
+ if (Array.isArray(request.body)) {
25
+ request.body = request.body.map((item)=>({
26
+ ...item,
27
+ [fieldName]: user.id
28
+ }));
29
+ } else if (typeof request.body === 'object' && request.body !== null) {
30
+ request.body = {
31
+ ...request.body,
32
+ [fieldName]: user.id
33
+ };
34
+ }
35
+ }
36
+ return next.handle();
37
+ }
38
+ };
39
+ SetUserFieldOnBody = _ts_decorate([
40
+ (0, _common.Injectable)()
41
+ ], SetUserFieldOnBody);
42
+ return SetUserFieldOnBody;
43
+ }