@flusys/nestjs-shared 3.0.0 → 4.0.0-lts

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 (103) hide show
  1. package/README.md +160 -80
  2. package/cjs/classes/api-controller.class.js +26 -8
  3. package/cjs/classes/api-service.class.js +100 -17
  4. package/cjs/classes/winston-logger-adapter.class.js +15 -20
  5. package/cjs/classes/winston.logger.class.js +103 -70
  6. package/cjs/constants/index.js +1 -0
  7. package/cjs/constants/message-keys.js +80 -0
  8. package/cjs/constants/permissions.js +65 -11
  9. package/cjs/decorators/index.js +1 -0
  10. package/cjs/decorators/log-action.decorator.js +149 -0
  11. package/cjs/dtos/response-payload.dto.js +72 -0
  12. package/cjs/enums/index.js +20 -0
  13. package/cjs/enums/notification-type.enum.js +17 -0
  14. package/cjs/enums/participant-status.enum.js +17 -0
  15. package/cjs/enums/recurrence-type.enum.js +18 -0
  16. package/cjs/exceptions/base-app.exception.js +145 -0
  17. package/cjs/exceptions/index.js +1 -0
  18. package/cjs/exceptions/permission.exception.js +12 -8
  19. package/cjs/filters/global-exception.filter.js +167 -0
  20. package/cjs/filters/index.js +18 -0
  21. package/cjs/guards/jwt-auth.guard.js +4 -1
  22. package/cjs/guards/permission.guard.js +6 -13
  23. package/cjs/index.js +2 -0
  24. package/cjs/interceptors/idempotency.interceptor.js +1 -1
  25. package/cjs/interceptors/index.js +0 -1
  26. package/cjs/interfaces/event-manager-adapter.interface.js +11 -0
  27. package/cjs/interfaces/index.js +2 -0
  28. package/cjs/interfaces/logger.interface.js +1 -4
  29. package/cjs/interfaces/notification-adapter.interface.js +11 -0
  30. package/cjs/middlewares/logger.middleware.js +83 -26
  31. package/cjs/modules/datasource/multi-tenant-datasource.service.js +33 -11
  32. package/cjs/modules/utils/utils.service.js +4 -20
  33. package/cjs/utils/index.js +0 -1
  34. package/cjs/utils/query-helpers.util.js +8 -1
  35. package/classes/api-controller.class.d.ts +1 -0
  36. package/classes/api-service.class.d.ts +5 -10
  37. package/classes/winston-logger-adapter.class.d.ts +12 -11
  38. package/classes/winston.logger.class.d.ts +1 -0
  39. package/constants/index.d.ts +1 -0
  40. package/constants/message-keys.d.ts +81 -0
  41. package/constants/permissions.d.ts +72 -0
  42. package/decorators/index.d.ts +1 -0
  43. package/decorators/log-action.decorator.d.ts +8 -0
  44. package/dtos/response-payload.dto.d.ts +8 -0
  45. package/enums/index.d.ts +3 -0
  46. package/enums/notification-type.enum.d.ts +6 -0
  47. package/enums/participant-status.enum.d.ts +6 -0
  48. package/enums/recurrence-type.enum.d.ts +7 -0
  49. package/exceptions/base-app.exception.d.ts +41 -0
  50. package/exceptions/index.d.ts +1 -0
  51. package/exceptions/permission.exception.d.ts +1 -1
  52. package/fesm/classes/api-controller.class.js +26 -8
  53. package/fesm/classes/api-service.class.js +101 -18
  54. package/fesm/classes/winston-logger-adapter.class.js +18 -44
  55. package/fesm/classes/winston.logger.class.js +100 -68
  56. package/fesm/constants/index.js +2 -0
  57. package/fesm/constants/message-keys.js +59 -0
  58. package/fesm/constants/permissions.js +51 -14
  59. package/fesm/decorators/index.js +1 -0
  60. package/fesm/decorators/log-action.decorator.js +139 -0
  61. package/fesm/dtos/response-payload.dto.js +72 -0
  62. package/fesm/enums/index.js +3 -0
  63. package/fesm/enums/notification-type.enum.js +7 -0
  64. package/fesm/enums/participant-status.enum.js +7 -0
  65. package/fesm/enums/recurrence-type.enum.js +8 -0
  66. package/fesm/exceptions/base-app.exception.js +109 -0
  67. package/fesm/exceptions/index.js +1 -0
  68. package/fesm/exceptions/permission.exception.js +15 -17
  69. package/fesm/filters/global-exception.filter.js +157 -0
  70. package/fesm/filters/index.js +1 -0
  71. package/fesm/guards/jwt-auth.guard.js +5 -2
  72. package/fesm/guards/permission.guard.js +8 -15
  73. package/fesm/index.js +2 -0
  74. package/fesm/interceptors/idempotency.interceptor.js +2 -2
  75. package/fesm/interceptors/index.js +0 -1
  76. package/fesm/interfaces/event-manager-adapter.interface.js +1 -0
  77. package/fesm/interfaces/index.js +2 -0
  78. package/fesm/interfaces/logger.interface.js +1 -4
  79. package/fesm/interfaces/notification-adapter.interface.js +1 -0
  80. package/fesm/middlewares/logger.middleware.js +83 -26
  81. package/fesm/modules/datasource/multi-tenant-datasource.service.js +34 -12
  82. package/fesm/modules/utils/utils.service.js +5 -21
  83. package/fesm/utils/index.js +0 -1
  84. package/fesm/utils/query-helpers.util.js +8 -1
  85. package/filters/global-exception.filter.d.ts +10 -0
  86. package/filters/index.d.ts +1 -0
  87. package/guards/permission.guard.d.ts +1 -3
  88. package/index.d.ts +2 -0
  89. package/interceptors/index.d.ts +0 -1
  90. package/interfaces/event-manager-adapter.interface.d.ts +43 -0
  91. package/interfaces/index.d.ts +2 -0
  92. package/interfaces/logger.interface.d.ts +5 -5
  93. package/interfaces/notification-adapter.interface.d.ts +22 -0
  94. package/modules/datasource/multi-tenant-datasource.service.d.ts +1 -2
  95. package/modules/utils/utils.service.d.ts +0 -1
  96. package/package.json +10 -3
  97. package/utils/index.d.ts +0 -1
  98. package/cjs/interceptors/query-performance.interceptor.js +0 -66
  99. package/cjs/utils/error-handler.util.js +0 -90
  100. package/fesm/interceptors/query-performance.interceptor.js +0 -56
  101. package/fesm/utils/error-handler.util.js +0 -82
  102. package/interceptors/query-performance.interceptor.d.ts +0 -8
  103. package/utils/error-handler.util.d.ts +0 -19
