@flusys/nestjs-shared 1.1.0-beta → 1.2.0-rc.1

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 (198) hide show
  1. package/README.md +146 -1330
  2. package/cjs/classes/api-controller.class.js +325 -49
  3. package/cjs/classes/api-service.class.js +271 -149
  4. package/cjs/classes/handle-custome-db.js +37 -0
  5. package/cjs/classes/index.js +2 -2
  6. package/cjs/classes/winston.logger.class.js +103 -70
  7. package/cjs/constants/index.js +15 -4
  8. package/cjs/constants/message-keys.js +68 -0
  9. package/cjs/constants/permissions.js +287 -0
  10. package/cjs/decorators/api-response.decorator.js +2 -3
  11. package/cjs/decorators/index.js +1 -0
  12. package/cjs/decorators/log-action.decorator.js +149 -0
  13. package/cjs/decorators/require-permission.decorator.js +5 -5
  14. package/cjs/dtos/delete.dto.js +10 -0
  15. package/cjs/dtos/filter-and-pagination.dto.js +24 -34
  16. package/cjs/dtos/get-by-ids.dto.js +65 -0
  17. package/cjs/dtos/identity-response.dto.js +4 -8
  18. package/cjs/dtos/index.js +1 -0
  19. package/cjs/dtos/pagination.dto.js +4 -8
  20. package/cjs/dtos/response-payload.dto.js +68 -112
  21. package/cjs/entities/identity.js +12 -6
  22. package/cjs/entities/index.js +1 -0
  23. package/cjs/entities/raw-type.js +4 -0
  24. package/cjs/entities/user-root.js +15 -15
  25. package/cjs/enums/index.js +20 -0
  26. package/cjs/enums/notification-type.enum.js +17 -0
  27. package/cjs/enums/participant-status.enum.js +17 -0
  28. package/cjs/enums/recurrence-type.enum.js +18 -0
  29. package/cjs/exceptions/base-app.exception.js +151 -0
  30. package/cjs/exceptions/index.js +1 -0
  31. package/cjs/exceptions/permission.exception.js +13 -9
  32. package/cjs/filters/global-exception.filter.js +279 -0
  33. package/cjs/filters/index.js +18 -0
  34. package/cjs/guards/jwt-auth.guard.js +4 -1
  35. package/cjs/guards/permission.guard.js +88 -148
  36. package/cjs/index.js +2 -0
  37. package/cjs/interceptors/idempotency.interceptor.js +1 -1
  38. package/cjs/interceptors/index.js +1 -4
  39. package/cjs/interceptors/set-user-field-on-body.interceptor.js +60 -0
  40. package/cjs/interceptors/slug.interceptor.js +30 -9
  41. package/cjs/interfaces/datasource.interface.js +4 -0
  42. package/cjs/interfaces/event-manager-adapter.interface.js +11 -0
  43. package/cjs/interfaces/index.js +4 -2
  44. package/cjs/interfaces/module-config.interface.js +4 -0
  45. package/cjs/interfaces/notification-adapter.interface.js +11 -0
  46. package/cjs/interfaces/permission.interface.js +1 -1
  47. package/cjs/middlewares/logger.middleware.js +133 -115
  48. package/cjs/modules/cache/cache.module.js +3 -3
  49. package/cjs/modules/datasource/index.js +0 -1
  50. package/cjs/modules/datasource/multi-tenant-datasource.service.js +60 -183
  51. package/cjs/modules/utils/utils.service.js +27 -206
  52. package/cjs/utils/date-time.util.js +94 -0
  53. package/cjs/utils/html-sanitizer.util.js +64 -0
  54. package/cjs/utils/index.js +5 -1
  55. package/cjs/utils/query-helpers.util.js +60 -0
  56. package/cjs/utils/request.util.js +96 -0
  57. package/cjs/utils/string.util.js +63 -0
  58. package/classes/api-controller.class.d.ts +16 -8
  59. package/classes/api-service.class.d.ts +30 -25
  60. package/classes/handle-custome-db.d.ts +3 -0
  61. package/classes/index.d.ts +2 -2
  62. package/classes/winston.logger.class.d.ts +1 -0
  63. package/constants/index.d.ts +2 -1
  64. package/constants/message-keys.d.ts +45 -0
  65. package/constants/permissions.d.ts +306 -0
  66. package/decorators/api-response.decorator.d.ts +1 -1
  67. package/decorators/current-user.decorator.d.ts +1 -1
  68. package/decorators/index.d.ts +1 -0
  69. package/decorators/log-action.decorator.d.ts +8 -0
  70. package/decorators/require-permission.decorator.d.ts +2 -2
  71. package/dtos/delete.dto.d.ts +1 -0
  72. package/dtos/filter-and-pagination.dto.d.ts +0 -2
  73. package/dtos/get-by-ids.dto.d.ts +4 -0
  74. package/dtos/index.d.ts +1 -0
  75. package/dtos/response-payload.dto.d.ts +8 -20
  76. package/entities/index.d.ts +1 -0
  77. package/entities/raw-type.d.ts +1 -0
  78. package/enums/index.d.ts +3 -0
  79. package/enums/notification-type.enum.d.ts +6 -0
  80. package/enums/participant-status.enum.d.ts +6 -0
  81. package/enums/recurrence-type.enum.d.ts +7 -0
  82. package/exceptions/base-app.exception.d.ts +41 -0
  83. package/exceptions/index.d.ts +1 -0
  84. package/exceptions/permission.exception.d.ts +2 -2
  85. package/fesm/classes/api-controller.class.js +326 -50
  86. package/fesm/classes/api-service.class.js +274 -152
  87. package/fesm/classes/handle-custome-db.js +19 -0
  88. package/fesm/classes/index.js +3 -2
  89. package/fesm/classes/winston.logger.class.js +100 -68
  90. package/fesm/constants/index.js +4 -1
  91. package/fesm/constants/message-keys.js +49 -0
  92. package/fesm/constants/permissions.js +200 -0
  93. package/fesm/decorators/api-response.decorator.js +3 -21
  94. package/fesm/decorators/index.js +1 -0
  95. package/fesm/decorators/log-action.decorator.js +139 -0
  96. package/fesm/decorators/require-permission.decorator.js +17 -65
  97. package/fesm/dtos/delete.dto.js +12 -2
  98. package/fesm/dtos/filter-and-pagination.dto.js +26 -47
  99. package/fesm/dtos/get-by-ids.dto.js +55 -0
  100. package/fesm/dtos/identity-response.dto.js +4 -8
  101. package/fesm/dtos/index.js +1 -0
  102. package/fesm/dtos/pagination.dto.js +4 -8
  103. package/fesm/dtos/response-payload.dto.js +68 -103
  104. package/fesm/entities/identity.js +12 -6
  105. package/fesm/entities/index.js +1 -0
  106. package/fesm/entities/raw-type.js +1 -0
  107. package/fesm/entities/user-root.js +15 -15
  108. package/fesm/enums/index.js +3 -0
  109. package/fesm/enums/notification-type.enum.js +7 -0
  110. package/fesm/enums/participant-status.enum.js +7 -0
  111. package/fesm/enums/recurrence-type.enum.js +8 -0
  112. package/fesm/exceptions/base-app.exception.js +115 -0
  113. package/fesm/exceptions/index.js +1 -0
  114. package/fesm/exceptions/permission.exception.js +16 -18
  115. package/fesm/filters/global-exception.filter.js +269 -0
  116. package/fesm/filters/index.js +1 -0
  117. package/fesm/guards/jwt-auth.guard.js +5 -2
  118. package/fesm/guards/permission.guard.js +90 -150
  119. package/fesm/index.js +2 -0
  120. package/fesm/interceptors/idempotency.interceptor.js +2 -2
  121. package/fesm/interceptors/index.js +1 -4
  122. package/fesm/interceptors/set-user-field-on-body.interceptor.js +39 -0
  123. package/fesm/interceptors/slug.interceptor.js +31 -10
  124. package/fesm/interfaces/datasource.interface.js +1 -0
  125. package/fesm/interfaces/event-manager-adapter.interface.js +1 -0
  126. package/fesm/interfaces/index.js +4 -2
  127. package/fesm/interfaces/module-config.interface.js +5 -0
  128. package/fesm/interfaces/notification-adapter.interface.js +1 -0
  129. package/fesm/interfaces/permission.interface.js +1 -3
  130. package/fesm/middlewares/logger.middleware.js +133 -109
  131. package/fesm/modules/cache/cache.module.js +2 -2
  132. package/fesm/modules/datasource/index.js +0 -1
  133. package/fesm/modules/datasource/multi-tenant-datasource.service.js +61 -184
  134. package/fesm/modules/utils/utils.service.js +27 -206
  135. package/fesm/utils/date-time.util.js +70 -0
  136. package/fesm/utils/html-sanitizer.util.js +69 -0
  137. package/fesm/utils/index.js +5 -1
  138. package/fesm/utils/query-helpers.util.js +85 -0
  139. package/fesm/utils/request.util.js +81 -0
  140. package/fesm/utils/string.util.js +71 -0
  141. package/filters/global-exception.filter.d.ts +9 -0
  142. package/filters/index.d.ts +1 -0
  143. package/guards/permission.guard.d.ts +4 -6
  144. package/index.d.ts +2 -0
  145. package/interceptors/idempotency.interceptor.d.ts +1 -1
  146. package/interceptors/index.d.ts +1 -4
  147. package/interceptors/set-user-field-on-body.interceptor.d.ts +5 -0
  148. package/interceptors/slug.interceptor.d.ts +2 -1
  149. package/interfaces/api.interface.d.ts +4 -2
  150. package/interfaces/datasource.interface.d.ts +5 -0
  151. package/interfaces/event-manager-adapter.interface.d.ts +43 -0
  152. package/interfaces/identity.interface.d.ts +4 -4
  153. package/interfaces/index.d.ts +4 -2
  154. package/interfaces/logged-user-info.interface.d.ts +0 -2
  155. package/interfaces/module-config.interface.d.ts +6 -0
  156. package/interfaces/notification-adapter.interface.d.ts +21 -0
  157. package/interfaces/permission.interface.d.ts +13 -9
  158. package/middlewares/logger.middleware.d.ts +2 -2
  159. package/modules/datasource/index.d.ts +0 -1
  160. package/modules/datasource/multi-tenant-datasource.service.d.ts +5 -16
  161. package/modules/utils/utils.service.d.ts +3 -14
  162. package/package.json +21 -14
  163. package/utils/date-time.util.d.ts +8 -0
  164. package/utils/html-sanitizer.util.d.ts +2 -0
  165. package/utils/index.d.ts +5 -1
  166. package/utils/query-helpers.util.d.ts +16 -0
  167. package/utils/request.util.d.ts +6 -0
  168. package/utils/string.util.d.ts +2 -0
  169. package/cjs/classes/request-scoped-api.service.js +0 -61
  170. package/cjs/classes/winston-logger-adapter.class.js +0 -121
  171. package/cjs/interceptors/query-performance.interceptor.js +0 -66
  172. package/cjs/interceptors/set-create-by-on-body.interceptor.js +0 -40
  173. package/cjs/interceptors/set-delete-by-on-body.interceptor.js +0 -40
  174. package/cjs/interceptors/set-update-by-on-body.interceptor.js +0 -40
  175. package/cjs/interfaces/base-query.interface.js +0 -6
  176. package/cjs/interfaces/logger.interface.js +0 -7
  177. package/cjs/modules/datasource/datasource.module.js +0 -120
  178. package/cjs/utils/error-handler.util.js +0 -67
  179. package/classes/request-scoped-api.service.d.ts +0 -13
  180. package/classes/winston-logger-adapter.class.d.ts +0 -20
  181. package/fesm/classes/request-scoped-api.service.js +0 -51
  182. package/fesm/classes/winston-logger-adapter.class.js +0 -124
  183. package/fesm/interceptors/query-performance.interceptor.js +0 -56
  184. package/fesm/interceptors/set-create-by-on-body.interceptor.js +0 -30
  185. package/fesm/interceptors/set-delete-by-on-body.interceptor.js +0 -30
  186. package/fesm/interceptors/set-update-by-on-body.interceptor.js +0 -30
  187. package/fesm/interfaces/base-query.interface.js +0 -3
  188. package/fesm/interfaces/logger.interface.js +0 -4
  189. package/fesm/modules/datasource/datasource.module.js +0 -110
  190. package/fesm/utils/error-handler.util.js +0 -59
  191. package/interceptors/query-performance.interceptor.d.ts +0 -8
  192. package/interceptors/set-create-by-on-body.interceptor.d.ts +0 -5
  193. package/interceptors/set-delete-by-on-body.interceptor.d.ts +0 -5
  194. package/interceptors/set-update-by-on-body.interceptor.d.ts +0 -5
  195. package/interfaces/base-query.interface.d.ts +0 -7
  196. package/interfaces/logger.interface.d.ts +0 -7
  197. package/modules/datasource/datasource.module.d.ts +0 -18
  198. package/utils/error-handler.util.d.ts +0 -24
