@flusys/nestjs-shared 1.1.0 → 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 (158) hide show
  1. package/README.md +139 -1103
  2. package/cjs/classes/api-controller.class.js +320 -29
  3. package/cjs/classes/api-service.class.js +219 -64
  4. package/cjs/classes/handle-custome-db.js +37 -0
  5. package/cjs/classes/index.js +1 -2
  6. package/cjs/classes/winston.logger.class.js +103 -70
  7. package/cjs/constants/index.js +1 -4
  8. package/cjs/constants/message-keys.js +68 -0
  9. package/cjs/constants/permissions.js +114 -11
  10. package/cjs/decorators/api-response.decorator.js +1 -2
  11. package/cjs/decorators/index.js +1 -1
  12. package/cjs/decorators/log-action.decorator.js +149 -0
  13. package/cjs/decorators/require-permission.decorator.js +5 -5
  14. package/cjs/dtos/get-by-ids.dto.js +65 -0
  15. package/cjs/dtos/identity-response.dto.js +4 -8
  16. package/cjs/dtos/index.js +1 -0
  17. package/cjs/dtos/response-payload.dto.js +72 -0
  18. package/cjs/entities/identity.js +8 -2
  19. package/cjs/entities/index.js +1 -0
  20. package/cjs/entities/raw-type.js +4 -0
  21. package/cjs/entities/user-root.js +2 -1
  22. package/cjs/enums/index.js +20 -0
  23. package/cjs/enums/notification-type.enum.js +17 -0
  24. package/cjs/enums/participant-status.enum.js +17 -0
  25. package/cjs/enums/recurrence-type.enum.js +18 -0
  26. package/cjs/exceptions/base-app.exception.js +151 -0
  27. package/cjs/exceptions/index.js +1 -0
  28. package/cjs/exceptions/permission.exception.js +13 -9
  29. package/cjs/filters/global-exception.filter.js +279 -0
  30. package/cjs/filters/index.js +18 -0
  31. package/cjs/guards/jwt-auth.guard.js +4 -1
  32. package/cjs/guards/permission.guard.js +71 -77
  33. package/cjs/index.js +2 -0
  34. package/cjs/interceptors/idempotency.interceptor.js +1 -1
  35. package/cjs/interceptors/index.js +0 -1
  36. package/cjs/interfaces/event-manager-adapter.interface.js +11 -0
  37. package/cjs/interfaces/index.js +2 -1
  38. package/cjs/interfaces/notification-adapter.interface.js +11 -0
  39. package/cjs/interfaces/permission.interface.js +1 -1
  40. package/cjs/middlewares/logger.middleware.js +83 -26
  41. package/cjs/modules/datasource/index.js +0 -1
  42. package/cjs/modules/datasource/multi-tenant-datasource.service.js +46 -85
  43. package/cjs/modules/utils/utils.service.js +4 -20
  44. package/cjs/utils/date-time.util.js +94 -0
  45. package/cjs/utils/index.js +1 -1
  46. package/cjs/utils/query-helpers.util.js +8 -1
  47. package/cjs/utils/request.util.js +25 -0
  48. package/classes/api-controller.class.d.ts +14 -6
  49. package/classes/api-service.class.d.ts +25 -22
  50. package/classes/handle-custome-db.d.ts +3 -0
  51. package/classes/index.d.ts +1 -2
  52. package/classes/winston.logger.class.d.ts +1 -0
  53. package/constants/index.d.ts +1 -1
  54. package/constants/message-keys.d.ts +45 -0
  55. package/constants/permissions.d.ts +127 -0
  56. package/decorators/api-response.decorator.d.ts +1 -1
  57. package/decorators/current-user.decorator.d.ts +1 -1
  58. package/decorators/index.d.ts +1 -1
  59. package/decorators/log-action.decorator.d.ts +8 -0
  60. package/decorators/require-permission.decorator.d.ts +2 -2
  61. package/dtos/get-by-ids.dto.d.ts +4 -0
  62. package/dtos/index.d.ts +1 -0
  63. package/dtos/response-payload.dto.d.ts +8 -0
  64. package/entities/index.d.ts +1 -0
  65. package/entities/raw-type.d.ts +1 -0
  66. package/enums/index.d.ts +3 -0
  67. package/enums/notification-type.enum.d.ts +6 -0
  68. package/enums/participant-status.enum.d.ts +6 -0
  69. package/enums/recurrence-type.enum.d.ts +7 -0
  70. package/exceptions/base-app.exception.d.ts +41 -0
  71. package/exceptions/index.d.ts +1 -0
  72. package/exceptions/permission.exception.d.ts +2 -2
  73. package/fesm/classes/api-controller.class.js +321 -30
  74. package/fesm/classes/api-service.class.js +222 -67
  75. package/fesm/classes/handle-custome-db.js +19 -0
  76. package/fesm/classes/index.js +1 -2
  77. package/fesm/classes/winston.logger.class.js +100 -68
  78. package/fesm/constants/index.js +2 -1
  79. package/fesm/constants/message-keys.js +49 -0
  80. package/fesm/constants/permissions.js +86 -14
  81. package/fesm/decorators/api-response.decorator.js +2 -20
  82. package/fesm/decorators/index.js +1 -1
  83. package/fesm/decorators/log-action.decorator.js +139 -0
  84. package/fesm/decorators/require-permission.decorator.js +17 -65
  85. package/fesm/dtos/get-by-ids.dto.js +55 -0
  86. package/fesm/dtos/identity-response.dto.js +4 -8
  87. package/fesm/dtos/index.js +1 -0
  88. package/fesm/dtos/response-payload.dto.js +72 -0
  89. package/fesm/entities/identity.js +8 -2
  90. package/fesm/entities/index.js +1 -0
  91. package/fesm/entities/raw-type.js +1 -0
  92. package/fesm/entities/user-root.js +2 -1
  93. package/fesm/enums/index.js +3 -0
  94. package/fesm/enums/notification-type.enum.js +7 -0
  95. package/fesm/enums/participant-status.enum.js +7 -0
  96. package/fesm/enums/recurrence-type.enum.js +8 -0
  97. package/fesm/exceptions/base-app.exception.js +115 -0
  98. package/fesm/exceptions/index.js +1 -0
  99. package/fesm/exceptions/permission.exception.js +16 -18
  100. package/fesm/filters/global-exception.filter.js +269 -0
  101. package/fesm/filters/index.js +1 -0
  102. package/fesm/guards/jwt-auth.guard.js +5 -2
  103. package/fesm/guards/permission.guard.js +73 -79
  104. package/fesm/index.js +2 -0
  105. package/fesm/interceptors/idempotency.interceptor.js +2 -2
  106. package/fesm/interceptors/index.js +0 -1
  107. package/fesm/interfaces/datasource.interface.js +1 -20
  108. package/fesm/interfaces/event-manager-adapter.interface.js +1 -0
  109. package/fesm/interfaces/index.js +2 -1
  110. package/fesm/interfaces/notification-adapter.interface.js +1 -0
  111. package/fesm/interfaces/permission.interface.js +1 -3
  112. package/fesm/middlewares/logger.middleware.js +83 -26
  113. package/fesm/modules/datasource/index.js +0 -1
  114. package/fesm/modules/datasource/multi-tenant-datasource.service.js +47 -86
  115. package/fesm/modules/utils/utils.service.js +5 -21
  116. package/fesm/utils/date-time.util.js +70 -0
  117. package/fesm/utils/index.js +1 -1
  118. package/fesm/utils/query-helpers.util.js +8 -1
  119. package/fesm/utils/request.util.js +22 -0
  120. package/filters/global-exception.filter.d.ts +9 -0
  121. package/filters/index.d.ts +1 -0
  122. package/guards/permission.guard.d.ts +3 -7
  123. package/index.d.ts +2 -0
  124. package/interceptors/idempotency.interceptor.d.ts +1 -1
  125. package/interceptors/index.d.ts +0 -1
  126. package/interfaces/api.interface.d.ts +2 -0
  127. package/interfaces/event-manager-adapter.interface.d.ts +43 -0
  128. package/interfaces/index.d.ts +2 -1
  129. package/interfaces/notification-adapter.interface.d.ts +21 -0
  130. package/interfaces/permission.interface.d.ts +13 -8
  131. package/modules/datasource/index.d.ts +0 -1
  132. package/modules/datasource/multi-tenant-datasource.service.d.ts +5 -15
  133. package/modules/utils/utils.service.d.ts +0 -1
  134. package/package.json +21 -14
  135. package/utils/date-time.util.d.ts +8 -0
  136. package/utils/index.d.ts +1 -1
  137. package/utils/request.util.d.ts +2 -0
  138. package/cjs/classes/request-scoped-api.service.js +0 -61
  139. package/cjs/classes/winston-logger-adapter.class.js +0 -104
  140. package/cjs/decorators/sanitize-html.decorator.js +0 -36
  141. package/cjs/interceptors/query-performance.interceptor.js +0 -66
  142. package/cjs/interfaces/logger.interface.js +0 -7
  143. package/cjs/modules/datasource/datasource.module.js +0 -117
  144. package/cjs/utils/error-handler.util.js +0 -90
  145. package/classes/request-scoped-api.service.d.ts +0 -14
  146. package/classes/winston-logger-adapter.class.d.ts +0 -22
  147. package/decorators/sanitize-html.decorator.d.ts +0 -2
  148. package/fesm/classes/request-scoped-api.service.js +0 -51
  149. package/fesm/classes/winston-logger-adapter.class.js +0 -107
  150. package/fesm/decorators/sanitize-html.decorator.js +0 -45
  151. package/fesm/interceptors/query-performance.interceptor.js +0 -56
  152. package/fesm/interfaces/logger.interface.js +0 -4
  153. package/fesm/modules/datasource/datasource.module.js +0 -107
  154. package/fesm/utils/error-handler.util.js +0 -82
  155. package/interceptors/query-performance.interceptor.d.ts +0 -8
  156. package/interfaces/logger.interface.d.ts +0 -7
  157. package/modules/datasource/datasource.module.d.ts +0 -19
  158. package/utils/error-handler.util.d.ts +0 -19
