@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
@@ -8,14 +8,16 @@ Object.defineProperty(exports, "createApiController", {
8
8
  return createApiController;
9
9
  }
10
10
  });
11
- const _decorators = require("@flusys/nestjs-shared/decorators");
12
- const _dtos = require("@flusys/nestjs-shared/dtos");
13
- const _guards = require("@flusys/nestjs-shared/guards");
14
- const _interceptors = require("@flusys/nestjs-shared/interceptors");
15
11
  const _common = require("@nestjs/common");
16
12
  const _swagger = require("@nestjs/swagger");
17
13
  const _classtransformer = require("class-transformer");
14
+ const _messagekeys = require("../constants/message-keys");
15
+ const _decorators = require("../decorators");
18
16
  const _apiresponsedecorator = require("../decorators/api-response.decorator");
17
+ const _dtos = require("../dtos");
18
+ const _exceptions = require("../exceptions");
19
+ const _guards = require("../guards");
20
+ const _interceptors = require("../interceptors");
19
21
  function _define_property(obj, key, value) {
20
22
  if (key in obj) {
21
23
  Object.defineProperty(obj, key, {
@@ -54,6 +56,24 @@ function _ts_param(paramIndex, decorator) {
54
56
  };
55
57
  return config;
56
58
  }
59
+ /**
60
+ * Helper to check if endpoint is enabled
61
+ */ function isEndpointEnabled(endpoint, enabledEndpoints) {
62
+ if (!enabledEndpoints || enabledEndpoints === 'all') return true;
63
+ return enabledEndpoints.includes(endpoint);
64
+ }
65
+ /**
66
+ * Helper to conditionally exclude endpoint from Swagger if disabled
67
+ */ function createEndpointDecorators(security, endpoint, enabledEndpoints) {
68
+ const decorators = [];
69
+ // Add security decorators
70
+ decorators.push(createSecurityDecorators(security));
71
+ // Exclude from Swagger if endpoint is disabled
72
+ if (!isEndpointEnabled(endpoint, enabledEndpoints)) {
73
+ decorators.push((0, _swagger.ApiExcludeEndpoint)());
74
+ }
75
+ return (0, _common.applyDecorators)(...decorators);
76
+ }
57
77
  /**
58
78
  * Creates security decorators based on configuration
59
79
  */ function createSecurityDecorators(security) {
@@ -72,11 +92,11 @@ function _ts_param(paramIndex, decorator) {
72
92
  // Apply PermissionGuard to check permissions from cache
73
93
  decorators.push((0, _common.UseGuards)(_guards.JwtAuthGuard, _guards.PermissionGuard));
74
94
  decorators.push((0, _swagger.ApiBearerAuth)());
75
- // Check for complex nested condition first
76
- if (security.condition) {
77
- decorators.push((0, _decorators.RequirePermissionCondition)(security.condition));
95
+ // Check for complex logic first
96
+ if (security.logic) {
97
+ decorators.push((0, _decorators.RequirePermissionLogic)(security.logic));
78
98
  } else if (security.permissions && security.permissions.length > 0) {
79
- if (security.operator === 'or') {
99
+ if (security.operator === 'OR') {
80
100
  decorators.push((0, _decorators.RequireAnyPermission)(...security.permissions));
81
101
  } else {
82
102
  // AND is default and most secure
@@ -88,13 +108,24 @@ function _ts_param(paramIndex, decorator) {
88
108
  return (0, _common.applyDecorators)(...decorators);
89
109
  }
90
110
  function createApiController(createDtoClass, updateDtoClass, responseDtoClass, options = {}) {
111
+ const excludeExtraneousValues = options.excludeExtraneousValues ?? true;
112
+ function toDto(cls, input) {
113
+ return (0, _classtransformer.plainToInstance)(cls, input, {
114
+ excludeExtraneousValues
115
+ });
116
+ }
117
+ // Entity name for message keys (defaults to 'item')
118
+ const entityName = options.entityName ?? 'item';
91
119
  // Determine if security is global (applies to all) or per-endpoint
92
120
  const securityConfig = options.security;
93
121
  const endpointKeys = [
94
122
  'insert',
95
123
  'insertMany',
96
124
  'getById',
125
+ 'getByIds',
97
126
  'getAll',
127
+ 'bulkUpsert',
128
+ 'getByFilter',
98
129
  'update',
99
130
  'updateMany',
100
131
  'delete'
@@ -104,34 +135,68 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
104
135
  // 2. It's an object with 'level' property but no endpoint keys
105
136
  const isGlobalSecurity = typeof securityConfig === 'string' || securityConfig && typeof securityConfig === 'object' && 'level' in securityConfig && !endpointKeys.some((key)=>key in securityConfig);
106
137
  // Normalize security config for each endpoint
138
+ // IMPORTANT: When per-endpoint security is specified, default to 'jwt' for unconfigured endpoints
139
+ // to prevent accidentally exposing endpoints without authentication
107
140
  const defaultSecurity = isGlobalSecurity ? normalizeSecurity(securityConfig) : {
108
- level: 'public'
141
+ level: 'jwt'
109
142
  };
110
143
  const security = {
111
144
  insert: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.insert),
112
145
  insertMany: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.insertMany),
113
146
  getById: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.getById),
147
+ getByIds: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.getByIds),
114
148
  getAll: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.getAll),
149
+ getByFilter: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.getByFilter),
150
+ bulkUpsert: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.bulkUpsert),
115
151
  update: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.update),
116
152
  updateMany: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.updateMany),
117
153
  delete: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.delete)
118
154
  };
119
155
  let ApiController = class ApiController {
156
+ isEnabled(endpoint) {
157
+ if (this.enabledEndpoints === 'all') return true;
158
+ return Array.isArray(this.enabledEndpoints) && this.enabledEndpoints.includes(endpoint);
159
+ }
120
160
  async insert(addDto, user) {
161
+ if (!this.isEnabled('insert')) {
162
+ throw new _exceptions.BaseAppException({
163
+ message: `Endpoint 'insert' is disabled`,
164
+ messageKey: _messagekeys.ERROR_MESSAGES.ENDPOINT_DISABLED,
165
+ messageVariables: {
166
+ endpoint: 'insert'
167
+ },
168
+ status: _common.HttpStatus.FORBIDDEN
169
+ });
170
+ }
121
171
  const entity = await this.service.insert(addDto, user);
122
- const data = (0, _classtransformer.plainToInstance)(responseDtoClass, entity);
172
+ const data = toDto(responseDtoClass, entity);
123
173
  return {
124
174
  success: true,
125
- message: 'Item created successfully',
175
+ message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)} created successfully`,
176
+ messageKey: `${entityName}.create.success`,
126
177
  data
127
178
  };
128
179
  }
129
180
  async insertMany(addDto, user) {
181
+ if (!this.isEnabled('insertMany')) {
182
+ throw new _exceptions.BaseAppException({
183
+ message: `Endpoint 'insertMany' is disabled`,
184
+ messageKey: _messagekeys.ERROR_MESSAGES.ENDPOINT_DISABLED,
185
+ messageVariables: {
186
+ endpoint: 'insertMany'
187
+ },
188
+ status: _common.HttpStatus.FORBIDDEN
189
+ });
190
+ }
130
191
  const entities = await this.service.insertMany(addDto, user);
131
- const data = entities.map((item)=>(0, _classtransformer.plainToInstance)(responseDtoClass, item));
192
+ const data = entities.map((item)=>toDto(responseDtoClass, item));
132
193
  return {
133
194
  success: true,
134
- message: `${data.length} items created successfully`,
195
+ message: `${data.length} ${entityName}s created successfully`,
196
+ messageKey: `${entityName}.create.many.success`,
197
+ messageVariables: {
198
+ count: data.length
199
+ },
135
200
  data,
136
201
  meta: {
137
202
  count: data.length,
@@ -141,29 +206,92 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
141
206
  };
142
207
  }
143
208
  async getById(id, body, user) {
209
+ if (!this.isEnabled('getById')) {
210
+ throw new _exceptions.BaseAppException({
211
+ message: `Endpoint 'getById' is disabled`,
212
+ messageKey: _messagekeys.ERROR_MESSAGES.ENDPOINT_DISABLED,
213
+ messageVariables: {
214
+ endpoint: 'getById'
215
+ },
216
+ status: _common.HttpStatus.FORBIDDEN
217
+ });
218
+ }
144
219
  const entity = await this.service.findById(id, user, body?.select);
145
- const data = (0, _classtransformer.plainToInstance)(responseDtoClass, entity);
220
+ const data = toDto(responseDtoClass, entity);
146
221
  return {
147
222
  success: true,
148
- message: 'Item retrieved successfully',
223
+ message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)} retrieved successfully`,
224
+ messageKey: `${entityName}.get.success`,
149
225
  data
150
226
  };
151
227
  }