@@ -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,12 @@ 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
10
  _export_star(require("./logger.interface"), exports);
10
11
  _export_star(require("./module-config.interface"), exports);
12
+ _export_star(require("./notification-adapter.interface"), exports);
11
13
  _export_star(require("./permission.interface"), exports);
12
14
  function _export_star(from, to) {
13
15
  Object.keys(from).forEach(function(k) {
@@ -1,7 +1,4 @@
1
- /**
2
- * Logger interface for dependency injection
3
- * Compatible with Winston, NestJS Logger, and console
4
- */ "use strict";
1
+ "use strict";
5
2
  Object.defineProperty(exports, "__esModule", {
6
3
  value: true
7
4
  });
@@ -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';
@@ -54,18 +54,15 @@ function _ts_decorate(decorators, target, key, desc) {
54
54
  return c > 3 && r && Object.defineProperty(target, key, r), r;
55
55
  }
56
56
  // Configuration
57
- const IS_DEBUG = _config.envConfig.getLogConfig().level === 'debug';
57
+ const logConfig = _config.envConfig.getLogConfig();
58
+ const IS_DEBUG = logConfig.level === 'debug';
59
+ const DISABLE_HTTP_LOGGING = logConfig.disableHttpLogging;
58
60
  const TENANT_ID_HEADER = 'x-tenant-id';
59
61
  const EXCLUDED_PATHS = [
60
62
  '/health',
61
63
  '/metrics',
62
64
  '/favicon.ico'
63
65
  ];
64
- const EXCLUDED_HEADERS = [
65
- 'authorization',
66
- 'cookie',
67
- 'x-api-key'
68
- ];
69
66
  const MAX_BODY_LOG_SIZE = 1000;
70
67
  const requestContext = new _async_hooks.AsyncLocalStorage();
71
68
  const getRequestId = ()=>requestContext.getStore()?.requestId;
@@ -73,17 +70,71 @@ const getTenantId = ()=>requestContext.getStore()?.tenantId;
73
70
  const getUserId = ()=>requestContext.getStore()?.userId;
74
71
  const getCompanyId = ()=>requestContext.getStore()?.companyId;
75
72
  // Helper Functions
76
- function sanitizeHeaders(headers) {
77
- const sanitized = {};
78
- for (const [key, value] of Object.entries(headers)){
79
- sanitized[key] = EXCLUDED_HEADERS.includes(key.toLowerCase()) ? '[REDACTED]' : value;
73
+ function summarizeRequestBody(body) {
74
+ if (!body) return undefined;
75
+ const parsed = typeof body === 'string' ? tryParseJson(body) : body;
76
+ if (!parsed || typeof parsed !== 'object') {
77
+ const str = String(body);
78
+ return str.length > MAX_BODY_LOG_SIZE ? str.substring(0, MAX_BODY_LOG_SIZE) + '...' : str;
79
+ }
80
+ // For API request bodies, provide a concise summary
81
+ const summary = {};
82
+ // Pagination info
83
+ if (parsed.pagination) {
84
+ summary.page = parsed.pagination.currentPage;
85
+ summary.pageSize = parsed.pagination.pageSize;
86
+ }
87
+ // Filter keys only (not values - may contain sensitive data)
88
+ if (parsed.filter && typeof parsed.filter === 'object') {
89
+ const filterKeys = Object.keys(parsed.filter);
90
+ if (filterKeys.length > 0) summary.filterKeys = filterKeys;
91
+ }
92
+ // Select fields count
93
+ if (Array.isArray(parsed.select) && parsed.select.length > 0) {
94
+ summary.selectCount = parsed.select.length;
95
+ }
96
+ // Sort keys
97
+ if (parsed.sort && typeof parsed.sort === 'object') {
98
+ const sortKeys = Object.keys(parsed.sort);
99
+ if (sortKeys.length > 0) summary.sortKeys = sortKeys;
80
100
  }
81
- return sanitized;
101
+ // ID for single item operations
102
+ if (parsed.id) summary.id = parsed.id;
103
+ // IDs for bulk operations
104
+ if (Array.isArray(parsed.ids)) summary.idsCount = parsed.ids.length;
105
+ return Object.keys(summary).length > 0 ? JSON.stringify(summary) : '{}';
82
106
  }
83
- function truncateBody(body) {
107
+ function summarizeResponseBody(body) {
84
108
  if (!body) return undefined;
85
- const str = typeof body === 'string' ? body : JSON.stringify(body);
86
- return str.length > MAX_BODY_LOG_SIZE ? str.substring(0, MAX_BODY_LOG_SIZE) + '...' : body;
109
+ const parsed = typeof body === 'string' ? tryParseJson(body) : body;
110
+ if (!parsed || typeof parsed !== 'object') {
111
+ const str = String(body);
112
+ return str.length > MAX_BODY_LOG_SIZE ? str.substring(0, MAX_BODY_LOG_SIZE) + '...' : str;
113
+ }
114
+ const summary = {};
115
+ if ('success' in parsed) summary.success = parsed.success;
116
+ if ('message' in parsed) summary.message = parsed.message;
117
+ if ('code' in parsed) summary.code = parsed.code;
118
+ if (Array.isArray(parsed.data)) {
119
+ summary.dataCount = parsed.data.length;
120
+ } else if (parsed.data && typeof parsed.data === 'object') {
121
+ summary.hasData = true;
122
+ }
123
+ if (parsed.meta) {
124
+ summary.total = parsed.meta.total;
125
+ summary.page = parsed.meta.page;
126
+ }
127
+ if (parsed.errors) {
128
+ summary.errorsCount = Array.isArray(parsed.errors) ? parsed.errors.length : 1;
129
+ }
130
+ return JSON.stringify(summary);
131
+ }
132
+ function tryParseJson(str) {
133
+ try {
134
+ return JSON.parse(str);
135
+ } catch {
136
+ return null;
137
+ }
87
138
  }
88
139
  function getClientIp(req) {
89
140
  return req.headers['x-forwarded-for']?.split(',')[0] || req.socket?.remoteAddress || 'unknown';
@@ -100,7 +151,7 @@ let LoggerMiddleware = class LoggerMiddleware {
100
151
  startTime
101
152
  };
102
153
  requestContext.run(context, ()=>{
103
- const shouldSkipLogging = EXCLUDED_PATHS.some((path)=>req.originalUrl.startsWith(path));
154
+ const shouldSkipLogging = DISABLE_HTTP_LOGGING || EXCLUDED_PATHS.some((path)=>req.originalUrl.startsWith(path));
104
155
  if (!shouldSkipLogging) {
105
156
  this.logRequest(req, requestId, tenantId);
106
157
  this.setupResponseLogging(req, res, startTime, requestId, tenantId);
@@ -141,20 +192,22 @@ let LoggerMiddleware = class LoggerMiddleware {
141
192
  });
142
193
  }
143
194
  logRequest(req, requestId, tenantId) {
195
+ const ip = getClientIp(req);
196
+ const contentType = req.headers['content-type'] || 'none';
197
+ // Build descriptive message like: Started processing request "POST" "/api/users" from "192.168.1.1"
198
+ const message = `Started processing request "${req.method}" "${req.originalUrl}" from "${ip}"`;
144
199
  const logData = {
145
200
  ...this.buildBaseLogData(req, requestId, tenantId),
146
201
  query: Object.keys(req.query).length > 0 ? req.query : undefined,
147
- ip: getClientIp(req),
202
+ ip,
148
203
  userAgent: req.headers['user-agent'],
149
- contentType: req.headers['content-type'],
204
+ contentType,
150
205
  contentLength: req.headers['content-length']
151
206
  };
152
207
  if (IS_DEBUG) {
153
- logData.headers = sanitizeHeaders(req.headers);
154
- logData.body = truncateBody(req.body);
155
- logData.params = Object.keys(req.params).length > 0 ? req.params : undefined;
208
+ logData.body = summarizeRequestBody(req.body);
156
209
  }
157
- this.logger.info('Incoming request', logData);
210
+ this.logger.info(message, logData);
158
211
  }
159
212
  logResponse(req, res, startTime, body, requestId, tenantId) {
160
213
  const duration = Date.now() - startTime;
@@ -162,6 +215,10 @@ let LoggerMiddleware = class LoggerMiddleware {
162
215
  const level = statusCode >= 500 ? 'error' : statusCode >= 400 ? 'warn' : 'info';
163
216
  const userId = getUserId();
164
217
  const companyId = getCompanyId();
218
+ const contentType = res.getHeader('content-type') || 'none';
219
+ const contentLength = res.getHeader('content-length') || 'null';
220
+ // Build descriptive message like: Request finished "HTTP/1.1" "POST" "/api/users" - 200 "application/json" 534.29ms
221
+ const message = `Request finished "HTTP/1.1" "${req.method}" "${req.originalUrl}" - ${statusCode} ${contentLength} "${contentType}" ${duration.toFixed(2)}ms`;
165
222
  const logData = {
166
223
  ...this.buildBaseLogData(req, requestId, tenantId),
167
224
  userId,
@@ -170,15 +227,15 @@ let LoggerMiddleware = class LoggerMiddleware {
170
227
  statusMessage: res.statusMessage,
171
228
  duration: `${duration}ms`,
172
229
  durationMs: duration,
173
- contentType: res.getHeader('content-type'),
174
- contentLength: res.getHeader('content-length')
230
+ contentType,
231
+ contentLength
175
232
  };
176
233
  if (statusCode >= 400 || IS_DEBUG) {
177
- logData.responseBody = truncateBody(body);
234
+ logData.responseBody = summarizeResponseBody(body);
178
235
  }
179
- this.logger.log(level, `Response [${statusCode}]`, logData);
236
+ this.logger.log(level, message, logData);
180
237
  if (duration > 3000 && statusCode < 400) {
181
- this.logger.warn('Slow request detected', {
238
+ this.logger.warn(`Slow request detected: "${req.method}" "${req.originalUrl}" took ${duration}ms (threshold: 3000ms)`, {
182
239
  ...this.buildBaseLogData(req, requestId, tenantId),
183
240
  userId,
184
241
  companyId,
@@ -10,6 +10,7 @@ Object.defineProperty(exports, "MultiTenantDataSourceService", {
10
10
  });
11
11
  const _nestjscore = require("@flusys/nestjs-core");
12
12
  const _common = require("@nestjs/common");
13
+ const _constants = require("../../constants");
13
14
  const _core = require("@nestjs/core");
14
15
  const _express = require("express");
15
16
  const _typeorm = require("typeorm");
@@ -68,7 +69,10 @@ let MultiTenantDataSourceService = class MultiTenantDataSourceService {
68
69
  const tenantId = this.request.headers[this.tenantHeader];
69
70
  if (!tenantId) return null;
70
71
  if (!/^[a-zA-Z0-9_-]+$/.test(tenantId)) {
71
- throw new _common.BadRequestException('Invalid tenant ID format');
72
+ throw new _common.BadRequestException({
73
+ message: 'Invalid tenant ID format',
74
+ messageKey: _constants.SYSTEM_MESSAGES.INVALID_TENANT_ID
75
+ });
72
76
  }
73
77
  return tenantId;
74
78
  }
@@ -90,7 +94,15 @@ let MultiTenantDataSourceService = class MultiTenantDataSourceService {
90
94
  }
91
95
  async getDataSourceForTenant(tenantId) {
92
96
  const tenant = this.getTenant(tenantId);
93
- if (!tenant) throw new Error(`Tenant '${tenantId}' not found`);
97
+ if (!tenant) {
98
+ throw new _common.BadRequestException({
99
+ message: `Tenant '${tenantId}' not found`,
100
+ messageKey: _constants.SYSTEM_MESSAGES.TENANT_NOT_FOUND,
101
+ messageParams: {
102
+ tenantId
103
+ }
104
+ });
105
+ }
94
106
  return this.getOrCreateTenantConnection(tenant);
95
107
  }
96
108
  setDataSource(dataSource) {
@@ -110,8 +122,8 @@ let MultiTenantDataSourceService = class MultiTenantDataSourceService {
110
122
  try {
111
123
  const dataSource = await this.getOrCreateTenantConnection(tenant);
112
124
  results.set(tenant.id, await callback(tenant, dataSource));
113
- } catch (error) {
114
- this.logger.error(`Error processing tenant ${tenant.id}: ${error}`);
125
+ } catch {
126
+ // Silent failure for individual tenant
115
127
  }
116
128
  }
117
129
  return results;
@@ -128,7 +140,6 @@ let MultiTenantDataSourceService = class MultiTenantDataSourceService {
128
140
  if (connection?.isInitialized) {
129
141
  await connection.destroy();
130
142
  MultiTenantDataSourceService.tenantConnections.delete(tenantId);
131
- this.logger.log(`Closed connection for tenant: ${tenantId}`);
132
143
  }
133
144
  }
134
145
  async onModuleDestroy() {
@@ -158,7 +169,10 @@ let MultiTenantDataSourceService = class MultiTenantDataSourceService {
158
169
  }
159
170
  const config = this.getDefaultDatabaseConfig();
160
171
  if (!config) {
161
- throw new Error('No database config available. Provide defaultDatabaseConfig or tenantDefaultDatabaseConfig.');
172
+ throw new _common.InternalServerErrorException({
173
+ message: 'No database config available. Provide defaultDatabaseConfig or tenantDefaultDatabaseConfig.',
174
+ messageKey: _constants.SYSTEM_MESSAGES.DATABASE_CONFIG_NOT_AVAILABLE
175
+ });
162
176
  }
163
177
  // Create connection with lock to prevent race conditions
164
178
  const connectionPromise = this.createDataSourceFromConfig(config);
@@ -176,7 +190,13 @@ let MultiTenantDataSourceService = class MultiTenantDataSourceService {
176
190
  */ async getTenantDataSource() {
177
191
  const tenant = this.getCurrentTenant();
178
192
  if (!tenant) {
179
- throw new Error(`Tenant not found. Ensure '${this.tenantHeader}' header is set.`);
193
+ throw new _common.BadRequestException({
194
+ message: `Tenant not found. Ensure '${this.tenantHeader}' header is set.`,
195
+ messageKey: _constants.SYSTEM_MESSAGES.TENANT_HEADER_REQUIRED,
196
+ messageParams: {
197
+ header: this.tenantHeader
198
+ }
199
+ });
180
200
  }
181
201
  return this.getOrCreateTenantConnection(tenant);
182
202
  }
@@ -191,7 +211,6 @@ let MultiTenantDataSourceService = class MultiTenantDataSourceService {
191
211
  try {
192
212
  const dataSource = await connectionPromise;
193
213
  MultiTenantDataSourceService.tenantConnections.set(tenant.id, dataSource);
194
- this.logger.log(`Created connection for tenant: ${tenant.id}`);
195
214
  return dataSource;
196
215
  } finally{
197
216
  MultiTenantDataSourceService.connectionLocks.delete(tenant.id);
@@ -202,7 +221,12 @@ let MultiTenantDataSourceService = class MultiTenantDataSourceService {
202
221
  }
203
222
  buildTenantDatabaseConfig(tenant) {
204
223
  const defaultConfig = this.getDefaultDatabaseConfig();
205
- if (!defaultConfig) throw new Error('No default database config for multi-tenant mode.');
224
+ if (!defaultConfig) {
225
+ throw new _common.InternalServerErrorException({
226
+ message: 'No default database config for multi-tenant mode.',
227
+ messageKey: _constants.SYSTEM_MESSAGES.DATABASE_CONFIG_NOT_AVAILABLE
228
+ });
229
+ }
206
230
  return {
207
231
  type: defaultConfig.type,
208
232
  host: tenant.host ?? defaultConfig.host,
@@ -230,11 +254,9 @@ let MultiTenantDataSourceService = class MultiTenantDataSourceService {
230
254
  constructor(options, request){
231
255
  _define_property(this, "options", void 0);
232
256
  _define_property(this, "request", void 0);
233
- _define_property(this, "logger", void 0);
234
257
  _define_property(this, "tenantHeader", void 0);
235
258
  this.options = options;
236
259
  this.request = request;
237
- this.logger = new _common.Logger(this.constructor.name);
238
260
  this.tenantHeader = _nestjscore.DEFAULT_TENANT_HEADER;
239
261
  this.initializeFromOptions();
240
262
  }
@@ -9,19 +9,6 @@ Object.defineProperty(exports, "UtilsService", {
9
9
  }
10
10
  });
11
11
  const _common = require("@nestjs/common");
12
- function _define_property(obj, key, value) {
13
- if (key in obj) {
14
- Object.defineProperty(obj, key, {
15
- value: value,
16
- enumerable: true,
17
- configurable: true,
18
- writable: true
19
- });
20
- } else {
21
- obj[key] = value;
22
- }
23
- return obj;
24
- }
25
12
  function _ts_decorate(decorators, target, key, desc) {
26
13
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
27
14
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -47,8 +34,8 @@ let UtilsService = class UtilsService {
47
34
  const keys = await cacheManager.get(trackingKey) || [];
48
35
  if (!keys.includes(cacheKey)) keys.push(cacheKey);
49
36
  await cacheManager.set(trackingKey, keys);
50
- } catch (error) {
51
- this.logger.error(`Cache tracking failed for ${entityName}`, error instanceof Error ? error.stack : String(error));
37
+ } catch {
38
+ // Silent failure - cache tracking is non-critical
52
39
  }
53
40
  }
54
41
  /**
@@ -59,8 +46,8 @@ let UtilsService = class UtilsService {
59
46
  const keys = await cacheManager.get(trackingKey) || [];
60
47
  await Promise.allSettled(keys.map((key)=>cacheManager.del(key)));
61
48
  await cacheManager.del(trackingKey);
62
- } catch (error) {
63
- this.logger.error(`Cache invalidation failed for ${entityName}`, error instanceof Error ? error.stack : String(error));
49
+ } catch {
50
+ // Silent failure - cache invalidation is non-critical
64
51
  }
65
52
  }
66
53
  // ---------------- STRING HELPERS ----------------
@@ -83,9 +70,6 @@ let UtilsService = class UtilsService {
83
70
  const prefix = this.buildTenantPrefix(tenantId);
84
71
  return entityId ? `${prefix}entity_${entityName}_id_${entityId}_keys` : `${prefix}entity_${entityName}_keys`;
85
72
  }
86
- constructor(){
87
- _define_property(this, "logger", new _common.Logger(UtilsService.name));
88
- }
89
73
  };
90
74
  UtilsService = _ts_decorate([
91
75
  (0, _common.Injectable)()
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", {
3
3
  value: true
4
4
  });
5
- _export_star(require("./error-handler.util"), exports);
6
5
  _export_star(require("./html-sanitizer.util"), exports);
7
6
  _export_star(require("./query-helpers.util"), exports);
8
7
  _export_star(require("./request.util"), exports);
@@ -23,6 +23,7 @@ _export(exports, {
23
23
  }
24
24
  });
25
25
  const _common = require("@nestjs/common");
26
+ const _constants = require("../constants");
26
27
  function applyCompanyFilter(query, config, user) {
27
28
  const columnName = config.columnName ?? 'companyId';
28
29
  if (config.isCompanyFeatureEnabled && user?.companyId) {
@@ -47,7 +48,13 @@ function hasCompanyId(entity) {
47
48
  function validateCompanyOwnership(entity, user, isCompanyFeatureEnabled, entityName) {
48
49
  if (isCompanyFeatureEnabled && user?.companyId && hasCompanyId(entity)) {
49
50
  if (entity.companyId && entity.companyId !== user.companyId) {
50
- throw new _common.BadRequestException(`${entityName} belongs to another company`);
51
+ throw new _common.BadRequestException({
52
+ message: `${entityName} belongs to another company`,
53
+ messageKey: _constants.AUTH_MESSAGES.COMPANY_NO_ACCESS,
54
+ messageParams: {
55
+ entity: entityName
56
+ }
57
+ });
51
58
  }
52
59
  }
53
60
  }
@@ -15,6 +15,7 @@ export type ApiSecurityConfig = {
15
15
  };
16
16
  export interface ApiControllerOptions {
17
17
  security?: ApiSecurityConfig | EndpointSecurity | SecurityLevel;
18
+ entityName?: string;
18
19
  }
19
20
  export declare function createApiController<CreateDtoT extends object, UpdateDtoT extends {
20
21
  id: string;
@@ -2,7 +2,6 @@ import { DeleteDto, FilterAndPaginationDto } from '../dtos';
2
2
  import { Identity } from '../entities';
3
3
  import { ILoggedUserInfo, IService } from '../interfaces';
4
4
  import { UtilsService } from '../modules/utils/utils.service';
5
- import { Logger } from '@nestjs/common';
6
5
  import { QueryRunner, Repository, SelectQueryBuilder } from 'typeorm';
7
6
  import { HybridCache } from './hybrid-cache.class';
8
7
  export declare abstract class ApiService<CreateDtoT extends object, UpdateDtoT extends {
@@ -12,10 +11,10 @@ export declare abstract class ApiService<CreateDtoT extends object, UpdateDtoT e
12
11
  protected repository: RepositoryT;
13
12
  protected cacheManager: HybridCache;
14
13
  protected utilsService: UtilsService;
15
- protected loggerName: string;
14
+ protected _loggerName: string;
16
15
  protected isCacheable: boolean;
17
- protected logger: Logger;
18
- constructor(entityName: string, repository: RepositoryT, cacheManager: HybridCache, utilsService: UtilsService, loggerName: string, isCacheable?: boolean);
16
+ protected moduleName?: string;
17
+ constructor(entityName: string, repository: RepositoryT, cacheManager: HybridCache, utilsService: UtilsService, _loggerName: string, isCacheable?: boolean, moduleName?: string);
19
18
  insert(dto: CreateDtoT, user: ILoggedUserInfo | null): Promise<InterfaceT>;
20
19
  insertMany(dtos: Array<CreateDtoT>, user: ILoggedUserInfo | null): Promise<InterfaceT[]>;
21
20
  update(dto: UpdateDtoT, user: ILoggedUserInfo | null): Promise<InterfaceT>;
@@ -41,15 +40,11 @@ export declare abstract class ApiService<CreateDtoT extends object, UpdateDtoT e
41
40
  protected afterDeleteOperation(_entity: Array<{
42
41
  id: string;
43
42
  }>, _user: ILoggedUserInfo | null, _queryRunner: QueryRunner): Promise<void>;
44
- protected getFilterQuery(query: SelectQueryBuilder<EntityT>, filter: {
45
- [key: string]: any;
46
- }, _user: ILoggedUserInfo | null): Promise<{
43
+ protected getFilterQuery(query: SelectQueryBuilder<EntityT>, filter: Record<string, unknown>, _user: ILoggedUserInfo | null): Promise<{
47
44
  query: SelectQueryBuilder<EntityT>;
48
45
  isRaw: boolean;
49
46
  }>;
50
- protected getSortQuery(query: SelectQueryBuilder<EntityT>, sort: {
51
- [key: string]: 'ASC' | 'DESC';
52
- }, _user: ILoggedUserInfo | null): Promise<{
47
+ protected getSortQuery(query: SelectQueryBuilder<EntityT>, sort: Record<string, 'ASC' | 'DESC'>, _user: ILoggedUserInfo | null): Promise<{
53
48
  query: SelectQueryBuilder<EntityT>;
54
49
  isRaw: boolean;
55
50
  }>;
@@ -2,21 +2,22 @@ import { Logger } from '@nestjs/common';
2
2
  import { ILogger } from '../interfaces/logger.interface';
3
3
  export declare class WinstonLoggerAdapter implements ILogger {
4
4
  private readonly context?;
5
- constructor(context?: string);
5
+ private readonly logger;
6
+ constructor(context?: string, moduleName?: string);
6
7
  private buildLogMeta;
7
- log(message: string, context?: string, ...args: any[]): void;
8
- error(message: string, trace?: string, context?: string, ...args: any[]): void;
9
- warn(message: string, context?: string, ...args: any[]): void;
10
- debug(message: string, context?: string, ...args: any[]): void;
11
- verbose(message: string, context?: string, ...args: any[]): void;
8
+ log(message: string, context?: string, ...args: unknown[]): void;
9
+ error(message: string, trace?: string, context?: string, ...args: unknown[]): void;
10
+ warn(message: string, context?: string, ...args: unknown[]): void;
11
+ debug(message: string, context?: string, ...args: unknown[]): void;
12
+ verbose(message: string, context?: string, ...args: unknown[]): void;
12
13
  }
13
14
  export declare class NestLoggerAdapter implements ILogger {
14
15
  private readonly logger;
15
16
  constructor(logger: Logger);
16
17
  private formatMessage;
17
- log(message: string, context?: string, ...args: any[]): void;
18
- error(message: string, trace?: string, context?: string, ...args: any[]): void;
19
- warn(message: string, context?: string, ...args: any[]): void;
20
- debug(message: string, context?: string, ...args: any[]): void;
21
- verbose(message: string, context?: string, ...args: any[]): void;
18
+ log(message: string, context?: string, ...args: unknown[]): void;
19
+ error(message: string, trace?: string, context?: string, ...args: unknown[]): void;
20
+ warn(message: string, context?: string, ...args: unknown[]): void;
21
+ debug(message: string, context?: string, ...args: unknown[]): void;
22
+ verbose(message: string, context?: string, ...args: unknown[]): void;
22
23
  }
@@ -1,2 +1,3 @@
1
1
  import { Logger } from 'winston';
2
2
  export declare const instance: Logger;
3
+ export declare function createModuleLogger(moduleName: string): Logger;
@@ -9,3 +9,4 @@ export declare const CLIENT_TYPE_HEADER = "x-client-type";
9
9
  export declare const PERMISSIONS_CACHE_PREFIX = "permissions";
10
10
  export declare const IDEMPOTENCY_CACHE_PREFIX = "idempotency";
11
11
  export * from './permissions';
12
+ export * from './message-keys';
@@ -0,0 +1,81 @@
1
+ export declare const AUTH_MESSAGES: {
2
+ readonly TOKEN_REQUIRED: "auth.token.required";
3
+ readonly TOKEN_INVALID: "auth.token.invalid";
4
+ readonly TOKEN_EXPIRED: "auth.token.expired";
5
+ readonly COMPANY_NO_ACCESS: "auth.company.no.access";
6
+ };
7
+ export declare const ERROR_MESSAGES: {
8
+ readonly NOT_FOUND: "error.not.found";
9
+ readonly VALIDATION: "error.validation";
10
+ readonly UNAUTHORIZED: "error.unauthorized";
11
+ readonly FORBIDDEN: "error.forbidden";
12
+ readonly CONFLICT: "error.conflict";
13
+ readonly INTERNAL: "error.internal";
14
+ readonly SERVICE_UNAVAILABLE: "error.service.unavailable";
15
+ readonly UNKNOWN: "error.unknown";
16
+ readonly HTTP: "error.http";
17
+ readonly GENERIC: "error.generic";
18
+ readonly PERMISSION_SYSTEM_UNAVAILABLE: "error.permission.system.unavailable";
19
+ readonly INSUFFICIENT_PERMISSIONS: "error.insufficient.permissions";
20
+ readonly INSUFFICIENT_PERMISSIONS_OR: "error.insufficient.permissions.or";
21
+ readonly NO_PERMISSIONS_FOUND: "error.no.permissions.found";
22
+ };
23
+ export declare const SYSTEM_MESSAGES: {
24
+ readonly REPOSITORY_NOT_AVAILABLE: "system.repository.not.available";
25
+ readonly DATASOURCE_NOT_AVAILABLE: "system.datasource.not.available";
26
+ readonly DATABASE_CONFIG_NOT_AVAILABLE: "system.database.config.not.available";
27
+ readonly SERVICE_NOT_AVAILABLE: "system.service.not.available";
28
+ readonly CONFIG_REQUIRED: "system.config.required";
29
+ readonly INTERNAL_ERROR: "system.internal.error";
30
+ readonly NOT_FOUND: "system.not.found";
31
+ readonly DUPLICATE_REQUEST: "system.duplicate.request";
32
+ readonly INVALID_TENANT_ID: "system.invalid.tenant.id";
33
+ readonly TENANT_NOT_FOUND: "system.tenant.not.found";
34
+ readonly TENANT_HEADER_REQUIRED: "system.tenant.header.required";
35
+ readonly MISSING_PARAMETER: "system.missing.parameter";
36
+ readonly SDK_NOT_INSTALLED: "system.sdk.not.installed";
37
+ readonly PATH_TRAVERSAL_DETECTED: "system.path.traversal.detected";
38
+ readonly INVALID_FILE_KEY: "system.invalid.file.key";
39
+ };
40
+ export declare const MESSAGE_KEYS: {
41
+ readonly AUTH: {
42
+ readonly TOKEN_REQUIRED: "auth.token.required";
43
+ readonly TOKEN_INVALID: "auth.token.invalid";
44
+ readonly TOKEN_EXPIRED: "auth.token.expired";
45
+ readonly COMPANY_NO_ACCESS: "auth.company.no.access";
46
+ };
47
+ readonly ERROR: {
48
+ readonly NOT_FOUND: "error.not.found";
49
+ readonly VALIDATION: "error.validation";
50
+ readonly UNAUTHORIZED: "error.unauthorized";
51
+ readonly FORBIDDEN: "error.forbidden";
52
+ readonly CONFLICT: "error.conflict";
53
+ readonly INTERNAL: "error.internal";
54
+ readonly SERVICE_UNAVAILABLE: "error.service.unavailable";
55
+ readonly UNKNOWN: "error.unknown";
56
+ readonly HTTP: "error.http";
57
+ readonly GENERIC: "error.generic";
58
+ readonly PERMISSION_SYSTEM_UNAVAILABLE: "error.permission.system.unavailable";
59
+ readonly INSUFFICIENT_PERMISSIONS: "error.insufficient.permissions";
60
+ readonly INSUFFICIENT_PERMISSIONS_OR: "error.insufficient.permissions.or";
61
+ readonly NO_PERMISSIONS_FOUND: "error.no.permissions.found";
62
+ };
63
+ readonly SYSTEM: {
64
+ readonly REPOSITORY_NOT_AVAILABLE: "system.repository.not.available";
65
+ readonly DATASOURCE_NOT_AVAILABLE: "system.datasource.not.available";
66
+ readonly DATABASE_CONFIG_NOT_AVAILABLE: "system.database.config.not.available";
67
+ readonly SERVICE_NOT_AVAILABLE: "system.service.not.available";
68
+ readonly CONFIG_REQUIRED: "system.config.required";
69
+ readonly INTERNAL_ERROR: "system.internal.error";
70
+ readonly NOT_FOUND: "system.not.found";
71
+ readonly DUPLICATE_REQUEST: "system.duplicate.request";
72
+ readonly INVALID_TENANT_ID: "system.invalid.tenant.id";
73
+ readonly TENANT_NOT_FOUND: "system.tenant.not.found";
74
+ readonly TENANT_HEADER_REQUIRED: "system.tenant.header.required";
75
+ readonly MISSING_PARAMETER: "system.missing.parameter";
76
+ readonly SDK_NOT_INSTALLED: "system.sdk.not.installed";
77
+ readonly PATH_TRAVERSAL_DETECTED: "system.path.traversal.detected";
78
+ readonly INVALID_FILE_KEY: "system.invalid.file.key";
79
+ };
80
+ };
81
+ export type MessageKey = (typeof MESSAGE_KEYS)[keyof typeof MESSAGE_KEYS][keyof (typeof MESSAGE_KEYS)[keyof typeof MESSAGE_KEYS]];