@@ -25,216 +25,159 @@ function _ts_param(paramIndex, decorator) {
25
25
  decorator(target, key, paramIndex);
26
26
  };
27
27
  }
28
- import { Inject, Injectable, Logger, Optional, UnauthorizedException } from '@nestjs/common';
28
+ import { Inject, Injectable, Optional, UnauthorizedException } from '@nestjs/common';
29
29
  import { Reflector } from '@nestjs/core';
30
30
  import { HybridCache } from '../classes/hybrid-cache.class';
31
- import { NestLoggerAdapter } from '../classes/winston-logger-adapter.class';
32
- import { CACHE_INSTANCE, IS_PUBLIC_KEY, LOGGER_INSTANCE, PERMISSION_GUARD_CONFIG, PERMISSIONS_CACHE_PREFIX, PERMISSIONS_KEY } from '../constants';
31
+ import { AUTH_MESSAGES, CACHE_INSTANCE, IS_PUBLIC_KEY, PERMISSION_GUARD_CONFIG, PERMISSIONS_CACHE_PREFIX, PERMISSIONS_KEY } from '../constants';
33
32
  import { InsufficientPermissionsException, NoPermissionsFoundException, PermissionSystemUnavailableException } from '../exceptions/permission.exception';
34
- import { ILogger } from '../interfaces/logger.interface';
35
33
  import { PermissionGuardConfig } from '../interfaces/permission.interface';
