@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
@@ -25,14 +25,16 @@ function _ts_param(paramIndex, decorator) {
25
25
  decorator(target, key, paramIndex);
26
26
  };
27
27
  }
28
- import { CurrentUser, Public, RequirePermission, RequireAnyPermission, RequirePermissionCondition } from '../decorators';
29
- import { DeleteDto, FilterAndPaginationDto, GetByIdBodyDto } from '../dtos';
30
- import { JwtAuthGuard, PermissionGuard } from '../guards';
31
- import { IdempotencyInterceptor, SetCreatedByOnBody, SetDeletedByOnBody, SetUpdateByOnBody, Slug } from '../interceptors';
32
28
  import { applyDecorators, Body, HttpCode, HttpStatus, Param, Post, Query, UseGuards, UseInterceptors, Version, VERSION_NEUTRAL } from '@nestjs/common';
33
- import { ApiBearerAuth, ApiBody, ApiHeader, ApiOperation, ApiParam, ApiQuery, ApiResponse } from '@nestjs/swagger';
29
+ import { ApiBearerAuth, ApiBody, ApiExcludeEndpoint, ApiHeader, ApiOperation, ApiParam, ApiQuery, ApiResponse } from '@nestjs/swagger';
34
30
  import { plainToInstance } from 'class-transformer';
31
+ import { ERROR_MESSAGES } from '../constants/message-keys';
32
+ import { CurrentUser, Public, RequireAnyPermission, RequirePermission, RequirePermissionLogic } from '../decorators';
35
33
  import { ApiResponseDto } from '../decorators/api-response.decorator';
34
+ import { DeleteDto, FilterAndPaginationDto, GetByIdBodyDto, GetByIdsDto } from '../dtos';
35
+ import { BaseAppException, NotFoundException } from '../exceptions';
36
+ import { JwtAuthGuard, PermissionGuard } from '../guards';
37
+ import { IdempotencyInterceptor, SetCreatedByOnBody, SetDeletedByOnBody, SetUpdateByOnBody, Slug } from '../interceptors';
36
38
  /**
37
39
  * Helper to normalize security config
38
40
  */ function normalizeSecurity(config) {
@@ -44,6 +46,24 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
44
46
  };
45
47
  return config;
46
48
  }
49
+ /**
50
+ * Helper to check if endpoint is enabled
51
+ */ function isEndpointEnabled(endpoint, enabledEndpoints) {
52
+ if (!enabledEndpoints || enabledEndpoints === 'all') return true;
53
+ return enabledEndpoints.includes(endpoint);
54
+ }
55
+ /**
56
+ * Helper to conditionally exclude endpoint from Swagger if disabled
57
+ */ function createEndpointDecorators(security, endpoint, enabledEndpoints) {
58
+ const decorators = [];
59
+ // Add security decorators
60
+ decorators.push(createSecurityDecorators(security));
61
+ // Exclude from Swagger if endpoint is disabled
62
+ if (!isEndpointEnabled(endpoint, enabledEndpoints)) {
63
+ decorators.push(ApiExcludeEndpoint());
64
+ }
65
+ return applyDecorators(...decorators);
66
+ }
47
67
  /**
48
68
  * Creates security decorators based on configuration
49
69
  */ function createSecurityDecorators(security) {
@@ -62,11 +82,11 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
62
82
  // Apply PermissionGuard to check permissions from cache
63
83
  decorators.push(UseGuards(JwtAuthGuard, PermissionGuard));
64
84
  decorators.push(ApiBearerAuth());
65
- // Check for complex nested condition first
66
- if (security.condition) {
67
- decorators.push(RequirePermissionCondition(security.condition));
85
+ // Check for complex logic first
86
+ if (security.logic) {
87
+ decorators.push(RequirePermissionLogic(security.logic));
68
88
  } else if (security.permissions && security.permissions.length > 0) {
69
- if (security.operator === 'or') {
89
+ if (security.operator === 'OR') {
70
90
  decorators.push(RequireAnyPermission(...security.permissions));
71
91
  } else {
72
92
  // AND is default and most secure
@@ -78,13 +98,24 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
78
98
  return applyDecorators(...decorators);
79
99
  }
80
100
  /** Creates an API Controller with standardized CRUD endpoints (POST-only RPC pattern) */ export function createApiController(createDtoClass, updateDtoClass, responseDtoClass, options = {}) {
101
+ const excludeExtraneousValues = options.excludeExtraneousValues ?? true;
102
+ function toDto(cls, input) {
103
+ return plainToInstance(cls, input, {
104
+ excludeExtraneousValues
105
+ });
106
+ }
107
+ // Entity name for message keys (defaults to 'item')
108
+ const entityName = options.entityName ?? 'item';
81
109
  // Determine if security is global (applies to all) or per-endpoint
82
110
  const securityConfig = options.security;
83
111
  const endpointKeys = [
84
112
  'insert',
85
113
  'insertMany',
86
114
  'getById',
115
+ 'getByIds',
87
116
  'getAll',
117
+ 'bulkUpsert',
118
+ 'getByFilter',
88
119
  'update',
89
120
  'updateMany',
90
121
  'delete'
@@ -103,27 +134,59 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
103
134
  insert: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.insert),
104
135
  insertMany: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.insertMany),
105
136
  getById: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.getById),
137
+ getByIds: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.getByIds),
106
138
  getAll: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.getAll),
139
+ getByFilter: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.getByFilter),
140
+ bulkUpsert: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.bulkUpsert),
107
141
  update: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.update),