228
+ async getByIds(body, user) {
229
+ if (!this.isEnabled('getByIds')) {
230
+ throw new _exceptions.BaseAppException({
231
+ message: `Endpoint 'getByIds' is disabled`,
232
+ messageKey: _messagekeys.ERROR_MESSAGES.ENDPOINT_DISABLED,
233
+ messageVariables: {
234
+ endpoint: 'getByIds'
235
+ },
236
+ status: _common.HttpStatus.FORBIDDEN
237
+ });
238
+ }
239
+ const entities = await this.service.findByIds(body.ids, user, body?.select);
240
+ const data = entities.map((item)=>toDto(responseDtoClass, item));
241
+ return {
242
+ success: true,
243
+ message: `${data.length} ${entityName}${data.length !== 1 ? 's' : ''} retrieved successfully`,
244
+ messageKey: `${entityName}.get.by.ids.success`,
245
+ data,
246
+ meta: {
247
+ total: data.length,
248
+ page: 0,
249
+ pageSize: data.length,
250
+ count: data.length,
251
+ totalPages: 1
252
+ }
253
+ };
254
+ }
152
255
  async update(updateDto, user) {
256
+ if (!this.isEnabled('update')) {
257
+ throw new _exceptions.BaseAppException({
258
+ message: `Endpoint 'update' is disabled`,
259
+ messageKey: _messagekeys.ERROR_MESSAGES.ENDPOINT_DISABLED,
260
+ messageVariables: {
261
+ endpoint: 'update'
262
+ },
263
+ status: _common.HttpStatus.FORBIDDEN
264
+ });
265
+ }
153
266
  const entity = await this.service.update(updateDto, user);
154
- const data = (0, _classtransformer.plainToInstance)(responseDtoClass, entity);
267
+ const data = toDto(responseDtoClass, entity);
155
268
  return {
156
269
  success: true,
157
- message: 'Item updated successfully',
270
+ message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)} updated successfully`,
271
+ messageKey: `${entityName}.update.success`,
158
272
  data
159
273
  };
160
274
  }
161
275
  async updateMany(updateDtos, user) {
276
+ if (!this.isEnabled('updateMany')) {
277
+ throw new _exceptions.BaseAppException({
278
+ message: `Endpoint 'updateMany' is disabled`,
279
+ messageKey: _messagekeys.ERROR_MESSAGES.ENDPOINT_DISABLED,
280
+ messageVariables: {
281
+ endpoint: 'updateMany'
282
+ },
283
+ status: _common.HttpStatus.FORBIDDEN
284
+ });
285
+ }
162
286
  const entities = await this.service.updateMany(updateDtos, user);
163
- const data = (0, _classtransformer.plainToInstance)(responseDtoClass, entities);
287
+ const data = entities.map((item)=>toDto(responseDtoClass, item));
164
288
  return {
165
289
  success: true,
166
- message: `${data.length} items updated successfully`,
290
+ message: `${data.length} ${entityName}s updated successfully`,
291
+ messageKey: `${entityName}.update.many.success`,
292
+ messageVariables: {
293
+ count: data.length
294
+ },
167
295
  data,
168
296
  meta: {
169
297
  count: data.length,
@@ -172,15 +300,93 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
172
300
  }
173
301
  };
174
302
  }
303
+ async bulkUpsert(dtos, user) {
304
+ if (!this.isEnabled('bulkUpsert')) {
305
+ throw new _exceptions.BaseAppException({
306
+ message: `Endpoint 'bulkUpsert' is disabled`,
307
+ messageKey: _messagekeys.ERROR_MESSAGES.ENDPOINT_DISABLED,
308
+ messageVariables: {
309
+ endpoint: 'bulkUpsert'
310
+ },
311
+ status: _common.HttpStatus.FORBIDDEN
312
+ });
313
+ }
314
+ const toInsert = [];
315
+ const toUpdate = [];
316
+ for (const dto of dtos){
317
+ if ('id' in dto && dto.id) {
318
+ toUpdate.push(dto);
319
+ } else {
320
+ toInsert.push(dto);
321
+ }
322
+ }
323
+ const allEntities = await this.service.bulkUpsert(toInsert, toUpdate, user);
324
+ const data = allEntities.map((item)=>toDto(responseDtoClass, item));
325
+ return {
326
+ success: true,
327
+ message: `${data.length} ${entityName}s upserted successfully`,
328
+ messageKey: `${entityName}.upsert.many.success`,
329
+ messageVariables: {
330
+ count: data.length
331
+ },
332
+ data,
333
+ meta: {
334
+ count: data.length,
335
+ total: dtos.length,
336
+ failed: dtos.length - data.length
337
+ }
338
+ };
339
+ }
340
+ async getByFilter(filter, user) {
341
+ if (!this.isEnabled('getByFilter')) {
342
+ throw new _exceptions.BaseAppException({
343
+ message: `Endpoint 'getByFilter' is disabled`,
344
+ messageKey: _messagekeys.ERROR_MESSAGES.ENDPOINT_DISABLED,
345
+ messageVariables: {
346
+ endpoint: 'getByFilter'
347
+ },
348
+ status: _common.HttpStatus.FORBIDDEN
349
+ });
350
+ }
351
+ const result = await this.service.getAll('', {
352
+ filter,
353
+ pagination: {
354
+ currentPage: 0,
355
+ pageSize: 1
356
+ }
357
+ }, user);
358
+ if (!result.data || result.data.length === 0) {
359
+ throw new _exceptions.NotFoundException(entityName);
360
+ }
361
+ const entity = result.data[0];
362
+ const data = toDto(responseDtoClass, entity);
363
+ return {
364
+ success: true,
365
+ message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)} retrieved successfully`,
366
+ messageKey: `${entityName}.get.by.filter.success`,
367
+ data
368
+ };
369
+ }
175
370
  async getAll(filterAndPaginationDto, user, search) {
371
+ if (!this.isEnabled('getAll')) {
372
+ throw new _exceptions.BaseAppException({
373
+ message: `Endpoint 'getAll' is disabled`,
374
+ messageKey: _messagekeys.ERROR_MESSAGES.ENDPOINT_DISABLED,
375
+ messageVariables: {
376
+ endpoint: 'getAll'
377
+ },
378
+ status: _common.HttpStatus.FORBIDDEN
379
+ });
380
+ }
176
381
  const result = await this.service.getAll(search ?? '', filterAndPaginationDto, user);
177
- const data = (0, _classtransformer.plainToInstance)(responseDtoClass, result.data);
382
+ const data = result.data.map((item)=>toDto(responseDtoClass, item));
178
383
  const page = filterAndPaginationDto.pagination?.currentPage ?? 0;
179
384
  const pageSize = filterAndPaginationDto.pagination?.pageSize ?? data.length;
180
385
  const totalPages = pageSize > 0 ? Math.ceil(result.total / pageSize) : 1;
181
386
  return {
182
387
  success: true,
183
- message: 'Data retrieved successfully',
388
+ message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)}s retrieved successfully`,
389
+ messageKey: `${entityName}.get.all.success`,
184
390
  data,
185
391
  meta: {
186
392
  total: result.total,
@@ -193,21 +399,37 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
193
399
  };
194
400
  }
195
401
  async delete(deleteDto, user) {
402
+ if (!this.isEnabled('delete')) {
403
+ throw new _exceptions.BaseAppException({
404
+ message: `Endpoint 'delete' is disabled`,
405
+ messageKey: _messagekeys.ERROR_MESSAGES.ENDPOINT_DISABLED,
406
+ messageVariables: {
407
+ endpoint: 'delete'
408
+ },
409
+ status: _common.HttpStatus.FORBIDDEN
410
+ });
411
+ }
196
412
  await this.service.delete(deleteDto, user);
197
413
  const count = Array.isArray(deleteDto.id) ? deleteDto.id.length : 1;
198
- const action = deleteDto.type === 'restore' ? 'restored' : 'deleted';
414
+ const action = deleteDto.type === 'restore' ? 'restore' : 'delete';
199
415
  return {
200
416
  success: true,
201
- message: `${count} item${count > 1 ? 's' : ''} ${action} successfully`
417
+ message: `${count} ${entityName}${count > 1 ? 's' : ''} ${action}d successfully`,
418
+ messageKey: `${entityName}.${action}.success`,
419
+ messageVariables: {
420
+ count
421
+ }
202
422
  };
203
423
  }
204
424
  constructor(service){
205
425
  _define_property(this, "service", void 0);
426
+ _define_property(this, "enabledEndpoints", void 0);
206
427
  this.service = service;
428
+ this.enabledEndpoints = options.enabledEndpoints ?? 'all';
207
429
  }
208
430
  };
209
431
  _ts_decorate([
210
- createSecurityDecorators(security.insert),
432
+ createEndpointDecorators(security.insert, 'insert', options.enabledEndpoints),
211
433
  (0, _common.Version)(_common.VERSION_NEUTRAL),
212
434
  (0, _common.Post)('insert'),
213
435
  (0, _common.HttpCode)(_common.HttpStatus.CREATED),
@@ -235,7 +457,7 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
235
457
  _ts_metadata("design:returntype", Promise)
236
458
  ], ApiController.prototype, "insert", null);
237
459
  _ts_decorate([
238
- createSecurityDecorators(security.insertMany),
460
+ createEndpointDecorators(security.insertMany, 'insertMany', options.enabledEndpoints),
239
461
  (0, _common.Version)(_common.VERSION_NEUTRAL),
240
462
  (0, _common.Post)('insert-many'),
241
463
  (0, _common.HttpCode)(_common.HttpStatus.CREATED),
@@ -264,7 +486,7 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
264
486
  _ts_metadata("design:returntype", Promise)
265
487
  ], ApiController.prototype, "insertMany", null);
266
488
  _ts_decorate([
267
- createSecurityDecorators(security.getById),
489
+ createEndpointDecorators(security.getById, 'getById', options.enabledEndpoints),
268
490
  (0, _common.Version)(_common.VERSION_NEUTRAL),
269
491
  (0, _common.Post)('get/:id'),
270
492
  (0, _common.HttpCode)(_common.HttpStatus.OK),
@@ -294,7 +516,29 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
294
516
  _ts_metadata("design:returntype", Promise)
295
517
  ], ApiController.prototype, "getById", null);
296
518
  _ts_decorate([
297
- createSecurityDecorators(security.update),
519
+ createEndpointDecorators(security.getByIds, 'getByIds', options.enabledEndpoints),
520
+ (0, _common.Version)(_common.VERSION_NEUTRAL),
521
+ (0, _common.Post)('get-by-ids'),
522
+ (0, _common.HttpCode)(_common.HttpStatus.OK),
523
+ (0, _swagger.ApiOperation)({
524
+ summary: 'Get items by IDs',
525
+ description: 'Retrieves multiple items by their IDs. Optionally specify fields to select.'
526
+ }),
527
+ (0, _swagger.ApiBody)({
528
+ type: _dtos.GetByIdsDto
529
+ }),
530
+ (0, _apiresponsedecorator.ApiResponseDto)(responseDtoClass, true, 'list'),
531
+ _ts_param(0, (0, _common.Body)()),
532
+ _ts_param(1, (0, _decorators.CurrentUser)()),
533
+ _ts_metadata("design:type", Function),
534
+ _ts_metadata("design:paramtypes", [
535
+ typeof _dtos.GetByIdsDto === "undefined" ? Object : _dtos.GetByIdsDto,
536
+ Object
537
+ ]),
538
+ _ts_metadata("design:returntype", Promise)
539
+ ], ApiController.prototype, "getByIds", null);
540
+ _ts_decorate([
541
+ createEndpointDecorators(security.update, 'update', options.enabledEndpoints),
298
542
  (0, _common.Version)(_common.VERSION_NEUTRAL),
299
543
  (0, _common.Post)('update'),
300
544
  (0, _common.HttpCode)(_common.HttpStatus.OK),
@@ -317,7 +561,7 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
317
561
  _ts_metadata("design:returntype", Promise)
318
562
  ], ApiController.prototype, "update", null);
319
563
  _ts_decorate([
320
- createSecurityDecorators(security.updateMany),
564
+ createEndpointDecorators(security.updateMany, 'updateMany', options.enabledEndpoints),
321
565
  (0, _common.Version)(_common.VERSION_NEUTRAL),
322
566
  (0, _common.Post)('update-many'),
323
567
  (0, _common.HttpCode)(_common.HttpStatus.OK),
@@ -341,22 +585,62 @@ function createApiController(createDtoClass, updateDtoClass, responseDtoClass, o
341
585
  _ts_metadata("design:returntype", Promise)
342
586
  ], ApiController.prototype, "updateMany", null);
343
587
  _ts_decorate([
344
- createSecurityDecorators(security.getAll),
588
+ createEndpointDecorators(security.bulkUpsert, 'bulkUpsert', options.enabledEndpoints),
589
+ (0, _common.Version)(_common.VERSION_NEUTRAL),
590
+ (0, _common.Post)('bulk-upsert'),
591
+ (0, _common.HttpCode)(_common.HttpStatus.OK),
592
+ (0, _swagger.ApiOperation)({
593
+ summary: 'Create or update multiple items',
594
+ description: 'Bulk upsert: items without ID are created, items with ID are updated.'
595
+ }),
596
+ (0, _apiresponsedecorator.ApiResponseDto)(responseDtoClass, true, 'bulk'),
597
+ (0, _swagger.ApiBody)({
598
+ type: createDtoClass,
599
+ isArray: true
600
+ }),
601
+ (0, _common.UseInterceptors)(_interceptors.SetCreatedByOnBody, _interceptors.SetUpdateByOnBody, _interceptors.Slug),
602
+ _ts_param(0, (0, _common.Body)()),
603
+ _ts_param(1, (0, _decorators.CurrentUser)()),
604
+ _ts_metadata("design:type", Function),
605
+ _ts_metadata("design:paramtypes", [
606
+ Array,
607
+ Object
608
+ ]),
609
+ _ts_metadata("design:returntype", Promise)
610
+ ], ApiController.prototype, "bulkUpsert", null);
611
+ _ts_decorate([
612
+ createEndpointDecorators(security.getByFilter, 'getByFilter', options.enabledEndpoints),
613
+ (0, _common.Version)(_common.VERSION_NEUTRAL),
614
+ (0, _common.Post)('get-by-filter'),
615
+ (0, _common.HttpCode)(_common.HttpStatus.OK),
616
+ (0, _swagger.ApiOperation)({
617
+ summary: 'Get single item by filter',
618
+ description: 'Retrieves a single item matching the provided filter criteria. Returns first match if multiple exist.'
619
+ }),
620
+ (0, _swagger.ApiBody)({
621
+ schema: {
622
+ type: 'object',
623
+ description: 'Filter criteria'
624
+ }
625
+ }),
626
+ (0, _apiresponsedecorator.ApiResponseDto)(responseDtoClass),
627
+ _ts_param(0, (0, _common.Body)()),
628
+ _ts_param(1, (0, _decorators.CurrentUser)()),
629
+ _ts_metadata("design:type", Function),
630
+ _ts_metadata("design:paramtypes", [
631
+ typeof Record === "undefined" ? Object : Record,
632
+ Object
633
+ ]),
634
+ _ts_metadata("design:returntype", Promise)
635
+ ], ApiController.prototype, "getByFilter", null);
636
+ _ts_decorate([
637
+ createEndpointDecorators(security.getAll, 'getAll', options.enabledEndpoints),
345
638
  (0, _common.Version)(_common.VERSION_NEUTRAL),
346
639
  (0, _common.Post)('get-all'),
347
640
  (0, _common.HttpCode)(_common.HttpStatus.OK),
348
641
  (0, _swagger.ApiOperation)({
349
642
  summary: 'Get all items with filters and pagination',
350
- description: `
351
- Retrieves items with support for:
352
- - **filter**: Apply field-based filters (e.g., \`{ "isActive": true }\`)
353
- - **pagination**: Control page and page size
354
- - **sort**: Order by any field (e.g., \`{ "createdAt": "DESC" }\`)
355
- - **select**: Choose specific fields to return
356
- - **withDeleted**: Include soft-deleted items
357
- - **extraKey**: Include additional relations
358
- - **q** (query param): Global text search
359
- `
643
+ description: 'Supports filter, pagination, sort, select, withDeleted, and q (search) params'
360
644
  }),