@@ -0,0 +1,279 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "GlobalExceptionFilter", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return GlobalExceptionFilter;
9
+ }
10
+ });
11
+ const _common = require("@nestjs/common");
12
+ const _typeorm = require("typeorm");
13
+ const _messagekeys = require("../constants/message-keys");
14
+ const _baseappexception = require("../exceptions/base-app.exception");
15
+ const _loggermiddleware = require("../middlewares/logger.middleware");
16
+ function _define_property(obj, key, value) {
17
+ if (key in obj) {
18
+ Object.defineProperty(obj, key, {
19
+ value: value,
20
+ enumerable: true,
21
+ configurable: true,
22
+ writable: true
23
+ });
24
+ } else {
25
+ obj[key] = value;
26
+ }
27
+ return obj;
28
+ }
29
+ function _ts_decorate(decorators, target, key, desc) {
30
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
31
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
32
+ 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;
33
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
34
+ }
35
+ let GlobalExceptionFilter = class GlobalExceptionFilter {
36
+ catch(exception, host) {
37
+ const ctx = host.switchToHttp();
38
+ const request = ctx.getRequest();
39
+ const response = ctx.getResponse();
40
+ const responseTime = Date.now() - (request._startTime || Date.now());
41
+ const requestId = (0, _loggermiddleware.getRequestId)() || request.headers['x-request-id'] || 'no-request-id';
42
+ const { statusCode, errorResponse } = this.resolveException(exception, requestId, responseTime);
43
+ const logContext = {
44
+ requestId,
45
+ userId: (0, _loggermiddleware.getUserId)(),
46
+ tenantId: (0, _loggermiddleware.getTenantId)(),
47
+ companyId: (0, _loggermiddleware.getCompanyId)(),
48
+ method: request.method,
49
+ url: request.originalUrl,
50
+ path: request.path,
51
+ statusCode,
52
+ duration: `${responseTime}ms`,
53
+ messageKey: errorResponse.messageKey
54
+ };
55
+ const logMessage = `Exception caught "HTTP/1.1" "${request.method}" "${request.originalUrl}" - ${statusCode} "${errorResponse.messageKey}" ${responseTime}ms | ${errorResponse.message}`;
56
+ if (statusCode >= 500) {
57
+ this.logger.error(logMessage, exception instanceof Error ? exception.stack : undefined, logContext);
58
+ } else if (statusCode >= 400) {
59
+ this.logger.warn(logMessage, logContext);
60
+ }
61
+ response.status(statusCode).json(errorResponse);
62
+ }
63
+ resolveException(exception, requestId, responseTime) {
64
+ const meta = {
65
+ requestId,
66
+ timestamp: new Date().toISOString(),
67
+ responseTime
68
+ };
69
+ if (exception instanceof _baseappexception.BaseAppException) {
70
+ return {
71
+ statusCode: exception.getStatus(),
72
+ errorResponse: {
73
+ success: false,
74
+ message: exception.message,
75
+ messageKey: exception.messageKey,
76
+ messageVariables: exception.messageVariables,
77
+ errors: exception.errors,
78
+ _meta: meta
79
+ }
80
+ };
81
+ }
82
+ if (exception instanceof _common.HttpException) {
83
+ const resolved = this.resolveHttpException(exception);
84
+ return {
85
+ statusCode: resolved.statusCode,
86
+ errorResponse: {
87
+ success: false,
88
+ message: resolved.message,
89
+ messageKey: resolved.messageKey,
90
+ messageVariables: resolved.messageVariables,
91
+ errors: resolved.errors,
92
+ _meta: meta
93
+ }
94
+ };
95
+ }
96
+ if (exception instanceof _typeorm.QueryFailedError) {
97
+ const resolved = this.resolveQueryFailedError(exception);
98
+ if (resolved) {
99
+ return {
100
+ statusCode: resolved.getStatus(),
101
+ errorResponse: {
102
+ success: false,
103
+ message: resolved.message,
104
+ messageKey: resolved.messageKey,
105
+ messageVariables: resolved.messageVariables,
106
+ _meta: meta
107
+ }
108
+ };
109
+ }
110
+ }
111
+ return {
112
+ statusCode: _common.HttpStatus.INTERNAL_SERVER_ERROR,
113
+ errorResponse: {
114
+ success: false,
115
+ message: 'Internal server error',
116
+ messageKey: _messagekeys.ERROR_MESSAGES.INTERNAL,
117
+ _meta: meta
118
+ }
119
+ };
120
+ }
121
+ resolveHttpException(exception) {
122
+ const statusCode = exception.getStatus();
123
+ const response = exception.getResponse();
124
+ if (typeof response === 'string') {
125
+ return {
126
+ statusCode,
127
+ message: response,
128
+ messageKey: _messagekeys.ERROR_MESSAGES.HTTP
129
+ };
130
+ }
131
+ if (typeof response === 'object' && response !== null) {
132
+ const obj = response;
133
+ if (Array.isArray(obj.message)) {
134
+ const errors = obj.message.map((msg)=>{
135
+ const [field, ...rest] = msg.split(' ');
136
+ return {
137
+ field: field || 'unknown',
138
+ message: rest.join(' ') || msg
139
+ };
140
+ });
141
+ return {
142
+ statusCode,
143
+ message: 'Validation failed',
144
+ messageKey: _messagekeys.ERROR_MESSAGES.VALIDATION,
145
+ errors
146
+ };
147
+ }
148
+ return {
149
+ statusCode,
150
+ message: String(obj.message || 'Unknown error'),
151
+ messageKey: String(obj.messageKey || _messagekeys.ERROR_MESSAGES.HTTP),
152
+ messageVariables: obj.messageVariables,
153
+ errors: obj.errors
154
+ };
155
+ }
156
+ return {
157
+ statusCode,
158
+ message: 'Unknown error',
159
+ messageKey: _messagekeys.ERROR_MESSAGES.UNKNOWN
160
+ };
161
+ }
162
+ resolveQueryFailedError(exception) {
163
+ const dbError = exception;
164
+ const code = dbError.driverError?.code ?? dbError.code;
165
+ const number = dbError.driverError?.number ?? dbError.number;
166
+ const errno = dbError.driverError?.errno ?? dbError.errno;
167
+ const sqlMessage = dbError.driverError?.sqlMessage ?? '';
168
+ const detail = dbError.driverError?.detail ?? dbError.detail ?? '';
169
+ const table = dbError.driverError?.table ?? dbError.table ?? '';
170
+ const column = dbError.driverError?.column ?? dbError.column;
171
+ const pgField = detail.match(/Key \(([^)]+)\)/)?.[1] ?? column;
172
+ // DUPLICATE / UNIQUE VIOLATION
173
+ // PG: 23505 | MSSQL: 2627 (constraint), 2601 (index) | MySQL: ER_DUP_ENTRY / errno 1062
174
+ if (code === '23505' || number === 2627 || number === 2601 || code === 'ER_DUP_ENTRY' || errno === 1062) {
175
+ const mysqlTable = sqlMessage.match(/for key '([^'.]+)\./)?.[1];
176
+ const mysqlIndex = sqlMessage.match(/for key '(?:[^.]+\.)?([^']+)'/)?.[1];
177
+ const resolvedTable = table || mysqlTable || 'record';
178
+ const resolvedField = pgField ? this.toHumanReadable(pgField) : mysqlIndex ? this.toHumanReadable(mysqlIndex) : undefined;
179
+ return new _baseappexception.ConflictException(this.toHumanReadable(resolvedTable), resolvedField);
180
+ }
181
+ // FOREIGN KEY VIOLATION
182
+ // PG: 23503 | MSSQL: 547 | MySQL: ER_NO_REFERENCED_ROW_2/1452 (insert), ER_ROW_IS_REFERENCED_2/1451 (delete)
183
+ if (code === '23503' || number === 547 || code === 'ER_NO_REFERENCED_ROW_2' || code === 'ER_ROW_IS_REFERENCED_2' || errno === 1452 || errno === 1451) {
184
+ const mysqlColumn = sqlMessage.match(/FOREIGN KEY \(`([^`]+)`\)/)?.[1];
185
+ const raw = pgField ?? mysqlColumn;
186
+ const humanField = raw ? this.toHumanReadable(raw) : undefined;
187
+ return new _baseappexception.BaseAppException({
188
+ message: humanField ? `Invalid reference for '${humanField}'` : 'Invalid reference',
189
+ messageKey: _messagekeys.ERROR_MESSAGES.DB_FOREIGN_KEY,
190
+ messageVariables: humanField ? {
191
+ field: humanField
192
+ } : undefined,
193
+ status: _common.HttpStatus.BAD_REQUEST
194
+ });
195
+ }
196
+ // NOT NULL VIOLATION
197
+ // PG: 23502 | MSSQL: 515 | MySQL: ER_BAD_NULL_ERROR / errno 1048
198
+ if (code === '23502' || number === 515 || code === 'ER_BAD_NULL_ERROR' || errno === 1048) {
199
+ const mysqlColumn = sqlMessage.match(/Column '([^']+)' cannot be null/)?.[1];
200
+ const humanField = this.toHumanReadable(column ?? mysqlColumn ?? 'field');
201
+ return new _baseappexception.BaseAppException({
202
+ message: `Required field '${humanField}' cannot be null`,
203
+ messageKey: _messagekeys.ERROR_MESSAGES.DB_NOT_NULL,
204
+ messageVariables: {
205
+ field: humanField
206
+ },
207
+ status: _common.HttpStatus.BAD_REQUEST
208
+ });
209
+ }
210
+ // INVALID INPUT SYNTAX — PostgreSQL only (MySQL silently coerces types)
211
+ if (code === '22P02') {
212
+ const typeMatch = exception.message.match(/invalid input syntax for(?:\s+type)?\s+(\w+)/i);
213
+ const type = typeMatch?.[1] ?? 'value';
214
+ const humanField = column ? this.toHumanReadable(column) : undefined;
215
+ return new _baseappexception.BaseAppException({
216
+ message: humanField ? `Invalid ${type} value for '${humanField}'` : `Invalid ${type} value`,
217
+ messageKey: _messagekeys.ERROR_MESSAGES.DB_INVALID_INPUT,
218
+ messageVariables: {
219
+ type,
220
+ ...humanField !== undefined && {
221
+ field: humanField
222
+ }
223
+ },
224
+ status: _common.HttpStatus.BAD_REQUEST
225
+ });
226
+ }
227
+ // VALUE TOO LONG
228
+ // PG: 22001 | MySQL: ER_DATA_TOO_LONG / errno 1406
229
+ if (code === '22001' || code === 'ER_DATA_TOO_LONG' || errno === 1406) {
230
+ const mysqlColumn = sqlMessage.match(/Data too long for column '([^']+)'/)?.[1];
231
+ const resolvedColumn = column ?? mysqlColumn;
232
+ const humanField = resolvedColumn ? this.toHumanReadable(resolvedColumn) : undefined;
233
+ return new _baseappexception.BaseAppException({
234
+ message: humanField ? `Value too long for '${humanField}'` : 'Value exceeds maximum length',
235
+ messageKey: _messagekeys.ERROR_MESSAGES.DB_VALUE_TOO_LONG,
236
+ messageVariables: humanField !== undefined ? {
237
+ field: humanField
238
+ } : undefined,
239
+ status: _common.HttpStatus.BAD_REQUEST
240
+ });
241
+ }
242
+ // CHECK CONSTRAINT VIOLATION
243
+ // PG: 23514 | MySQL 8.0+: ER_CHECK_CONSTRAINT_VIOLATED / errno 3819
244
+ if (code === '23514' || code === 'ER_CHECK_CONSTRAINT_VIOLATED' || errno === 3819) {
245
+ const constraint = dbError.driverError?.constraint ?? dbError.constraint;
246
+ const humanTable = this.toHumanReadable(table || 'record');
247
+ return new _baseappexception.BaseAppException({
248
+ message: `${humanTable} violates a validation rule`,
249
+ messageKey: _messagekeys.ERROR_MESSAGES.DB_CHECK_CONSTRAINT,
250
+ messageVariables: {
251
+ entity: humanTable,
252
+ ...constraint !== undefined && {
253
+ constraint
254
+ }
255
+ },
256
+ status: _common.HttpStatus.BAD_REQUEST
257
+ });
258
+ }
259
+ // DEADLOCK
260
+ // PG: 40P01 | MSSQL: 1205 | MySQL: ER_LOCK_DEADLOCK / errno 1213
261
+ if (code === '40P01' || number === 1205 || code === 'ER_LOCK_DEADLOCK' || errno === 1213) {
262
+ return new _baseappexception.BaseAppException({
263
+ message: 'The request could not be completed due to a conflict, please try again',
264
+ messageKey: _messagekeys.ERROR_MESSAGES.DB_DEADLOCK,
265
+ status: _common.HttpStatus.SERVICE_UNAVAILABLE
266
+ });
267
+ }
268
+ return null;
269
+ }
270
+ toHumanReadable(dbName) {
271
+ return dbName.split(', ').map((part)=>part.split('_').map((word)=>word.toLowerCase() === 'id' ? 'ID' : word.charAt(0).toUpperCase() + word.slice(1)).join(' ')).join(', ');
272
+ }
273
+ constructor(){
274
+ _define_property(this, "logger", new _common.Logger('ExceptionFilter'));
275
+ }
276
+ };
277
+ GlobalExceptionFilter = _ts_decorate([
278
+ (0, _common.Catch)()
279
+ ], GlobalExceptionFilter);
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ _export_star(require("./global-exception.filter"), exports);
6
+ function _export_star(from, to) {
7
+ Object.keys(from).forEach(function(k) {
8
+ if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
9
+ Object.defineProperty(to, k, {
10
+ enumerable: true,
11
+ get: function() {
12
+ return from[k];
13
+ }
14
+ });
15
+ }
16
+ });
17
+ return from;
18
+ }
@@ -53,7 +53,10 @@ let JwtAuthGuard = class JwtAuthGuard extends (0, _passport.AuthGuard)('jwt') {
53
53
  }
54
54
  handleRequest(err, user) {
55
55
  if (err || !user) {
56
- throw err || new _common.UnauthorizedException('Invalid or expired token');
56
+ throw err || new _common.UnauthorizedException({
57
+ message: 'Invalid or expired token',
58
+ messageKey: _constants.AUTH_MESSAGES.TOKEN_INVALID
59
+ });
57
60
  }
58
61
  return user;
59
62
  }
@@ -11,10 +11,8 @@ Object.defineProperty(exports, "PermissionGuard", {
11
11
  const _common = require("@nestjs/common");
12
12
  const _core = require("@nestjs/core");
13
13
  const _hybridcacheclass = require("../classes/hybrid-cache.class");
14
- const _winstonloggeradapterclass = require("../classes/winston-logger-adapter.class");
15
14
  const _constants = require("../constants");
16
15
  const _permissionexception = require("../exceptions/permission.exception");
17
- const _loggerinterface = require("../interfaces/logger.interface");
18
16
  const _permissioninterface = require("../interfaces/permission.interface");
19
17
  function _define_property(obj, key, value) {
20
18
  if (key in obj) {
@@ -55,104 +53,105 @@ let PermissionGuard = class PermissionGuard {
55
53
  context.getClass()
56
54
  ]);
57
55
  if (!permissionConfig) return true;
58
- const { permissions: requiredPermissions, operator } = this.normalizePermissionConfig(permissionConfig);
59
- if (!requiredPermissions || requiredPermissions.length === 0) return true;
60
56
  const request = context.switchToHttp().getRequest();
61
57
  const user = request.user;
62
- if (!user) throw new _common.UnauthorizedException('Authentication required');
58
+ if (!user) throw new _common.UnauthorizedException({
59
+ message: 'Authentication required',
60
+ messageKey: _constants.AUTH_MESSAGES.TOKEN_REQUIRED
61
+ });
63
62
  if (!this.cache) {
64
- this.logger.error(`Cache not available (userId: ${user.id})`, undefined, 'PermissionGuard');
65
63
  throw new _permissionexception.PermissionSystemUnavailableException();
66
64
  }
67
65
  const userPermissions = await this.getUserPermissions(user);
68
66
  if (!userPermissions || userPermissions.length === 0) {
69
- this.logger.warn(`No permissions found (userId: ${user.id})`, 'PermissionGuard');
70
67
  throw new _permissionexception.NoPermissionsFoundException();
71
68
  }
72
- if (this.isNestedCondition(permissionConfig)) {
73
- const result = this.evaluateCondition(permissionConfig, userPermissions);
74
- if (!result.passed) {
75
- this.logger.warn(`Permission denied (userId: ${user.id})`, 'PermissionGuard');
76
- throw new _permissionexception.InsufficientPermissionsException(result.missingPermissions, result.operator);
77
- }
78
- } else {
79
- this.validateSimplePermissions(requiredPermissions, userPermissions, operator);
69
+ const logicNode = this.normalizeToLogicNode(permissionConfig);
70
+ if (!logicNode) return true;
71
+ const result = this.evaluateLogicNode(logicNode, userPermissions);
72
+ if (!result.passed) {
73
+ throw new _permissionexception.InsufficientPermissionsException(result.missingPermissions, result.operator);
80
74
  }
81
75
  return true;
82
76
  }
83
- normalizePermissionConfig(config) {
84
- if (Array.isArray(config)) return {
85
- permissions: config,
86
- operator: 'and'
87
- };
88
- return {
89
- permissions: config.permissions || [],
90
- operator: config.operator || 'and'
91
- };
92
- }
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
- }
77
+ normalizeToLogicNode(config) {
78
+ // string - single permission
79
+ if (typeof config === 'string') {
80
+ return config ? {
81
+ type: 'action',
82
+ actionId: config
83
+ } : null;
103
84
  }
85
+ // string[] - treat as AND of multiple permissions
86
+ if (Array.isArray(config)) {
87
+ if (config.length === 0) return null;
88
+ if (config.length === 1) return {
89
+ type: 'action',
90
+ actionId: config[0]
91
+ };
92
+ return {
93
+ type: 'group',
94
+ operator: 'AND',
95
+ children: config.map((p)=>({
96
+ type: 'action',
97
+ actionId: p
98
+ }))
99
+ };
100
+ }
101
+ // SimplePermissionConfig - { permissions: string[], operator: 'AND'|'OR' }
102
+ if ('permissions' in config && Array.isArray(config.permissions)) {
103
+ const simple = config;
104
+ if (simple.permissions.length === 0) return null;
105
+ if (simple.permissions.length === 1) return {
106
+ type: 'action',
107
+ actionId: simple.permissions[0]
108
+ };
109
+ return {
110
+ type: 'group',
111
+ operator: simple.operator || 'AND',
112
+ children: simple.permissions.map((p)=>({
113
+ type: 'action',
114
+ actionId: p
115
+ }))
116
+ };
117
+ }
118
+ // ILogicNode
119
+ return config;
104
120
  }
105
- isNestedCondition(config) {
106
- if (Array.isArray(config)) return false;
107
- return 'children' in config && Array.isArray(config.children) && config.children.length > 0;
108
- }
109
- evaluateCondition(condition, userPermissions) {
110
- const { permissions = [], operator, children = [] } = condition;
111
- // SECURITY: Fail-closed - deny access when no permissions configured (empty condition)
112
- if (permissions.length === 0 && children.length === 0) {
121
+ evaluateLogicNode(node, userPermissions) {
122
+ if (node.type === 'action') {
123
+ const passed = this.hasPermission(userPermissions, node.actionId);
124
+ return {
125
+ passed,
126
+ missingPermissions: passed ? [] : [
127
+ node.actionId
128
+ ],
129
+ operator: 'AND'
130
+ };
131
+ }
132
+ // Group node
133
+ const { operator, children } = node;
134
+ // SECURITY: Fail-closed - deny access when no children configured
135
+ if (!children || children.length === 0) {
113
136
  return {
114
137
  passed: false,
115
- message: 'No permissions configured - access denied by default',
116
138
  missingPermissions: [],
117
139
  operator
118
140
  };
119
141
  }
120
142
  const results = [];
121
- const failureDetails = [];
122
143
  const missingPermissions = [];
123
- if (permissions.length > 0) {
124
- if (operator === 'or') {
125
- const hasAny = permissions.some((p)=>this.hasPermission(userPermissions, p));
126
- results.push(hasAny);
127
- if (!hasAny) {
128
- failureDetails.push(`needs one of: [${permissions.join(', ')}]`);
129
- missingPermissions.push(...permissions);
130
- }
131
- } else {
132
- const hasAll = permissions.every((p)=>this.hasPermission(userPermissions, p));
133
- results.push(hasAll);
134
- if (!hasAll) {
135
- const missing = permissions.filter((p)=>!this.hasPermission(userPermissions, p));
136
- failureDetails.push(`missing: [${missing.join(', ')}]`);
137
- missingPermissions.push(...missing);
138
- }
139
- }
140
- }
141
144
  for (const child of children){
142
- const childResult = this.evaluateCondition(child, userPermissions);
145
+ const childResult = this.evaluateLogicNode(child, userPermissions);
143
146
  results.push(childResult.passed);
144
147
  if (!childResult.passed) {
145
- failureDetails.push(`(${childResult.message})`);
146
148
  missingPermissions.push(...childResult.missingPermissions);
147
149
  }
148
150
  }
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()} `)}`;
151
+ const passed = operator === 'OR' ? results.some((r)=>r) : results.every((r)=>r);
152
152
  return {
153
153
  passed,
154
- message,
155
- missingPermissions,
154
+ missingPermissions: passed ? [] : missingPermissions,
156
155
  operator
157
156
  };
158
157
  }
@@ -177,11 +176,10 @@ let PermissionGuard = class PermissionGuard {
177
176
  }
178
177
  return false;
179
178
  }
180
- constructor(reflector, cache, config, logger){
179
+ constructor(reflector, cache, config){
181
180
  _define_property(this, "reflector", void 0);
182
181
  _define_property(this, "cache", void 0);
183
182
  _define_property(this, "config", void 0);
184
- _define_property(this, "logger", void 0);
185
183
  this.reflector = reflector;
186
184
  this.cache = cache;
187
185
  this.config = {
@@ -190,7 +188,6 @@ let PermissionGuard = class PermissionGuard {
190
188
  companyPermissionKeyFormat: `${_constants.PERMISSIONS_CACHE_PREFIX}:company:{companyId}:branch:{branchId}:user:{userId}`,
191
189
  ...config
192
190
  };
193
- this.logger = logger || new _winstonloggeradapterclass.NestLoggerAdapter(new _common.Logger(PermissionGuard.name));
194
191
  }
195
192
  };
196
193
  PermissionGuard = _ts_decorate([
@@ -200,13 +197,10 @@ PermissionGuard = _ts_decorate([
200
197
  _ts_param(1, (0, _common.Inject)(_constants.CACHE_INSTANCE)),
201
198
  _ts_param(2, (0, _common.Optional)()),
202
199
  _ts_param(2, (0, _common.Inject)(_constants.PERMISSION_GUARD_CONFIG)),
203
- _ts_param(3, (0, _common.Optional)()),
204
- _ts_param(3, (0, _common.Inject)(_constants.LOGGER_INSTANCE)),
205
200
  _ts_metadata("design:type", Function),
206
201
  _ts_metadata("design:paramtypes", [
207
202
  typeof _core.Reflector === "undefined" ? Object : _core.Reflector,
208
203
  typeof _hybridcacheclass.HybridCache === "undefined" ? Object : _hybridcacheclass.HybridCache,
209
- typeof _permissioninterface.PermissionGuardConfig === "undefined" ? Object : _permissioninterface.PermissionGuardConfig,
210
- typeof _loggerinterface.ILogger === "undefined" ? Object : _loggerinterface.ILogger
204
+ typeof _permissioninterface.PermissionGuardConfig === "undefined" ? Object : _permissioninterface.PermissionGuardConfig
211
205
  ])
212
206
  ], PermissionGuard);
package/cjs/index.js CHANGED
@@ -7,7 +7,9 @@ _export_star(require("./constants"), exports);
7
7
  _export_star(require("./decorators"), exports);
8
8
  _export_star(require("./dtos"), exports);
9
9
  _export_star(require("./entities"), exports);
10
+ _export_star(require("./enums"), exports);
10
11
  _export_star(require("./exceptions"), exports);
12
+ _export_star(require("./filters"), exports);
11
13
  _export_star(require("./guards"), exports);
12
14
  _export_star(require("./interceptors"), exports);
13
15
  _export_star(require("./interfaces"), exports);
@@ -61,7 +61,7 @@ let IdempotencyInterceptor = class IdempotencyInterceptor {
61
61
  throw new _common.ConflictException({
62
62
  success: false,
63
63
  message: 'Request is already being processed',
64
- code: 'DUPLICATE_REQUEST_IN_PROGRESS'
64
+ messageKey: _constants.SYSTEM_MESSAGES.DUPLICATE_REQUEST
65
65
  });
66
66
  }
67
67
  // Return cached response
@@ -4,7 +4,6 @@ Object.defineProperty(exports, "__esModule", {
4
4
  });
5
5
  _export_star(require("./delete-empty-id-from-body.interceptor"), exports);
6
6
  _export_star(require("./idempotency.interceptor"), exports);
7
- _export_star(require("./query-performance.interceptor"), exports);
8
7
  _export_star(require("./response-meta.interceptor"), exports);
9
8
  _export_star(require("./set-user-field-on-body.interceptor"), exports);
10
9
  _export_star(require("./slug.interceptor"), exports);
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "EVENT_MANAGER_ADAPTER", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return EVENT_MANAGER_ADAPTER;
9
+ }
10
+ });
11
+ const EVENT_MANAGER_ADAPTER = 'EVENT_MANAGER_ADAPTER';
@@ -4,10 +4,11 @@ Object.defineProperty(exports, "__esModule", {
4
4
  });
5
5
  _export_star(require("./api.interface"), exports);
6
6
  _export_star(require("./datasource.interface"), exports);
7
+ _export_star(require("./event-manager-adapter.interface"), exports);
7
8
  _export_star(require("./identity.interface"), exports);
8
9
  _export_star(require("./logged-user-info.interface"), exports);
9
- _export_star(require("./logger.interface"), exports);
10
10
  _export_star(require("./module-config.interface"), exports);
11
+ _export_star(require("./notification-adapter.interface"), exports);
11
12
  _export_star(require("./permission.interface"), exports);
12
13
  function _export_star(from, to) {
13
14
  Object.keys(from).forEach(function(k) {
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "NOTIFICATION_ADAPTER", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return NOTIFICATION_ADAPTER;
9
+ }
10
+ });
11
+ const NOTIFICATION_ADAPTER = 'NOTIFICATION_ADAPTER';
@@ -1,4 +1,4 @@
1
- "use strict";
1
+ /** Action node - checks a single permission/action code */ "use strict";
2
2
  Object.defineProperty(exports, "__esModule", {
3
3
  value: true
4
4
  });