108
142
  updateMany: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.updateMany),
109
143
  delete: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.delete)
110
144
  };
111
145
  let ApiController = class ApiController {
146
+ isEnabled(endpoint) {
147
+ if (this.enabledEndpoints === 'all') return true;
148
+ return Array.isArray(this.enabledEndpoints) && this.enabledEndpoints.includes(endpoint);
149
+ }
112
150
  async insert(addDto, user) {
151
+ if (!this.isEnabled('insert')) {
152
+ throw new BaseAppException({
153
+ message: `Endpoint 'insert' is disabled`,
154
+ messageKey: ERROR_MESSAGES.ENDPOINT_DISABLED,
155
+ messageVariables: {
156
+ endpoint: 'insert'
157
+ },
158
+ status: HttpStatus.FORBIDDEN
159
+ });
160
+ }
113
161
  const entity = await this.service.insert(addDto, user);
114
- const data = plainToInstance(responseDtoClass, entity);
162
+ const data = toDto(responseDtoClass, entity);
115
163
  return {
116
164
  success: true,
117
- message: 'Item created successfully',
165
+ message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)} created successfully`,
166
+ messageKey: `${entityName}.create.success`,
118
167
  data
119
168
  };
120
169
  }
121
170
  async insertMany(addDto, user) {
171
+ if (!this.isEnabled('insertMany')) {
172
+ throw new BaseAppException({
173
+ message: `Endpoint 'insertMany' is disabled`,
174
+ messageKey: ERROR_MESSAGES.ENDPOINT_DISABLED,
175
+ messageVariables: {
176
+ endpoint: 'insertMany'
177
+ },
178
+ status: HttpStatus.FORBIDDEN
179
+ });
180
+ }
122
181
  const entities = await this.service.insertMany(addDto, user);
123
- const data = entities.map((item)=>plainToInstance(responseDtoClass, item));
182
+ const data = entities.map((item)=>toDto(responseDtoClass, item));
124
183
  return {
125
184
  success: true,
126
- message: `${data.length} items created successfully`,
185
+ message: `${data.length} ${entityName}s created successfully`,
186
+ messageKey: `${entityName}.create.many.success`,
187
+ messageVariables: {
188
+ count: data.length
189
+ },
127
190
  data,
128
191
  meta: {
129
192
  count: data.length,
@@ -133,29 +196,92 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
133
196
  };
134
197
  }
135
198
  async getById(id, body, user) {
199
+ if (!this.isEnabled('getById')) {
200
+ throw new BaseAppException({
201
+ message: `Endpoint 'getById' is disabled`,
202
+ messageKey: ERROR_MESSAGES.ENDPOINT_DISABLED,
203
+ messageVariables: {
204
+ endpoint: 'getById'
205
+ },
206
+ status: HttpStatus.FORBIDDEN
207
+ });
208
+ }
136
209
  const entity = await this.service.findById(id, user, body?.select);
137
- const data = plainToInstance(responseDtoClass, entity);
210
+ const data = toDto(responseDtoClass, entity);
138
211
  return {
139
212
  success: true,
140
- message: 'Item retrieved successfully',
213
+ message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)} retrieved successfully`,
214
+ messageKey: `${entityName}.get.success`,
141
215
  data
142
216
  };