361
645
  (0, _swagger.ApiQuery)({
362
646
  name: 'q',
@@ -379,21 +663,13 @@ Retrieves items with support for:
379
663
  _ts_metadata("design:returntype", Promise)
380
664
  ], ApiController.prototype, "getAll", null);
381
665
  _ts_decorate([
382
- createSecurityDecorators(security.delete),
666
+ createEndpointDecorators(security.delete, 'delete', options.enabledEndpoints),
383
667
  (0, _common.Version)(_common.VERSION_NEUTRAL),
384
668
  (0, _common.Post)('delete'),
385
669
  (0, _common.HttpCode)(_common.HttpStatus.OK),
386
670
  (0, _swagger.ApiOperation)({
387
671
  summary: 'Delete, restore, or permanently remove items',
388
- description: `
389
- Performs one of three actions:
390
-
391
- - **"delete"** (soft delete): Marks items as deleted but keeps in database
392
- - **"restore"**: Reverts soft-deleted items to active
393
- - **"permanent"**: Completely removes items from database
394
-
395
- Supports single ID or array of IDs for batch operations.
396
- `
672
+ description: 'Types: delete (soft), restore, permanent. Supports batch IDs.'
397
673
  }),
398
674
  (0, _swagger.ApiResponse)({
399
675
  status: 200,