36
34
  export class PermissionGuard {
37
35
  async canActivate(context) {
38
- // Check if route is marked as public
39
36
  const isPublic = this.reflector.getAllAndOverride(IS_PUBLIC_KEY, [
40
37
  context.getHandler(),
41
38
  context.getClass()
42
39
  ]);
43
40
  if (isPublic) return true;
44
- // Get required permissions from decorator
45
41
  const permissionConfig = this.reflector.getAllAndOverride(PERMISSIONS_KEY, [
46
42
  context.getHandler(),
47
43
  context.getClass()
48
44
  ]);
49
- // If no permissions required, allow access
50
- if (!permissionConfig) {
51
- return true;
52
- }
53
- // Normalize permission config (support old format: string[])
54
- const { permissions: requiredPermissions, operator } = this.normalizePermissionConfig(permissionConfig);
55
- // If no permissions required, allow access
56
- if (!requiredPermissions || requiredPermissions.length === 0) {
57
- return true;
58
- }
45
+ if (!permissionConfig) return true;
59
46
  const request = context.switchToHttp().getRequest();
60
47
  const user = request.user;
61
- // User must be authenticated
62
- if (!user) {
63
- throw new UnauthorizedException('Authentication required');
64
- }
65
- // Cache is required for permission checks - fail securely if unavailable
48
+ if (!user) throw new UnauthorizedException({
49
+ message: 'Authentication required',
50
+ messageKey: AUTH_MESSAGES.TOKEN_REQUIRED
51
+ });
66
52
  if (!this.cache) {
67
- // Log error (in production, this should be monitored)
68
- this.logger.error(`Cache not available - permission system unavailable (userId: ${user.id})`, undefined, 'PermissionGuard');
69
- // Fail securely - deny access rather than allowing without permission check
70
53
  throw new PermissionSystemUnavailableException();
71
54
  }
72
- // Get user's permissions from cache
73
55
  const userPermissions = await this.getUserPermissions(user);
74
- // If no permissions found in cache, deny access
75
56
  if (!userPermissions || userPermissions.length === 0) {
76
- this.logger.warn(`No permissions found for user (userId: ${user.id})`, 'PermissionGuard');
77
57
  throw new NoPermissionsFoundException();
78
58
  }
79
- // Check if this is a nested condition or simple permission list
80
- if (this.isNestedCondition(permissionConfig)) {
81
- // Complex nested permission check
82
- const result = this.evaluateCondition(permissionConfig, userPermissions);
83
- if (!result.passed) {
84
- this.logger.warn(`Permission check failed (userId: ${user.id}, missing: ${result.missingPermissions.join(', ')})`, 'PermissionGuard');
85
- throw new InsufficientPermissionsException(result.missingPermissions, result.operator);
86
- }
87
- } else {
88
- // Simple permission check (backward compatible)
89
- let hasRequiredPermissions;
90
- if (operator === 'or') {
91
- // OR: User must have at least ONE permission
92
- hasRequiredPermissions = requiredPermissions.some((permission)=>this.hasPermission(userPermissions, permission));
93
- if (!hasRequiredPermissions) {
94
- throw new InsufficientPermissionsException(requiredPermissions, 'or');
95
- }
96
- } else {
97
- // AND (default): User must have ALL permissions
98
- hasRequiredPermissions = requiredPermissions.every((permission)=>this.hasPermission(userPermissions, permission));
99
- if (!hasRequiredPermissions) {
100
- const missing = requiredPermissions.filter((permission)=>!this.hasPermission(userPermissions, permission));
101
- throw new InsufficientPermissionsException(missing, 'and');
102
- }
103
- }
59
+ const logicNode = this.normalizeToLogicNode(permissionConfig);
60
+ if (!logicNode) return true;
61
+ const result = this.evaluateLogicNode(logicNode, userPermissions);
62
+ if (!result.passed) {
63
+ throw new InsufficientPermissionsException(result.missingPermissions, result.operator);
104
64
  }
105
65
  return true;
106
66
  }
107
- /**
108
- * Normalize permission config to handle both old and new formats
109
- */ normalizePermissionConfig(config) {
110
- // Old format: string[]
67
+ normalizeToLogicNode(config) {
68
+ // string - single permission
69
+ if (typeof config === 'string') {
70
+ return config ? {
71
+ type: 'action',
72
+ actionId: config
73
+ } : null;
74
+ }
75
+ // string[] - treat as AND of multiple permissions
111
76
  if (Array.isArray(config)) {
77
+ if (config.length === 0) return null;
78
+ if (config.length === 1) return {
79
+ type: 'action',
80
+ actionId: config[0]
81
+ };
112
82
  return {
113
- permissions: config,
114
- operator: 'and'
83
+ type: 'group',
84
+ operator: 'AND',
85
+ children: config.map((p)=>({
86
+ type: 'action',
87
+ actionId: p
88
+ }))
115
89
  };
116
90
  }
117
- // New format: PermissionConfig
118
- return {
119
- permissions: config.permissions || [],
120
- operator: config.operator || 'and'
121
- };
122
- }
123
- /**
124
- * Check if config is a nested condition (has children)
125
- */ isNestedCondition(config) {
126
- if (Array.isArray(config)) return false;
127
- return 'children' in config && Array.isArray(config.children) && config.children.length > 0;
91
+ // SimplePermissionConfig - { permissions: string[], operator: 'AND'|'OR' }
92
+ if ('permissions' in config && Array.isArray(config.permissions)) {
93
+ const simple = config;
94
+ if (simple.permissions.length === 0) return null;
95
+ if (simple.permissions.length === 1) return {
96
+ type: 'action',
97
+ actionId: simple.permissions[0]
98
+ };
99
+ return {
100
+ type: 'group',
101
+ operator: simple.operator || 'AND',
102
+ children: simple.permissions.map((p)=>({
103
+ type: 'action',
104
+ actionId: p
105
+ }))
106
+ };
107
+ }
108
+ // ILogicNode
109
+ return config;
128
110
  }
129
- /**
130
- * Evaluate a nested permission condition recursively
131
- */ evaluateCondition(condition, userPermissions) {
132
- const { permissions = [], operator, children = [] } = condition;
133
- // Results for this level
111
+ evaluateLogicNode(node, userPermissions) {
112
+ if (node.type === 'action') {
113
+ const passed = this.hasPermission(userPermissions, node.actionId);
114
+ return {
115
+ passed,
116
+ missingPermissions: passed ? [] : [
117
+ node.actionId
118
+ ],
119
+ operator: 'AND'
120
+ };
121
+ }
122
+ // Group node
123
+ const { operator, children } = node;
124
+ // SECURITY: Fail-closed - deny access when no children configured
125
+ if (!children || children.length === 0) {
126
+ return {
127
+ passed: false,
128
+ missingPermissions: [],
129
+ operator
130
+ };
131
+ }
134
132
  const results = [];
135
- const failureDetails = [];
136
133
  const missingPermissions = [];
137
- // Check permissions at this level
138
- if (permissions.length > 0) {
139
- if (operator === 'or') {
140
- const hasAny = permissions.some((p)=>this.hasPermission(userPermissions, p));
141
- results.push(hasAny);
142
- if (!hasAny) {
143
- failureDetails.push(`needs one of: [${permissions.join(', ')}]`);
144
- missingPermissions.push(...permissions);
145
- }
146
- } else {
147
- const hasAll = permissions.every((p)=>this.hasPermission(userPermissions, p));
148
- results.push(hasAll);
149
- if (!hasAll) {
150
- const missing = permissions.filter((p)=>!this.hasPermission(userPermissions, p));
151
- failureDetails.push(`missing: [${missing.join(', ')}]`);
152
- missingPermissions.push(...missing);
153
- }
154
- }
155
- }
156
- // Evaluate children recursively
157
134
  for (const child of children){
158
- const childResult = this.evaluateCondition(child, userPermissions);
135
+ const childResult = this.evaluateLogicNode(child, userPermissions);
159
136
  results.push(childResult.passed);
160
137
  if (!childResult.passed) {
161
- failureDetails.push(`(${childResult.message})`);
162
138
  missingPermissions.push(...childResult.missingPermissions);
163
139
  }
164
140
  }
165
- // Combine results based on operator
166
- let passed;
167
- if (operator === 'or') {
168
- passed = results.length === 0 || results.some((r)=>r);
169
- } else {
170
- passed = results.length === 0 || results.every((r)=>r);
171
- }
172
- const message = passed ? 'Permission granted' : `Permission denied: ${failureDetails.join(` ${operator.toUpperCase()} `)}`;
141
+ const passed = operator === 'OR' ? results.some((r)=>r) : results.every((r)=>r);
173
142
  return {
174
143
  passed,
175
- message,
176
- missingPermissions,
144
+ missingPermissions: passed ? [] : missingPermissions,
177
145
  operator
178
146
  };
179
147
  }
180
- /**
181
- * Get user's permissions from cache
182
- */ async getUserPermissions(user) {
183
- if (!this.cache) {
184
- throw new PermissionSystemUnavailableException();
185
- }
186
- let cacheKey;
148
+ async getUserPermissions(user) {
149
+ if (!this.cache) throw new PermissionSystemUnavailableException();
150
+ const cacheKey = this.buildPermissionCacheKey(user);
151
+ return await this.cache.get(cacheKey) || [];
152
+ }
153
+ buildPermissionCacheKey(user) {
187
154
  if (this.config.enableCompanyFeature && user.companyId) {
188
- // Company-based permissions (includes branchId for branch-scoped DIRECT permissions)
189
- const format = this.config.companyPermissionKeyFormat || `${PERMISSIONS_CACHE_PREFIX}:company:{companyId}:branch:{branchId}:user:{userId}`;
190
- cacheKey = format.replace('{userId}', user.id).replace('{companyId}', user.companyId).replace('{branchId}', user.branchId || 'null');
191
- } else {
192
- // User-based permissions
193
- const format = this.config.userPermissionKeyFormat || `${PERMISSIONS_CACHE_PREFIX}:user:{userId}`;
194
- cacheKey = format.replace('{userId}', user.id);
155
+ return (this.config.companyPermissionKeyFormat || `${PERMISSIONS_CACHE_PREFIX}:company:{companyId}:branch:{branchId}:user:{userId}`).replace('{userId}', user.id).replace('{companyId}', user.companyId).replace('{branchId}', user.branchId || 'null');
195
156
  }
196
- const permissions = await this.cache.get(cacheKey);
197
- return permissions || [];
157
+ return (this.config.userPermissionKeyFormat || `${PERMISSIONS_CACHE_PREFIX}:user:{userId}`).replace('{userId}', user.id);
198
158
  }
199
- /**
200
- * Check if user has a specific permission
201
- * Supports wildcard matching (e.g., 'admin.*' matches 'admin.users.read')
202
- */ hasPermission(userPermissions, requiredPermission) {
203
- // Direct match
204
- if (userPermissions.includes(requiredPermission)) {
205
- return true;
206
- }
207
- // Wildcard match (e.g., '*' or 'admin.*')
159
+ hasPermission(userPermissions, requiredPermission) {
160
+ if (userPermissions.includes(requiredPermission)) return true;
208
161
  for (const permission of userPermissions){
209
- if (permission === '*') {
210
- return true; // Super admin
211
- }
212
- if (permission.endsWith('.*')) {
213
- const prefix = permission.slice(0, -1); // Remove '*'
214
- if (requiredPermission.startsWith(prefix)) {
215
- return true;
216
- }
162
+ if (permission === '*') return true;
163
+ if (permission.endsWith('.*') && requiredPermission.startsWith(permission.slice(0, -1))) {
164
+ return true;
217
165
  }
218
166
  }
219
167
  return false;
220
168
  }
221
- // NOTE: @Inject(Reflector) required for bundled code - external classes need explicit injection
222
- constructor(reflector, cache, config, logger){
169
+ constructor(reflector, cache, config){
223
170
  _define_property(this, "reflector", void 0);
224
171
  _define_property(this, "cache", void 0);
225
172
  _define_property(this, "config", void 0);
226
- _define_property(this, "logger", void 0);
227
173
  this.reflector = reflector;
228
174
  this.cache = cache;
229
175
  this.config = {
230
176
  enableCompanyFeature: false,
231
- cacheKeyPrefix: PERMISSIONS_CACHE_PREFIX,
232
177
  userPermissionKeyFormat: `${PERMISSIONS_CACHE_PREFIX}:user:{userId}`,
233
178
  companyPermissionKeyFormat: `${PERMISSIONS_CACHE_PREFIX}:company:{companyId}:branch:{branchId}:user:{userId}`,
234
179
  ...config
235
180
  };
236
- // Use provided logger or fallback to NestJS Logger wrapped in adapter
237
- this.logger = logger || new NestLoggerAdapter(new Logger(PermissionGuard.name));
238
181
  }
239
182
  }
240
183
  PermissionGuard = _ts_decorate([
@@ -244,13 +187,10 @@ PermissionGuard = _ts_decorate([
244
187
  _ts_param(1, Inject(CACHE_INSTANCE)),
245
188
  _ts_param(2, Optional()),
246
189
  _ts_param(2, Inject(PERMISSION_GUARD_CONFIG)),
247
- _ts_param(3, Optional()),
248
- _ts_param(3, Inject(LOGGER_INSTANCE)),
249
190
  _ts_metadata("design:type", Function),
250
191
  _ts_metadata("design:paramtypes", [
251
192
  typeof Reflector === "undefined" ? Object : Reflector,
252
193
  typeof HybridCache === "undefined" ? Object : HybridCache,
253
- typeof PermissionGuardConfig === "undefined" ? Object : PermissionGuardConfig,
254
- typeof ILogger === "undefined" ? Object : ILogger
194
+ typeof PermissionGuardConfig === "undefined" ? Object : PermissionGuardConfig
255
195
  ])
256
196
  ], PermissionGuard);
package/fesm/index.js CHANGED
@@ -3,7 +3,9 @@ export * from './constants';
3
3
  export * from './decorators';
4
4
  export * from './dtos';
5
5
  export * from './entities';
6
+ export * from './enums';
6
7
  export * from './exceptions';
8
+ export * from './filters';
7
9
  export * from './guards';
8
10
  export * from './interceptors';
9
11
  export * from './interfaces';
@@ -29,7 +29,7 @@ import { ConflictException, Inject, Injectable, Optional } from '@nestjs/common'
29
29
  import { of } from 'rxjs';
30
30
  import { tap } from 'rxjs/operators';
31
31
  import { HybridCache } from '../classes/hybrid-cache.class';
32
- import { CACHE_INSTANCE, IDEMPOTENCY_CACHE_PREFIX, IDEMPOTENCY_KEY_HEADER } from '../constants';
32
+ import { CACHE_INSTANCE, IDEMPOTENCY_CACHE_PREFIX, IDEMPOTENCY_KEY_HEADER, SYSTEM_MESSAGES } from '../constants';
33
33
  export class IdempotencyInterceptor {
34
34
  async intercept(context, next) {
35
35
  // Skip if no cache available
@@ -51,7 +51,7 @@ export class IdempotencyInterceptor {
51
51
  throw new ConflictException({
52
52
  success: false,
53
53
  message: 'Request is already being processed',
54
- code: 'DUPLICATE_REQUEST_IN_PROGRESS'
54
+ messageKey: SYSTEM_MESSAGES.DUPLICATE_REQUEST
55
55
  });
56
56
  }
57
57
  // Return cached response
@@ -1,8 +1,5 @@
1
1
  export * from './delete-empty-id-from-body.interceptor';
2
2
  export * from './idempotency.interceptor';
3
- export * from './query-performance.interceptor';
4
3
  export * from './response-meta.interceptor';
5
- export * from './set-create-by-on-body.interceptor';
6
- export * from './set-delete-by-on-body.interceptor';
7
- export * from './set-update-by-on-body.interceptor';
4
+ export * from './set-user-field-on-body.interceptor';
8
5
  export * from './slug.interceptor';
@@ -0,0 +1,39 @@
1
+ function _ts_decorate(decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ 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;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ }
7
+ import { Injectable } from '@nestjs/common';
8
+ /**
9
+ * Factory function to create interceptors that set user ID fields on request body
10
+ * @param fieldName - The field name to set (e.g., 'createdById', 'updatedById', 'deletedById')
11
+ */ export function createSetUserFieldInterceptor(fieldName) {
12
+ let SetUserFieldOnBody = class SetUserFieldOnBody {
13
+ intercept(context, next) {
14
+ const request = context.switchToHttp().getRequest();
15
+ const user = request?.user;
16
+ if (user) {
17
+ if (Array.isArray(request.body)) {
18
+ request.body = request.body.map((item)=>({
19
+ ...item,
20
+ [fieldName]: user.id
21
+ }));
22
+ } else if (typeof request.body === 'object' && request.body !== null) {
23
+ request.body = {
24
+ ...request.body,
25
+ [fieldName]: user.id
26
+ };
27
+ }
28
+ }
29
+ return next.handle();
30
+ }
31
+ };
32
+ SetUserFieldOnBody = _ts_decorate([
33
+ Injectable()
34
+ ], SetUserFieldOnBody);
35
+ return SetUserFieldOnBody;
36
+ }
37
+ /** Sets createdById field on request body from authenticated user */ export const SetCreatedByOnBody = createSetUserFieldInterceptor('createdById');
38
+ /** Sets updatedById field on request body from authenticated user */ export const SetUpdateByOnBody = createSetUserFieldInterceptor('updatedById');
39
+ /** Sets deletedById field on request body from authenticated user */ export const SetDeletedByOnBody = createSetUserFieldInterceptor('deletedById');
@@ -17,28 +17,49 @@ function _ts_decorate(decorators, target, key, desc) {
17
17
  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;
18
18
  return c > 3 && r && Object.defineProperty(target, key, r), r;
19
19
  }
20
- import { Injectable } from '@nestjs/common';
20
+ function _ts_metadata(k, v) {
21
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
22
+ }
23
+ function _ts_param(paramIndex, decorator) {
24
+ return function(target, key) {
25
+ decorator(target, key, paramIndex);
26
+ };
27
+ }
28
+ import { Inject, Injectable } from '@nestjs/common';
21
29
  import { UtilsService } from '../modules/utils/utils.service';
22
30
  export class Slug {
23
31
  intercept(context, next) {
24
32
  const request = context.switchToHttp().getRequest();
25
33
  if (Array.isArray(request.body)) {
26
- request.body = request.body.map((item)=>({
27
- ...item,
28
- slug: item.slug || (item?.name ? this.utilsService.transformToSlug(item.name) : null)
29
- }));
30
- } else if (!request.body.slug) {
34
+ request.body = request.body.map((item)=>{
35
+ // Only generate slug if not provided and name exists
36
+ if (!item.slug && item?.name) {
37
+ return {
38
+ ...item,
39
+ slug: this.utilsService.transformToSlug(item.name)
40
+ };
41
+ }
42
+ return item;
43
+ });
44
+ } else if (!request.body?.slug && request.body?.name) {
45
+ // Only generate slug if not provided and name exists
31
46
  request.body = {
32
47
  ...request.body,
33
- slug: request.body?.name ? this.utilsService.transformToSlug(request.body?.name) : null
48
+ slug: this.utilsService.transformToSlug(request.body.name)
34
49
  };
35
50
  }
36
51
  return next.handle();
37
52
  }
38
- constructor(){
39
- _define_property(this, "utilsService", new UtilsService());
53
+ constructor(utilsService){
54
+ _define_property(this, "utilsService", void 0);
55
+ this.utilsService = utilsService;
40
56
  }
41
57
  }
42
58
  Slug = _ts_decorate([
43
- Injectable()
59
+ Injectable(),
60
+ _ts_param(0, Inject(UtilsService)),
61
+ _ts_metadata("design:type", Function),
62
+ _ts_metadata("design:paramtypes", [
63
+ typeof UtilsService === "undefined" ? Object : UtilsService
64
+ ])
44
65
  ], Slug);
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1 @@
1
+ export const EVENT_MANAGER_ADAPTER = 'EVENT_MANAGER_ADAPTER';
@@ -1,6 +1,8 @@
1
1
  export * from './api.interface';
2
- export * from './base-query.interface';
2
+ export * from './datasource.interface';
3
+ export * from './event-manager-adapter.interface';
3
4
  export * from './identity.interface';
4
5
  export * from './logged-user-info.interface';
5
- export * from './logger.interface';
6
+ export * from './module-config.interface';
7
+ export * from './notification-adapter.interface';
6
8
  export * from './permission.interface';
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Common interface for module configuration services.
3
+ * All feature module config services should implement this interface
4
+ * to ensure consistent access to bootstrap configuration.
5
+ */ export { };
@@ -0,0 +1 @@
1
+ export const NOTIFICATION_ADAPTER = 'NOTIFICATION_ADAPTER';
@@ -1,3 +1 @@
1
- /**
2
- * Configuration for the permission guard
3
- */ export { };
1
+ /** Action node - checks a single permission/action code */ /** Configuration for the permission guard */ export { };