143
217
  }
218
+ async getByIds(body, user) {
219
+ if (!this.isEnabled('getByIds')) {
220
+ throw new BaseAppException({
221
+ message: `Endpoint 'getByIds' is disabled`,
222
+ messageKey: ERROR_MESSAGES.ENDPOINT_DISABLED,
223
+ messageVariables: {
224
+ endpoint: 'getByIds'
225
+ },
226
+ status: HttpStatus.FORBIDDEN
227
+ });
228
+ }
229
+ const entities = await this.service.findByIds(body.ids, user, body?.select);
230
+ const data = entities.map((item)=>toDto(responseDtoClass, item));
231
+ return {
232
+ success: true,
233
+ message: `${data.length} ${entityName}${data.length !== 1 ? 's' : ''} retrieved successfully`,
234
+ messageKey: `${entityName}.get.by.ids.success`,
235
+ data,
236
+ meta: {
237
+ total: data.length,
238
+ page: 0,
239
+ pageSize: data.length,
240
+ count: data.length,
241
+ totalPages: 1
242
+ }
243
+ };
244
+ }
144
245
  async update(updateDto, user) {
246
+ if (!this.isEnabled('update')) {
247
+ throw new BaseAppException({
248
+ message: `Endpoint 'update' is disabled`,
249
+ messageKey: ERROR_MESSAGES.ENDPOINT_DISABLED,
250
+ messageVariables: {
251
+ endpoint: 'update'
252
+ },
253
+ status: HttpStatus.FORBIDDEN
254
+ });
255
+ }
145
256
  const entity = await this.service.update(updateDto, user);
146
- const data = plainToInstance(responseDtoClass, entity);
257
+ const data = toDto(responseDtoClass, entity);
147
258
  return {
148
259
  success: true,
149
- message: 'Item updated successfully',
260
+ message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)} updated successfully`,
261
+ messageKey: `${entityName}.update.success`,
150
262
  data
151
263
  };
152
264
  }
153
265
  async updateMany(updateDtos, user) {
266
+ if (!this.isEnabled('updateMany')) {
267
+ throw new BaseAppException({
268
+ message: `Endpoint 'updateMany' is disabled`,
269
+ messageKey: ERROR_MESSAGES.ENDPOINT_DISABLED,
270
+ messageVariables: {
271
+ endpoint: 'updateMany'
272
+ },
273
+ status: HttpStatus.FORBIDDEN
274
+ });
275
+ }
154
276
  const entities = await this.service.updateMany(updateDtos, user);
155
- const data = plainToInstance(responseDtoClass, entities);
277
+ const data = entities.map((item)=>toDto(responseDtoClass, item));
156
278
  return {
157
279
  success: true,
158
- message: `${data.length} items updated successfully`,
280
+ message: `${data.length} ${entityName}s updated successfully`,
281
+ messageKey: `${entityName}.update.many.success`,
282
+ messageVariables: {
283
+ count: data.length
284
+ },
159
285
  data,
160
286
  meta: {
161
287
  count: data.length,
@@ -164,15 +290,93 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
164
290
  }
165
291
  };
166
292
  }
293
+ async bulkUpsert(dtos, user) {
294
+ if (!this.isEnabled('bulkUpsert')) {
295
+ throw new BaseAppException({
296
+ message: `Endpoint 'bulkUpsert' is disabled`,
297
+ messageKey: ERROR_MESSAGES.ENDPOINT_DISABLED,
298
+ messageVariables: {
299
+ endpoint: 'bulkUpsert'
300
+ },
301
+ status: HttpStatus.FORBIDDEN
302
+ });
303
+ }
304
+ const toInsert = [];
305
+ const toUpdate = [];
306
+ for (const dto of dtos){
307
+ if ('id' in dto && dto.id) {
308
+ toUpdate.push(dto);
309
+ } else {
310
+ toInsert.push(dto);
311
+ }
312
+ }
313
+ const allEntities = await this.service.bulkUpsert(toInsert, toUpdate, user);
314
+ const data = allEntities.map((item)=>toDto(responseDtoClass, item));
315
+ return {
316
+ success: true,
317
+ message: `${data.length} ${entityName}s upserted successfully`,
318
+ messageKey: `${entityName}.upsert.many.success`,
319
+ messageVariables: {
320
+ count: data.length
321
+ },
322
+ data,
323
+ meta: {
324
+ count: data.length,
325
+ total: dtos.length,
326
+ failed: dtos.length - data.length
327
+ }
328
+ };
329
+ }
330
+ async getByFilter(filter, user) {
331
+ if (!this.isEnabled('getByFilter')) {
332
+ throw new BaseAppException({
333
+ message: `Endpoint 'getByFilter' is disabled`,
334
+ messageKey: ERROR_MESSAGES.ENDPOINT_DISABLED,
335
+ messageVariables: {
336
+ endpoint: 'getByFilter'
337
+ },
338
+ status: HttpStatus.FORBIDDEN
339
+ });
340
+ }
341
+ const result = await this.service.getAll('', {
342
+ filter,
343
+ pagination: {
344
+ currentPage: 0,
345
+ pageSize: 1
346
+ }
347
+ }, user);
348
+ if (!result.data || result.data.length === 0) {
349
+ throw new NotFoundException(entityName);
350
+ }
351
+ const entity = result.data[0];
352
+ const data = toDto(responseDtoClass, entity);
353
+ return {
354
+ success: true,
355
+ message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)} retrieved successfully`,
356
+ messageKey: `${entityName}.get.by.filter.success`,
357
+ data
358
+ };
359
+ }
167
360
  async getAll(filterAndPaginationDto, user, search) {
361
+ if (!this.isEnabled('getAll')) {
362
+ throw new BaseAppException({
363
+ message: `Endpoint 'getAll' is disabled`,
364
+ messageKey: ERROR_MESSAGES.ENDPOINT_DISABLED,
365
+ messageVariables: {
366
+ endpoint: 'getAll'
367
+ },
368
+ status: HttpStatus.FORBIDDEN
369
+ });
370
+ }
168
371
  const result = await this.service.getAll(search ?? '', filterAndPaginationDto, user);
169
- const data = plainToInstance(responseDtoClass, result.data);
372
+ const data = result.data.map((item)=>toDto(responseDtoClass, item));
170
373
  const page = filterAndPaginationDto.pagination?.currentPage ?? 0;
171
374
  const pageSize = filterAndPaginationDto.pagination?.pageSize ?? data.length;
172
375
  const totalPages = pageSize > 0 ? Math.ceil(result.total / pageSize) : 1;
173
376
  return {
174
377
  success: true,
175
- message: 'Data retrieved successfully',
378
+ message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)}s retrieved successfully`,
379
+ messageKey: `${entityName}.get.all.success`,
176
380
  data,
177
381
  meta: {
178
382
  total: result.total,
@@ -185,21 +389,37 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
185
389
  };
186
390
  }
187
391
  async delete(deleteDto, user) {
392
+ if (!this.isEnabled('delete')) {
393
+ throw new BaseAppException({
394
+ message: `Endpoint 'delete' is disabled`,
395
+ messageKey: ERROR_MESSAGES.ENDPOINT_DISABLED,
396
+ messageVariables: {
397
+ endpoint: 'delete'
398
+ },
399
+ status: HttpStatus.FORBIDDEN
400
+ });
401
+ }
188
402
  await this.service.delete(deleteDto, user);
189
403
  const count = Array.isArray(deleteDto.id) ? deleteDto.id.length : 1;
190
- const action = deleteDto.type === 'restore' ? 'restored' : 'deleted';
404
+ const action = deleteDto.type === 'restore' ? 'restore' : 'delete';
191
405
  return {
192
406
  success: true,
193
- message: `${count} item${count > 1 ? 's' : ''} ${action} successfully`
407
+ message: `${count} ${entityName}${count > 1 ? 's' : ''} ${action}d successfully`,
408
+ messageKey: `${entityName}.${action}.success`,
409
+ messageVariables: {
410
+ count
411
+ }
194
412
  };
195
413
  }
196
414
  constructor(service){
197
415
  _define_property(this, "service", void 0);
416
+ _define_property(this, "enabledEndpoints", void 0);
198
417
  this.service = service;
418
+ this.enabledEndpoints = options.enabledEndpoints ?? 'all';
199
419
  }
200
420
  };
201
421
  _ts_decorate([
202
- createSecurityDecorators(security.insert),
422
+ createEndpointDecorators(security.insert, 'insert', options.enabledEndpoints),
203
423
  Version(VERSION_NEUTRAL),
204
424
  Post('insert'),
205
425
  HttpCode(HttpStatus.CREATED),
@@ -227,7 +447,7 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
227
447
  _ts_metadata("design:returntype", Promise)
228
448
  ], ApiController.prototype, "insert", null);
229
449
  _ts_decorate([
230
- createSecurityDecorators(security.insertMany),
450
+ createEndpointDecorators(security.insertMany, 'insertMany', options.enabledEndpoints),
231
451
  Version(VERSION_NEUTRAL),
232
452
  Post('insert-many'),
233
453
  HttpCode(HttpStatus.CREATED),
@@ -256,7 +476,7 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
256
476
  _ts_metadata("design:returntype", Promise)
257
477
  ], ApiController.prototype, "insertMany", null);
258
478
  _ts_decorate([
259
- createSecurityDecorators(security.getById),
479
+ createEndpointDecorators(security.getById, 'getById', options.enabledEndpoints),
260
480
  Version(VERSION_NEUTRAL),
261
481
  Post('get/:id'),
262
482
  HttpCode(HttpStatus.OK),
@@ -286,7 +506,29 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
286
506
  _ts_metadata("design:returntype", Promise)
287
507
  ], ApiController.prototype, "getById", null);
288
508
  _ts_decorate([
289
- createSecurityDecorators(security.update),
509
+ createEndpointDecorators(security.getByIds, 'getByIds', options.enabledEndpoints),
510
+ Version(VERSION_NEUTRAL),
511
+ Post('get-by-ids'),
512
+ HttpCode(HttpStatus.OK),
513
+ ApiOperation({
514
+ summary: 'Get items by IDs',
515
+ description: 'Retrieves multiple items by their IDs. Optionally specify fields to select.'
516
+ }),
517
+ ApiBody({
518
+ type: GetByIdsDto
519
+ }),
520
+ ApiResponseDto(responseDtoClass, true, 'list'),
521
+ _ts_param(0, Body()),
522
+ _ts_param(1, CurrentUser()),
523
+ _ts_metadata("design:type", Function),
524
+ _ts_metadata("design:paramtypes", [
525
+ typeof GetByIdsDto === "undefined" ? Object : GetByIdsDto,
526
+ Object
527
+ ]),
528
+ _ts_metadata("design:returntype", Promise)
529
+ ], ApiController.prototype, "getByIds", null);
530
+ _ts_decorate([
531
+ createEndpointDecorators(security.update, 'update', options.enabledEndpoints),
290
532
  Version(VERSION_NEUTRAL),
291
533
  Post('update'),
292
534
  HttpCode(HttpStatus.OK),
@@ -309,7 +551,7 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
309
551
  _ts_metadata("design:returntype", Promise)
310
552
  ], ApiController.prototype, "update", null);
311
553
  _ts_decorate([
312
- createSecurityDecorators(security.updateMany),
554
+ createEndpointDecorators(security.updateMany, 'updateMany', options.enabledEndpoints),
313
555
  Version(VERSION_NEUTRAL),
314
556
  Post('update-many'),
315
557
  HttpCode(HttpStatus.OK),
@@ -333,7 +575,56 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
333
575
  _ts_metadata("design:returntype", Promise)
334
576
  ], ApiController.prototype, "updateMany", null);
335
577
  _ts_decorate([
336
- createSecurityDecorators(security.getAll),
578
+ createEndpointDecorators(security.bulkUpsert, 'bulkUpsert', options.enabledEndpoints),
579
+ Version(VERSION_NEUTRAL),
580
+ Post('bulk-upsert'),
581
+ HttpCode(HttpStatus.OK),
582
+ ApiOperation({
583
+ summary: 'Create or update multiple items',
584
+ description: 'Bulk upsert: items without ID are created, items with ID are updated.'
585
+ }),
586
+ ApiResponseDto(responseDtoClass, true, 'bulk'),
587
+ ApiBody({
588
+ type: createDtoClass,
589
+ isArray: true
590
+ }),
591
+ UseInterceptors(SetCreatedByOnBody, SetUpdateByOnBody, Slug),
592
+ _ts_param(0, Body()),
593
+ _ts_param(1, CurrentUser()),
594
+ _ts_metadata("design:type", Function),
595
+ _ts_metadata("design:paramtypes", [
596
+ Array,
597
+ Object
598
+ ]),
599
+ _ts_metadata("design:returntype", Promise)
600
+ ], ApiController.prototype, "bulkUpsert", null);
601
+ _ts_decorate([
602
+ createEndpointDecorators(security.getByFilter, 'getByFilter', options.enabledEndpoints),
603
+ Version(VERSION_NEUTRAL),
604
+ Post('get-by-filter'),
605
+ HttpCode(HttpStatus.OK),
606
+ ApiOperation({
607
+ summary: 'Get single item by filter',
608
+ description: 'Retrieves a single item matching the provided filter criteria. Returns first match if multiple exist.'
609
+ }),
610
+ ApiBody({
611
+ schema: {
612
+ type: 'object',
613
+ description: 'Filter criteria'
614
+ }
615
+ }),
616
+ ApiResponseDto(responseDtoClass),
617
+ _ts_param(0, Body()),
618
+ _ts_param(1, CurrentUser()),
619
+ _ts_metadata("design:type", Function),
620
+ _ts_metadata("design:paramtypes", [
621
+ typeof Record === "undefined" ? Object : Record,
622
+ Object
623
+ ]),
624
+ _ts_metadata("design:returntype", Promise)
625
+ ], ApiController.prototype, "getByFilter", null);
626
+ _ts_decorate([
627
+ createEndpointDecorators(security.getAll, 'getAll', options.enabledEndpoints),
337
628
  Version(VERSION_NEUTRAL),
338
629
  Post('get-all'),
339
630
  HttpCode(HttpStatus.OK),
@@ -362,7 +653,7 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
362
653
  _ts_metadata("design:returntype", Promise)
363
654
  ], ApiController.prototype, "getAll", null);
364
655
  _ts_decorate([
365
- createSecurityDecorators(security.delete),
656
+ createEndpointDecorators(security.delete, 'delete', options.enabledEndpoints),
366
657
  Version(VERSION_NEUTRAL),
367
658
  Post('delete'),
368
659
  HttpCode(HttpStatus.OK),