@makebelieve21213-packages/nest-common 1.1.0 → 1.1.2

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 (56) hide show
  1. package/LICENSE +0 -1
  2. package/README.md +278 -1775
  3. package/dist/decorators/permissions.decorator.d.ts.map +1 -1
  4. package/dist/decorators/permissions.decorator.js.map +1 -1
  5. package/dist/errors/__tests__/http.error.spec.js.map +1 -1
  6. package/dist/errors/__tests__/json-rpc.error.spec.js.map +1 -1
  7. package/dist/errors/http.error.d.ts.map +1 -1
  8. package/dist/errors/http.error.js +3 -6
  9. package/dist/errors/http.error.js.map +1 -1
  10. package/dist/filters/__tests__/json-rpc-exception.filter.spec.js +3 -1
  11. package/dist/filters/__tests__/json-rpc-exception.filter.spec.js.map +1 -1
  12. package/dist/filters/__tests__/websocket-exception-handler.spec.js.map +1 -1
  13. package/dist/filters/rpc-exception-handler.d.ts.map +1 -1
  14. package/dist/filters/rpc-exception-handler.js.map +1 -1
  15. package/dist/guards/api-key.guard.d.ts.map +1 -1
  16. package/dist/guards/api-key.guard.js +1 -1
  17. package/dist/guards/api-key.guard.js.map +1 -1
  18. package/dist/guards/jwt-auth.guard.d.ts.map +1 -1
  19. package/dist/guards/jwt-auth.guard.js +1 -1
  20. package/dist/guards/jwt-auth.guard.js.map +1 -1
  21. package/dist/index.d.ts +2 -2
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +1 -1
  24. package/dist/index.js.map +1 -1
  25. package/dist/interceptors/__tests__/http-logging.interceptor.spec.js.map +1 -1
  26. package/dist/interceptors/__tests__/unified.interceptor.spec.js.map +1 -1
  27. package/dist/interceptors/__tests__/websocket-logging.interceptor.spec.js.map +1 -1
  28. package/dist/interceptors/http-logging.interceptor.d.ts.map +1 -1
  29. package/dist/interceptors/http-logging.interceptor.js.map +1 -1
  30. package/dist/interceptors/response.interceptor.d.ts.map +1 -1
  31. package/dist/interceptors/response.interceptor.js.map +1 -1
  32. package/dist/interceptors/websocket-logging.interceptor.d.ts.map +1 -1
  33. package/dist/interceptors/websocket-logging.interceptor.js.map +1 -1
  34. package/dist/pipes/__tests__/file-validation.pipe.spec.js.map +1 -1
  35. package/dist/pipes/__tests__/header-validation.pipe.spec.js.map +1 -1
  36. package/dist/pipes/__tests__/http-validation.pipe.spec.js.map +1 -1
  37. package/dist/pipes/__tests__/json-rpc-validation.pipe.spec.js.map +1 -1
  38. package/dist/pipes/file-validation.pipe.d.ts.map +1 -1
  39. package/dist/pipes/file-validation.pipe.js +1 -1
  40. package/dist/pipes/file-validation.pipe.js.map +1 -1
  41. package/dist/pipes/http-validation.pipe.d.ts.map +1 -1
  42. package/dist/pipes/http-validation.pipe.js +1 -3
  43. package/dist/pipes/http-validation.pipe.js.map +1 -1
  44. package/dist/pipes/json-rpc-validation.pipe.d.ts.map +1 -1
  45. package/dist/pipes/json-rpc-validation.pipe.js +3 -7
  46. package/dist/pipes/json-rpc-validation.pipe.js.map +1 -1
  47. package/dist/pipes/rpc-validation.pipe.d.ts.map +1 -1
  48. package/dist/pipes/rpc-validation.pipe.js +1 -3
  49. package/dist/pipes/rpc-validation.pipe.js.map +1 -1
  50. package/dist/utils/__tests__/circuit-breaker.spec.js.map +1 -1
  51. package/dist/utils/__tests__/file.utils.spec.js.map +1 -1
  52. package/dist/utils/compression.utils.d.ts.map +1 -1
  53. package/dist/utils/compression.utils.js.map +1 -1
  54. package/dist/utils/file.utils.d.ts.map +1 -1
  55. package/dist/utils/file.utils.js.map +1 -1
  56. package/package.json +1 -1
package/README.md CHANGED
@@ -1,1793 +1,376 @@
1
1
  # @makebelieve21213-packages/nest-common
2
2
 
3
- Общий пакет с утилитами для NestJS микросервисов проекта NakolenkeChain. Предоставляет единую систему обработки ошибок, валидации, логирования и базовые классы для HTTP, RPC и WebSocket контекстов.
3
+ Общий пакет с утилитами для NestJS микросервисов. Предоставляет единую систему обработки ошибок, валидации, логирования и базовые классы для HTTP, RPC и WebSocket контекстов.
4
4
 
5
5
  ## 📋 Содержание
6
6
 
7
- 1. [Обзор](#обзор)
8
- 2. [Система обработки ошибок](#система-обработки-ошибок)
9
- 3. [Классы ошибок](#классы-ошибок)
10
- 4. [Глобальные фильтры](#глобальные-фильтры)
11
- 5. [Перехватчики](#перехватчики)
12
- 6. [Базовые классы](#базовые-классы)
13
- 7. [Пайпы валидации](#пайпы-валидации)
14
- 8. [Утилиты](#утилиты)
15
- 9. [Типы](#типы)
16
- 10. [Использование](#использование)
17
- 11. [Best Practices](#best-practices)
18
- 12. [Обработка ошибок WebSocket](#обработка-ошибок-websocket)
19
- 13. [Дополнительные ресурсы](#дополнительные-ресурсы)
20
-
21
- ---
22
-
23
- ## 🎯 Обзор
24
-
25
- Пакет предоставляет единую систему обработки ошибок для всех типов контекстов:
26
- - **HTTP** - для HTTP контроллеров (api-service)
27
- - **RPC** - для RabbitMQ RPC запросов (analytics-service, data-service, token-service)
28
- - **WebSocket** - для Socket.io соединений (api-service)
7
+ - [Возможности](#-возможности)
8
+ - [Требования](#-требования)
9
+ - [Установка](#-установка)
10
+ - [Быстрый старт](#-быстрый-старт)
11
+ - [Система обработки ошибок](#-система-обработки-ошибок)
12
+ - [API Reference](#-api-reference)
13
+ - [Best Practices](#-best-practices)
14
+ - [Тестирование](#-тестирование)
15
+ - [Разработка](#-разработка)
16
+
17
+ ## 🚀 Возможности
18
+
19
+ - **Единая система обработки ошибок** - автоматическое преобразование ошибок между HTTP, RPC и WebSocket контекстами
20
+ - ✅ **Глобальные фильтры** - автоматическая обработка всех ошибок без try-catch в контроллерах
21
+ - ✅ **Валидация** - готовые пайпы для валидации DTO, файлов, query параметров и заголовков
22
+ - ✅ **Логирование** - автоматическое логирование всех HTTP, RPC и WebSocket запросов
23
+ - **Guards** - готовые guards для аутентификации, авторизации и rate limiting
24
+ - ✅ **Декораторы** - удобные декораторы для метаданных (@Public, @Roles, @Permissions, @ApiKey, @Serialize)
25
+ - **Утилиты** - функции для работы с контекстом, файлами, CORS, compression, versioning
26
+ - **100% покрытие тестами** - надежность и качество кода
27
+ - **TypeScript типизация** - полная типобезопасность
28
+
29
+ ## 📋 Требования
30
+
31
+ - **Node.js**: >= 22.11.0
32
+ - **npm**: >= 10.0.0
33
+ - **NestJS**: >= 11.0.0
29
34
 
30
- Все ошибки автоматически преобразуются между контекстами с сохранением типа и статус-кода.
31
-
32
- ---
33
-
34
- ## 🔄 Система обработки ошибок
35
-
36
- ### Архитектура
37
-
38
- ```
39
- ┌─────────────────────────────────────────────────────────────┐
40
- │ HTTP Контекст (api-service) │
41
- ├─────────────────────────────────────────────────────────────┤
42
- │ │
43
- │ HTTP Controller (БЕЗ catch блоков) │
44
- │ ↓ │
45
- │ Сервис (может иметь catch для логирования) │
46
- │ ↓ (await rabbitMQService.publish) │
47
- │ RpcException от RabbitMQ │
48
- │ ↓ (пробрасывается дальше) │
49
- │ UnifiedExceptionFilter (глобальный) │
50
- │ ↓ HttpExceptionFilter.handleException() │
51
- │ ↓ HttpError.fromUnknown() │
52
- │ HttpError с правильным статусом │
53
- │ ↓ │
54
- │ HTTP Response для фронтенда │
55
- │ │
56
- └─────────────────────────────────────────────────────────────┘
57
-
58
- ┌─────────────────────────────────────────────────────────────┐
59
- │ RPC Контекст (analytics-service) │
60
- ├─────────────────────────────────────────────────────────────┤
61
- │ │
62
- │ RPC Controller (БЕЗ catch блоков) │
63
- │ ↓ │
64
- │ Сервис (ошибка в бизнес-логике) │
65
- │ ↓ (пробрасывается дальше) │
66
- │ UnifiedExceptionFilter (глобальный) │
67
- │ ↓ RpcExceptionFilter.handleException() │
68
- │ ↓ RpcError.fromUnknown() │
69
- │ RpcError с типом (временная/постоянная) │
70
- │ ↓ (определяет retry/DLX логику) │
71
- │ Отправка через RabbitMQ в api-service │
72
- │ │
73
- └─────────────────────────────────────────────────────────────┘
74
-
75
- ┌─────────────────────────────────────────────────────────────┐
76
- │ WebSocket Контекст (api-service) │
77
- ├─────────────────────────────────────────────────────────────┤
78
- │ │
79
- │ SocketGateway.publish() │
80
- │ ↓ (ошибка при отправке события) │
81
- │ SocketError.fromUnknown() │
82
- │ ↓ │
83
- │ Логирование + сохранение в Redis │
84
- │ ↓ (опционально) │
85
- │ Отправка события ERROR на фронт через socket │
86
- │ │
87
- └─────────────────────────────────────────────────────────────┘
88
- ```
89
-
90
- ### Принципы работы
91
-
92
- 1. **Глобальные фильтры** - все ошибки обрабатываются автоматически через глобальные фильтры
93
- 2. **Без catch блоков в контроллерах** - контроллеры не должны иметь try-catch блоков
94
- 3. **Автоматическое преобразование** - ошибки автоматически преобразуются между контекстами
95
- 4. **Сохранение типа и статуса** - тип ошибки и статус-код сохраняются при преобразовании
96
-
97
- ---
98
-
99
- ## 🚨 Классы ошибок
100
-
101
- ### HttpError
102
-
103
- Класс ошибок для HTTP контекста. Используется в `api-service` для обработки HTTP запросов.
104
-
105
- **Расположение:** `src/errors/http.error.ts`
106
-
107
- **Наследование:** `HttpException` (NestJS)
108
-
109
- **Особенности:**
110
- - Автоматически преобразует `RpcException`/`RpcError` в HTTP ошибку
111
- - Автоматически преобразует `SocketError` в HTTP ошибку
112
- - Сохраняет правильный HTTP статус-код
113
- - Включает stack trace в ответе, если он доступен в исходной ошибке
114
-
115
- **Пример использования:**
116
-
117
- ```typescript
118
- // В HTTP контроллере (БЕЗ catch блока)
119
- @Get("/analytics/global")
120
- async getGlobal() {
121
- // Ошибка автоматически обработается UnifiedExceptionFilter
122
- return await this.analyticsService.getGlobalData();
123
- }
124
-
125
- // В сервисе (опционально для логирования)
126
- async getGlobalData() {
127
- try {
128
- const data = await this.rabbitMQService.publish(...);
129
- return data;
130
- } catch (error) {
131
- // Дополнительное логирование (опционально)
132
- this.logger.error(`Ошибка RPC: ${error}`);
133
- // Пробрасываем дальше - фильтр преобразует в HttpError
134
- throw error;
135
- }
136
- }
137
- ```
138
-
139
- **Преобразование ошибок:**
140
-
141
- ```typescript
142
- // HttpError.fromUnknown() автоматически обрабатывает:
143
- - HttpError → возвращает как есть (с опциональным descriptionPrefix)
144
- - HttpException → извлекает статус и тип, сохраняет оригинальный формат message (массив остается массивом в getResponse())
145
- - RpcException/RpcError → преобразует в HTTP с правильным статусом
146
- - SocketError → преобразует в HTTP
147
- - Error → определяет тип и создает HttpError
148
- - Объекты с полем message → преобразует в HttpError
149
- - Неизвестные типы → преобразует в строку и создает HttpError
150
- ```
151
-
152
- **Важно:**
153
- - При преобразовании `HttpException` с массивом сообщений, оригинальный массив сохраняется в `getResponse()`, но для внутреннего `message` используется строка (массив объединяется через `"; "`).
154
- - Опциональный параметр `descriptionPrefix` добавляется к сообщению ошибки для дополнительного контекста.
155
-
156
- ### RpcError
157
-
158
- Класс ошибок для RabbitMQ RPC контекста. Используется в `analytics-service`, `data-service`, `token-service`.
159
-
160
- **Расположение:** `src/errors/rpc.error.ts`
161
-
162
- **Наследование:** `RpcException` (NestJS)
163
-
164
- **Особенности:**
165
- - Автоматически определяет тип ошибки (временная/постоянная)
166
- - Сохраняет статус-код для будущего преобразования в HTTP
167
- - Сериализуется через RabbitMQ с сохранением типа и сообщения
168
- - Используется в `RpcExceptionFilter` для retry/DLX логики
169
-
170
- **Типы ошибок:**
171
-
172
- **Временные (retry):**
173
- - `RPC_TIMEOUT` - таймаут соединения
174
- - `SERVICE_UNAVAILABLE` - сервис недоступен
175
- - `RPC_SERVICE_UNAVAILABLE` - RPC сервис недоступен
176
-
177
- **Постоянные (DLX сразу):**
178
- - `BAD_REQUEST` - неправильный запрос
179
- - `UNAUTHORIZED` - ошибка авторизации
180
- - `FORBIDDEN` - ошибка доступа
181
- - `NOT_FOUND` - ресурс не найден
182
- - `VALIDATION_ERROR` - ошибка валидации
183
- - `RPC_VALIDATION_ERROR` - ошибка валидации RPC
184
-
185
- **Методы:**
186
- - `isTransient(): boolean` - проверяет, является ли ошибка временной (требует retry)
187
-
188
- **Пример использования:**
189
-
190
- ```typescript
191
- // В RPC контроллере (БЕЗ catch блока)
192
- @MessagePattern(ROUTING_KEYS.ANALYTICS_GLOBAL)
193
- async getGlobalData(
194
- @Payload() _: GlobalDataIncomeDto,
195
- @Ctx() ctx: RmqContext,
196
- ): Promise<GlobalDataOutcomeDto> {
197
- // Ошибка автоматически обработается UnifiedExceptionFilter
198
- const data = await this.analyticsService.getGlobalData();
199
- this.acknowledge(ctx);
200
- return data;
201
- }
202
- ```
203
-
204
- **Преобразование ошибок:**
205
-
206
- ```typescript
207
- // RpcError.fromUnknown() автоматически:
208
- - Распознает ошибки с свойством isAxiosError и преобразует их с сохранением типа и статуса
209
- - Определяет тип ошибки из сообщения
210
- - Классифицирует как временную или постоянную
211
- - Сохраняет оригинальную ошибку для логирования
212
- - Обрабатывает сериализованные объекты ошибок из RabbitMQ
213
- ```
214
-
215
- **Интеграция с AxiosError:**
216
-
217
- `RpcError.fromUnknown()` автоматически распознает ошибки с свойством `isAxiosError` и преобразует их в `RpcError` с сохранением типа ошибки и статус-кода из оригинальной Axios ошибки.
218
-
219
- ### SocketError
220
-
221
- Класс ошибок для WebSocket контекста. Используется в `api-service` для обработки ошибок Socket.io.
222
-
223
- **Расположение:** `src/errors/socket.error.ts`
224
-
225
- **Наследование:** `HttpException` (NestJS) - для совместимости с HTTP фильтрами
226
-
227
- **Особенности:**
228
- - Используется для обработки ошибок при отправке событий через Socket.io
229
- - Логируется и сохраняется в Redis
230
- - Может быть отправлена на фронт через событие `SOCKET_EVENTS.ERROR`
231
- - Не связана с HTTP или RPC ошибками
232
-
233
- **Пример использования:**
234
-
235
- ```typescript
236
- // В SocketGateway
237
- async publish(userId: string, event: SOCKET_EVENTS, payload: unknown) {
238
- try {
239
- await this.io.timeout(5000).to(`user:${userId}`).emitWithAck(event, payload);
240
- } catch (error) {
241
- const socketError = SocketError.fromUnknown(error);
242
-
243
- // Логирование
244
- this.logger.error(`Ошибка отправки события: ${socketError.message}`);
245
-
246
- // Сохранение в Redis
247
- await this.redisService.hSet(
248
- REDIS_H_KEYS.SOCKET_ERROR,
249
- userId,
250
- socketError.message,
251
- );
252
-
253
- // Опционально: отправка ошибки на фронт
254
- await this.io.to(`user:${userId}`).emit(SOCKET_EVENTS.ERROR, {
255
- status: "error",
256
- message: socketError.message,
257
- });
258
-
259
- throw socketError;
260
- }
261
- }
262
- ```
263
-
264
- ### JsonRpcException
265
-
266
- Класс исключений для JSON-RPC 2.0 протокола. Используется для обработки ошибок в формате JSON-RPC.
267
-
268
- **Расположение:** `src/errors/json-rpc.error.ts`
269
-
270
- **Наследование:** `HttpException` (NestJS) - для совместимости с NestJS фильтрами
271
-
272
- **Особенности:**
273
- - Поддерживает стандартные коды ошибок JSON-RPC 2.0
274
- - Автоматически маппит HTTP статусы на JSON-RPC коды ошибок
275
- - Поддерживает дополнительные данные в поле `data`
276
- - Может быть создан из `HttpException` или обычной `Error`
277
- - Возвращает ответ в формате JSON-RPC 2.0
278
-
279
- **Пример использования:**
280
-
281
- ```typescript
282
- import { JsonRpcException, JsonRpcErrorCode } from "@packages/nest-common";
283
-
284
- // Создание исключения с кодом ошибки
285
- throw new JsonRpcException(
286
- JsonRpcErrorCode.INVALID_REQUEST,
287
- "Invalid request parameters",
288
- { requestId: "123", details: "Missing required field" },
289
- );
290
-
291
- // Создание из HttpException
292
- const httpException = new HttpException("Not found", HttpStatus.NOT_FOUND);
293
- const rpcException = JsonRpcException.fromHttpException(httpException, "request-id");
294
-
295
- // Создание из обычной Error
296
- const error = new Error("Something went wrong");
297
- const rpcException = JsonRpcException.fromError(error, "request-id");
298
- ```
299
-
300
- **Коды ошибок JSON-RPC 2.0:**
301
- - `PARSE_ERROR` (-32700) - Ошибка парсинга JSON
302
- - `INVALID_REQUEST` (-32600) - Невалидный запрос
303
- - `METHOD_NOT_FOUND` (-32601) - Метод не найден
304
- - `INVALID_PARAMS` (-32602) - Невалидные параметры
305
- - `INTERNAL_ERROR` (-32603) - Внутренняя ошибка сервера
306
- - `UNAUTHORIZED` (-32001) - Неавторизованный доступ
307
- - `FORBIDDEN` (-32002) - Доступ запрещен
308
- - `NOT_FOUND` (-32003) - Ресурс не найден
309
- - И другие кастомные коды (-32004 до -32099)
310
-
311
- ---
312
-
313
- ## 🛡️ Глобальные фильтры
314
-
315
- ### UnifiedExceptionFilter
316
-
317
- Единый глобальный фильтр для обработки HTTP и RPC ошибок. Автоматически определяет тип контекста и использует соответствующий обработчик.
318
-
319
- **Расположение:** `src/filters/unified-exception.filter.ts`
320
-
321
- ### JsonRpcExceptionFilter
322
-
323
- Фильтр исключений для обработки ошибок в формате JSON-RPC 2.0. Преобразует все исключения NestJS в формат JSON-RPC 2.0.
324
-
325
- **Расположение:** `src/filters/json-rpc-exception.filter.ts`
326
-
327
- **Особенности:**
328
- - Автоматически извлекает `requestId` из тела запроса
329
- - Преобразует `JsonRpcException` в JSON-RPC формат ответа
330
- - Преобразует обычные `Error` в JSON-RPC формат с автоматическим определением статуса
331
- - Обрабатывает неизвестные типы исключений
332
- - Логирует все ошибки с контекстом
333
-
334
- **Использование:**
335
-
336
- ```typescript
337
- import { Controller, Post, Body, UseFilters } from "@nestjs/common";
338
- import { JsonRpcExceptionFilter, JsonRpcValidationPipe } from "@packages/nest-common";
339
- import type { JsonRpcRequest } from "@packages/nest-common";
340
-
341
- @Controller("mcp")
342
- @UseFilters(JsonRpcExceptionFilter)
343
- export default class McpController extends BaseController {
344
- constructor(private readonly logger: LoggerService) {
345
- super();
346
- }
347
-
348
- @Post()
349
- async handleJsonRpcRequest(
350
- @Body(JsonRpcValidationPipe) request: JsonRpcRequest,
351
- ) {
352
- // Обработка запроса
353
- if (request.method === "test.method") {
354
- return {
355
- jsonrpc: "2.0",
356
- id: request.id,
357
- result: { success: true },
358
- };
359
- }
360
-
361
- throw new JsonRpcException(
362
- JsonRpcErrorCode.METHOD_NOT_FOUND,
363
- `Method ${request.method} not found`,
364
- );
365
- }
366
- }
367
- ```
368
-
369
- **Регистрация глобально:**
370
-
371
- ```typescript
372
- // В main.ts
373
- import { JsonRpcExceptionFilter } from "@packages/nest-common";
374
-
375
- async function bootstrap() {
376
- const app = await NestFactory.create(AppModule);
377
- const logger = await connectLogger(app, "ServiceName");
378
-
379
- app.useGlobalFilters(new JsonRpcExceptionFilter(logger));
380
-
381
- await app.listen(PORT);
382
- }
383
- ```
384
-
385
- **Регистрация:**
386
-
387
- ```typescript
388
- // В main.ts микросервиса
389
- import { UnifiedExceptionFilter } from "@packages/nest-common";
390
-
391
- async function bootstrap() {
392
- const app = await NestFactory.create(AppModule);
393
- const logger = await connectLogger(app, "ServiceName");
394
-
395
- // Регистрируем глобальный фильтр
396
- // Для RPC контекста можно передать dlxExchange для DLX логики
397
- app.useGlobalFilters(
398
- new UnifiedExceptionFilter(logger, dlxExchange), // dlxExchange опционально
399
- );
400
-
401
- await app.listen(PORT);
402
- }
403
- ```
404
-
405
- **Логика работы:**
406
-
407
- 1. Определяет тип контекста (HTTP или RPC)
408
- 2. Использует соответствующий обработчик:
409
- - **HTTP контекст** → `HttpExceptionFilter.handleException()`
410
- - **RPC контекст** → `RpcExceptionFilter.handleException()`
411
- 3. Обработчики преобразуют ошибки и формируют ответы
412
-
413
- **Внутренние обработчики:**
414
-
415
- - **HttpExceptionFilter** (`src/filters/http-exception-handler.ts`) - обрабатывает HTTP ошибки
416
- - **RpcExceptionFilter** (`src/filters/rpc-exception-handler.ts`) - обрабатывает RPC ошибки с поддержкой retry/DLX
417
-
418
- **Формат HTTP ответа:**
419
-
420
- ```json
421
- {
422
- "statusCode": 500,
423
- "timestamp": "2024-01-01T00:00:00.000Z",
424
- "path": "/api/analytics/global",
425
- "error": "INTERNAL_SERVER_ERROR",
426
- "message": "Ошибка получения данных",
427
- "stack": "..." // Если доступен в исходной ошибке
428
- }
429
- ```
430
-
431
- **Retry логика для RPC:**
432
-
433
- ```typescript
434
- // Постоянная ошибка (BAD_REQUEST, UNAUTHORIZED, etc.)
435
- if (!isTransient) {
436
- // Сразу в DLX, не тратим попытки retry
437
- channel.publish(dlxExchange, routingKey, content);
438
- channel.ack(msg);
439
- throw rpcError; // Отправляем клиенту
440
- }
441
-
442
- // Временная ошибка (TIMEOUT, SERVICE_UNAVAILABLE)
443
- if (retries < MAX_RETRIES) {
444
- // Отправляем в retry очередь
445
- channel.nack(msg, false, false);
446
- return; // НЕ throw - сообщение будет обработано повторно
447
- } else {
448
- // Превышен лимит - в DLX
449
- channel.publish(dlxExchange, routingKey, content);
450
- channel.ack(msg);
451
- throw rpcError; // Отправляем клиенту
452
- }
453
- ```
454
-
455
- ---
456
-
457
- ## 🔍 Перехватчики
458
-
459
- Перехватчики (Interceptors) используются для логирования и мониторинга всех запросов в приложении. Они автоматически перехватывают запросы до и после их выполнения, позволяя логировать метрики производительности и отслеживать все входящие запросы.
460
-
461
- ### UnifiedInterceptor
462
-
463
- Единый глобальный перехватчик для логирования HTTP и RPC запросов. Автоматически определяет тип контекста и использует соответствующий обработчик.
464
-
465
- **Расположение:** `src/interceptors/unified.interceptor.ts`
466
-
467
- **Особенности:**
468
- - Автоматически определяет тип контекста (HTTP или RPC)
469
- - Использует соответствующий обработчик для логирования
470
- - Логирует все входящие запросы с метриками производительности
471
- - Работает с любыми эндпоинтами автоматически
472
-
473
- **Регистрация:**
474
-
475
- ```typescript
476
- // В main.ts микросервиса
477
- import { UnifiedInterceptor } from "@packages/nest-common";
478
- import { LoggerService } from "@makebelieve21213-packages/logger";
479
-
480
- async function bootstrap() {
481
- const app = await NestFactory.create(AppModule);
482
- const logger = await connectLogger(app, "ServiceName");
483
-
484
- // Регистрируем глобальный перехватчик
485
- app.useGlobalInterceptors(new UnifiedInterceptor(logger));
486
-
487
- await app.listen(PORT);
488
- }
489
- ```
490
-
491
- **Внутренние обработчики:**
492
-
493
- - **HttpLoggingInterceptor** (`src/interceptors/http-logging.interceptor.ts`) - логирует HTTP запросы
494
- - **RpcLoggingInterceptor** (`src/interceptors/rpc-logging.interceptor.ts`) - логирует RPC запросы
495
- - **WebSocketLoggingInterceptor** (`src/interceptors/websocket-logging.interceptor.ts`) - логирует WebSocket запросы
496
-
497
- **Логика работы:**
498
-
499
- 1. Определяет тип контекста (HTTP, RPC или WebSocket)
500
- 2. Использует соответствующий обработчик:
501
- - **HTTP контекст** → `HttpLoggingInterceptor.intercept()`
502
- - **RPC контекст** → `RpcLoggingInterceptor.intercept()`
503
- 3. Обработчики логируют запросы с метриками времени выполнения
504
-
505
- **Формат логов HTTP:**
506
-
507
- ```
508
- [HTTP] Incoming request [GET /api/analytics/global] from 127.0.0.1 (Mozilla/5.0...)
509
- [HTTP] Request completed [GET /api/analytics/global] 200 45ms
510
- [HTTP] Request failed [GET /api/analytics/global] 500 120ms - Internal server error
511
- ```
512
-
513
- **Формат логов RPC:**
514
-
515
- ```
516
- [RPC] Incoming request [pattern: analytics.global]
517
- [RPC] Request completed [pattern: analytics.global, duration: 45ms]
518
- [RPC] Request failed [pattern: analytics.global, duration: 120ms, error: Service unavailable]
519
- ```
520
-
521
- ---
522
-
523
- ## 🎛️ Базовые классы
524
-
525
- ### BaseController
526
-
527
- Базовый контроллер для всех контроллеров проекта. Предоставляет общую функциональность для HTTP и RPC контроллеров.
528
-
529
- **Расположение:** `src/base/base.controller.ts`
530
-
531
- **Особенности:**
532
- - Предоставляет общие методы для HTTP и RPC контроллеров
533
- - Метод `acknowledge(ctx: RmqContext)` для подтверждения RPC сообщений
534
- - Автоматически настраивает контекст логирования на имя класса контроллера
535
-
536
- **Методы:**
537
- - `protected acknowledge(ctx: RmqContext): void` - отправляет acknowledge сообщение в RabbitMQ
538
-
539
- **Пример использования:**
540
-
541
- ```typescript
542
- import { Controller, Get } from "@nestjs/common";
543
- import { BaseController } from "@packages/nest-common";
544
- import { LoggerService } from "@makebelieve21213-packages/logger";
545
-
546
- @Controller("analytics")
547
- export default class AnalyticsController extends BaseController {
548
- constructor(
549
- private readonly analyticsService: AnalyticsService,
550
- logger: LoggerService,
551
- ) {
552
- super(logger);
553
- }
554
-
555
- @Get("global")
556
- async getGlobal() {
557
- // БЕЗ try-catch - глобальный фильтр все обработает
558
- return await this.analyticsService.getGlobalData();
559
- }
560
- }
561
- ```
562
-
563
- ---
564
-
565
- ## 🔧 Пайпы валидации
566
-
567
- ### HttpValidationPipe
568
-
569
- Валидация DTO для HTTP контроллеров с использованием `class-validator`.
570
-
571
- **Расположение:** `src/pipes/http-validation.pipe.ts`
572
-
573
- **Особенности:**
574
- - Автоматическое преобразование plain objects в DTO экземпляры через `plainToInstance` с `enableImplicitConversion: true`
575
- - Валидация с `whitelist: true` и `forbidNonWhitelisted: true`
576
- - Выбрасывает `BadRequestException` при ошибках валидации
577
- - Сообщения об ошибках объединяются через `"; "`
578
-
579
- **Использование:**
580
-
581
- ```typescript
582
- import { Controller, Post, Body } from "@nestjs/common";
583
- import { HttpValidationPipe } from "@packages/nest-common";
584
- import { CreateDto } from "@packages/dtos";
585
-
586
- @Controller("tokens")
587
- export default class TokenController extends BaseController {
588
- @Post("create")
589
- @UsePipes(new HttpValidationPipe(CreateDto))
590
- async create(@Body() dto: CreateDto) {
591
- return await this.tokenService.create(dto);
592
- }
593
- }
594
- ```
595
-
596
- ### RpcValidationPipe
597
-
598
- Валидация DTO для RabbitMQ микросервисов с использованием `class-validator`.
599
-
600
- **Расположение:** `src/pipes/rpc-validation.pipe.ts`
601
-
602
- **Особенности:**
603
- - Автоматическое преобразование plain objects в DTO экземпляры через `plainToInstance` с `enableImplicitConversion: true`
604
- - Обрабатывает случаи, когда value может быть `undefined` или `null` (для пустых DTO передает пустой объект)
605
- - Автоматически извлекает и исключает `correlationId` и `correlationTimestamp` из сообщения перед валидацией (для поддержки идемпотентности)
606
- - Валидация с `whitelist: true` и `forbidNonWhitelisted: true`
607
- - Выбрасывает `RpcException` при ошибках валидации
608
- - Сообщения об ошибках объединяются через `"; "`
609
-
610
- **Использование:**
611
-
612
- ```typescript
613
- import { Controller } from "@nestjs/common";
614
- import { MessagePattern, Payload } from "@nestjs/microservices";
615
- import { RpcValidationPipe } from "@packages/nest-common";
616
- import { GlobalDataIncomeDto } from "@packages/dtos";
617
-
618
- @Controller()
619
- export default class AnalyticsController extends BaseController {
620
- @MessagePattern(ROUTING_KEYS.ANALYTICS_GLOBAL)
621
- @UsePipes(new RpcValidationPipe(GlobalDataIncomeDto))
622
- async getGlobalData(@Payload() dto: GlobalDataIncomeDto) {
623
- return await this.analyticsService.getGlobalData();
624
- }
625
- }
626
- ```
627
-
628
- **Обработка correlationId и correlationTimestamp:**
629
-
630
- Pipe автоматически извлекает поля `correlationId` и `correlationTimestamp` из входящего сообщения (если они присутствуют) и исключает их из валидации DTO. Это позволяет использовать эти поля для идемпотентности без необходимости добавлять их в DTO классы.
631
-
632
- ### FileValidationPipe
633
-
634
- Валидация загруженных файлов с проверкой размера, MIME типа и расширения.
635
-
636
- **Расположение:** `src/pipes/file-validation.pipe.ts`
637
-
638
- **Особенности:**
639
- - Валидирует одиночные файлы и массивы файлов
640
- - Проверяет размер файла (по умолчанию максимум 10MB)
641
- - Проверяет MIME тип файла
642
- - Проверяет расширение файла
643
- - Выбрасывает `BadRequestException` при ошибках валидации
644
-
645
- **Использование:**
646
-
647
- ```typescript
648
- import { Controller, Post, UploadedFile, UseInterceptors } from "@nestjs/common";
649
- import { FileInterceptor } from "@nestjs/platform-express";
650
- import { FileValidationPipe } from "@packages/nest-common";
651
-
652
- @Controller("upload")
653
- export default class UploadController extends BaseController {
654
- @Post("file")
655
- @UseInterceptors(FileInterceptor("file"))
656
- async uploadFile(
657
- @UploadedFile(
658
- new FileValidationPipe({
659
- maxSize: 5 * 1024 * 1024, // 5MB
660
- allowedMimeTypes: ["image/jpeg", "image/png"],
661
- allowedExtensions: ["jpg", "jpeg", "png"],
662
- }),
663
- )
664
- file: MulterFile,
665
- ) {
666
- return await this.uploadService.saveFile(file);
667
- }
668
- }
669
- ```
670
-
671
- ### QueryValidationPipe
672
-
673
- Валидация query параметров HTTP запросов с использованием `class-validator`.
674
-
675
- **Расположение:** `src/pipes/query-validation.pipe.ts`
676
-
677
- **Особенности:**
678
- - Автоматическое преобразование plain objects в DTO экземпляры через `plainToInstance`
679
- - Валидация только для query параметров (`metadata.type === "query"`)
680
- - Выбрасывает `BadRequestException` с детальными ошибками валидации
681
- - Поддерживает вложенные объекты и массивы
682
-
683
- **Использование:**
684
-
685
- ```typescript
686
- import { Controller, Get, Query } from "@nestjs/common";
687
- import { QueryValidationPipe } from "@packages/nest-common";
688
- import { PaginationDto } from "@packages/dtos";
689
-
690
- @Controller("users")
691
- export default class UsersController extends BaseController {
692
- @Get()
693
- async getUsers(
694
- @Query(new QueryValidationPipe(PaginationDto)) query: PaginationDto,
695
- ) {
696
- return await this.usersService.findAll(query);
697
- }
698
- }
699
- ```
700
-
701
- ### HeaderValidationPipe
702
-
703
- Валидация заголовков HTTP запросов с использованием `class-validator`.
704
-
705
- **Расположение:** `src/pipes/header-validation.pipe.ts`
706
-
707
- **Особенности:**
708
- - Автоматическое преобразование plain objects в DTO экземпляры через `plainToInstance`
709
- - Валидация только для custom параметров (`metadata.type === "custom"`)
710
- - Выбрасывает `BadRequestException` с детальными ошибками валидации
711
- - Поддерживает вложенные объекты и массивы
712
-
713
- **Использование:**
714
-
715
- ```typescript
716
- import { Controller, Get, Headers } from "@nestjs/common";
717
- import { HeaderValidationPipe } from "@packages/nest-common";
718
- import { ApiHeadersDto } from "@packages/dtos";
719
-
720
- @Controller("api")
721
- export default class ApiController extends BaseController {
722
- @Get("data")
723
- async getData(
724
- @Headers(new HeaderValidationPipe(ApiHeadersDto)) headers: ApiHeadersDto,
725
- ) {
726
- return await this.apiService.getData(headers);
727
- }
728
- }
729
- ```
730
-
731
- ### JsonRpcValidationPipe
732
-
733
- Валидация JSON-RPC 2.0 запросов согласно спецификации протокола.
734
-
735
- **Расположение:** `src/pipes/json-rpc-validation.pipe.ts`
736
-
737
- **Особенности:**
738
- - Проверяет структуру запроса согласно спецификации JSON-RPC 2.0
739
- - Валидирует версию протокола (`jsonrpc: "2.0"`)
740
- - Проверяет наличие и тип поля `method` (должно быть строкой)
741
- - Валидирует тип поля `id` (должно быть string, number или null)
742
- - Валидирует тип поля `params` (должно быть object или undefined)
743
- - Игнорирует объекты с NestJS метаданными (constructorRef, handler, contextType)
744
- - Выбрасывает `JsonRpcException` при ошибках валидации
745
-
746
- **Использование:**
747
-
748
- ```typescript
749
- import { Controller, Post, Body } from "@nestjs/common";
750
- import { JsonRpcValidationPipe, JsonRpcExceptionFilter } from "@packages/nest-common";
751
- import type { JsonRpcRequest } from "@packages/nest-common";
752
-
753
- @Controller("mcp")
754
- @UseFilters(JsonRpcExceptionFilter)
755
- export default class McpController extends BaseController {
756
- @Post()
757
- async handleJsonRpcRequest(
758
- @Body(JsonRpcValidationPipe) request: JsonRpcRequest,
759
- ) {
760
- // Обработка JSON-RPC запроса
761
- return {
762
- jsonrpc: "2.0",
763
- id: request.id,
764
- result: { /* результат */ },
765
- };
766
- }
767
- }
768
- ```
769
-
770
- ---
771
-
772
- ## 🔍 Перехватчики (дополнительные)
773
-
774
- ### ResponseInterceptor
775
-
776
- Перехватчик для стандартизации формата HTTP ответов. Автоматически оборачивает ответы в стандартный формат `StandardResponse`.
777
-
778
- **Расположение:** `src/interceptors/response.interceptor.ts`
779
-
780
- **Особенности:**
781
- - Автоматически оборачивает ответы в формат `{ success: true, data: T, meta: {...} }`
782
- - Добавляет метаданные: timestamp, path, requestId
783
- - Если ответ уже в стандартном формате, возвращает как есть
784
- - Работает только с HTTP контекстом
785
-
786
- **Использование:**
787
-
788
- ```typescript
789
- import { Controller, Get, UseInterceptors } from "@nestjs/common";
790
- import { ResponseInterceptor } from "@packages/nest-common";
791
-
792
- @Controller("users")
793
- @UseInterceptors(ResponseInterceptor)
794
- export default class UsersController extends BaseController {
795
- @Get()
796
- async getUsers() {
797
- // Ответ автоматически обернется в StandardResponse
798
- return await this.usersService.findAll();
799
- }
800
- }
801
- ```
802
-
803
- **Формат ответа:**
804
-
805
- ```json
806
- {
807
- "success": true,
808
- "data": { /* ваши данные */ },
809
- "meta": {
810
- "timestamp": "2024-01-01T00:00:00.000Z",
811
- "path": "/api/users",
812
- "requestId": "req-123"
813
- }
814
- }
815
- ```
816
-
817
- ### SerializeInterceptor
818
-
819
- Перехватчик для сериализации ответов с исключением чувствительных данных на основе DTO классов.
820
-
821
- **Расположение:** `src/interceptors/serialize.interceptor.ts`
822
-
823
- **Особенности:**
824
- - Использует декоратор `@Serialize()` для указания DTO класса
825
- - Автоматически исключает поля, не помеченные `@Expose()` в DTO
826
- - Поддерживает массивы данных
827
- - Работает с `class-transformer` и `excludeExtraneousValues: true`
828
-
829
- **Использование:**
830
-
831
- ```typescript
832
- import { Controller, Get, UseInterceptors } from "@nestjs/common";
833
- import { SerializeInterceptor, Serialize } from "@packages/nest-common";
834
- import { UserResponseDto } from "@packages/dtos";
835
-
836
- @Controller("users")
837
- @UseInterceptors(SerializeInterceptor)
838
- export default class UsersController extends BaseController {
839
- @Get(":id")
840
- @Serialize(UserResponseDto) // Указываем DTO для сериализации
841
- async getUser(@Param("id") id: string) {
842
- // Поля, не помеченные @Expose() в UserResponseDto, будут исключены
843
- return await this.usersService.findOne(id);
844
- }
845
- }
846
- ```
847
-
848
- ### CompressionInterceptor
849
-
850
- Перехватчик для автоматического сжатия больших HTTP ответов с использованием gzip.
851
-
852
- **Расположение:** `src/interceptors/compression.interceptor.ts`
853
-
854
- **Особенности:**
855
- - Автоматически сжимает ответы больше заданного порога (по умолчанию 1024 байт)
856
- - Использует gzip сжатие
857
- - Проверяет эффективность сжатия (минимальный коэффициент сжатия 0.8)
858
- - Устанавливает заголовки `Content-Encoding: gzip` и `Content-Type: application/json`
859
- - Если сжатие неэффективно, возвращает исходные данные
860
-
861
- **Использование:**
862
-
863
- ```typescript
864
- import { Controller, Get, UseInterceptors } from "@nestjs/common";
865
- import { CompressionInterceptor } from "@packages/nest-common";
866
-
867
- @Controller("data")
868
- @UseInterceptors(new CompressionInterceptor(2048, 0.7)) // Порог 2KB, коэффициент 0.7
869
- export default class DataController extends BaseController {
870
- @Get("large")
871
- async getLargeData() {
872
- // Большие ответы автоматически сжимаются
873
- return await this.dataService.getLargeDataset();
874
- }
875
- }
876
- ```
877
-
878
- ### RequestIdResponseInterceptor
879
-
880
- Перехватчик для добавления Request ID в заголовки HTTP ответов.
881
-
882
- **Расположение:** `src/interceptors/request-id-response.interceptor.ts`
883
-
884
- **Особенности:**
885
- - Извлекает Request ID из заголовков запроса (`x-request-id`) или `request.id`
886
- - Добавляет Request ID в заголовок ответа `X-Request-ID`
887
- - Работает только с HTTP контекстом
888
-
889
- **Использование:**
890
-
891
- ```typescript
892
- import { Controller, Get, UseInterceptors } from "@nestjs/common";
893
- import { RequestIdResponseInterceptor } from "@packages/nest-common";
894
-
895
- @Controller("api")
896
- @UseInterceptors(RequestIdResponseInterceptor)
897
- export default class ApiController extends BaseController {
898
- @Get("data")
899
- async getData() {
900
- // Request ID автоматически добавится в заголовки ответа
901
- return await this.apiService.getData();
902
- }
903
- }
904
- ```
905
-
906
- ---
907
-
908
- ## 🛡️ Guards (Охранники)
909
-
910
- Guards используются для контроля доступа к эндпоинтам на основе аутентификации, авторизации и других условий.
911
-
912
- ### JwtAuthGuard
913
-
914
- Guard для проверки JWT токена и аутентификации пользователя.
915
-
916
- **Расположение:** `src/guards/jwt-auth.guard.ts`
917
-
918
- **Особенности:**
919
- - Проверяет наличие пользователя в `request.user`
920
- - Уважает декоратор `@Public()` - пропускает публичные эндпоинты
921
- - Выбрасывает `UnauthorizedException` если пользователь не аутентифицирован
922
-
923
- **Использование:**
924
-
925
- ```typescript
926
- import { Controller, Get, UseGuards } from "@nestjs/common";
927
- import { JwtAuthGuard } from "@packages/nest-common";
928
-
929
- @Controller("users")
930
- @UseGuards(JwtAuthGuard)
931
- export default class UsersController extends BaseController {
932
- @Get("profile")
933
- async getProfile(@Request() req) {
934
- // req.user доступен после прохождения JwtAuthGuard
935
- return await this.usersService.getProfile(req.user);
936
- }
937
- }
938
- ```
939
-
940
- ### ApiKeyGuard
941
-
942
- Guard для проверки API ключа в заголовках запроса.
943
-
944
- **Расположение:** `src/guards/api-key.guard.ts`
945
-
946
- **Особенности:**
947
- - Проверяет наличие API ключа в заголовке (по умолчанию `x-api-key`)
948
- - Уважает декоратор `@Public()` - пропускает публичные эндпоинты
949
- - Работает только с эндпоинтами, помеченными декоратором `@ApiKey()`
950
- - Опционально проверяет ключ против списка валидных ключей
951
- - Выбрасывает `UnauthorizedException` при отсутствии или невалидном ключе
952
-
953
- **Использование:**
954
-
955
- ```typescript
956
- import { Controller, Get, UseGuards } from "@nestjs/common";
957
- import { ApiKeyGuard, ApiKey } from "@packages/nest-common";
958
-
959
- @Controller("api")
960
- export default class ApiController extends BaseController {
961
- constructor() {
962
- // Создаем guard с валидными ключами
963
- super();
964
- }
965
-
966
- @Get("data")
967
- @UseGuards(new ApiKeyGuard(undefined, "x-api-key", new Set(["key1", "key2"])))
968
- @ApiKey() // Помечаем эндпоинт как требующий API ключ
969
- async getData() {
970
- return await this.apiService.getData();
971
- }
972
- }
973
- ```
974
-
975
- ### RolesGuard
976
-
977
- Guard для проверки ролей пользователя.
978
-
979
- **Расположение:** `src/guards/roles.guard.ts`
980
-
981
- **Особенности:**
982
- - Проверяет наличие требуемых ролей у пользователя
983
- - Использует декоратор `@Roles()` для указания требуемых ролей
984
- - Проверяет, что у пользователя есть хотя бы одна из указанных ролей
985
- - Выбрасывает `ForbiddenException` при недостаточных правах
986
-
987
- **Использование:**
988
-
989
- ```typescript
990
- import { Controller, Get, UseGuards } from "@nestjs/common";
991
- import { JwtAuthGuard, RolesGuard, Roles } from "@packages/nest-common";
992
-
993
- @Controller("admin")
994
- @UseGuards(JwtAuthGuard, RolesGuard)
995
- export default class AdminController extends BaseController {
996
- @Get("users")
997
- @Roles("admin", "moderator") // Требуются роли admin или moderator
998
- async getUsers() {
999
- return await this.adminService.getUsers();
1000
- }
1001
- }
1002
- ```
1003
-
1004
- ### PermissionsGuard
1005
-
1006
- Guard для проверки разрешений пользователя.
1007
-
1008
- **Расположение:** `src/guards/permissions.guard.ts`
1009
-
1010
- **Особенности:**
1011
- - Проверяет наличие требуемых разрешений у пользователя
1012
- - Использует декоратор `@Permissions()` для указания требуемых разрешений
1013
- - Проверяет, что у пользователя есть ВСЕ указанные разрешения
1014
- - Выбрасывает `ForbiddenException` при недостаточных правах
1015
-
1016
- **Использование:**
1017
-
1018
- ```typescript
1019
- import { Controller, Delete, UseGuards } from "@nestjs/common";
1020
- import { JwtAuthGuard, PermissionsGuard, Permissions } from "@packages/nest-common";
1021
-
1022
- @Controller("users")
1023
- @UseGuards(JwtAuthGuard, PermissionsGuard)
1024
- export default class UsersController extends BaseController {
1025
- @Delete(":id")
1026
- @Permissions("users:delete", "users:write") // Требуются ВСЕ указанные разрешения
1027
- async deleteUser(@Param("id") id: string) {
1028
- return await this.usersService.delete(id);
1029
- }
1030
- }
1031
- ```
1032
-
1033
- ### RateLimitGuard
1034
-
1035
- Guard для ограничения частоты запросов (Rate Limiting).
1036
-
1037
- **Расположение:** `src/guards/rate-limit.guard.ts`
1038
-
1039
- **Особенности:**
1040
- - Ограничивает количество запросов за временное окно
1041
- - По умолчанию: 100 запросов в минуту для аутентифицированных пользователей
1042
- - Для публичных эндпоинтов применяется более строгий лимит (50% от базового)
1043
- - Использует IP адрес и путь запроса для идентификации клиента
1044
- - Поддерживает кастомный генератор ключей
1045
- - Выбрасывает `HttpException` с кодом 429 (Too Many Requests) при превышении лимита
1046
- - Автоматически очищает устаревшие записи
1047
-
1048
- **Использование:**
1049
-
1050
- ```typescript
1051
- import { Controller, Get, UseGuards } from "@nestjs/common";
1052
- import { RateLimitGuard } from "@packages/nest-common";
1053
-
1054
- @Controller("api")
1055
- @UseGuards(new RateLimitGuard(undefined, 200, 60000)) // 200 запросов в минуту
1056
- export default class ApiController extends BaseController {
1057
- @Get("data")
1058
- async getData() {
1059
- return await this.apiService.getData();
1060
- }
1061
- }
1062
- ```
1063
-
1064
- ### WebSocketAuthGuard
1065
-
1066
- Guard для проверки аутентификации при подключении к WebSocket.
1067
-
1068
- **Расположение:** `src/guards/websocket-auth.guard.ts`
1069
-
1070
- **Особенности:**
1071
- - Проверяет наличие токена в данных подключения или handshake
1072
- - Поддерживает кастомный валидатор токена через коллбек `TokenValidator`
1073
- - Выбрасывает `WsException` при отсутствии или невалидном токене
1074
-
1075
- **Использование:**
1076
-
1077
- ```typescript
1078
- import { WebSocketGateway, SubscribeMessage, UseGuards } from "@nestjs/websockets";
1079
- import { WebSocketAuthGuard } from "@packages/nest-common";
1080
-
1081
- @WebSocketGateway()
1082
- export default class ChatGateway {
1083
- constructor(
1084
- @Inject(WebSocketAuthGuard)
1085
- private readonly authGuard: WebSocketAuthGuard,
1086
- ) {
1087
- // Создаем guard с валидатором токена
1088
- this.authGuard = new WebSocketAuthGuard(async (token, context) => {
1089
- return await this.authService.validateToken(token);
1090
- });
1091
- }
1092
-
1093
- @SubscribeMessage("message")
1094
- @UseGuards(this.authGuard)
1095
- async handleMessage(client: Socket, payload: unknown) {
1096
- // Токен проверен через WebSocketAuthGuard
1097
- return await this.chatService.sendMessage(payload);
1098
- }
1099
- }
1100
- ```
1101
-
1102
- ---
1103
-
1104
- ## 🎨 Декораторы
1105
-
1106
- Декораторы используются для добавления метаданных к эндпоинтам и контроллерам.
1107
-
1108
- ### @Public()
1109
-
1110
- Декоратор для пометки эндпоинта как публичного (без аутентификации).
1111
-
1112
- **Расположение:** `src/decorators/public.decorator.ts`
1113
-
1114
- **Использование:**
1115
-
1116
- ```typescript
1117
- import { Controller, Get } from "@nestjs/common";
1118
- import { Public } from "@packages/nest-common";
1119
-
1120
- @Controller("public")
1121
- export default class PublicController extends BaseController {
1122
- @Get("info")
1123
- @Public() // Эндпоинт доступен без аутентификации
1124
- async getInfo() {
1125
- return { message: "Public information" };
1126
- }
1127
- }
1128
- ```
1129
-
1130
- ### @Roles()
1131
-
1132
- Декоратор для указания ролей, необходимых для доступа к эндпоинту.
1133
-
1134
- **Расположение:** `src/decorators/roles.decorator.ts`
1135
-
1136
- **Использование:**
1137
-
1138
- ```typescript
1139
- import { Controller, Get } from "@nestjs/common";
1140
- import { Roles } from "@packages/nest-common";
1141
-
1142
- @Controller("admin")
1143
- export default class AdminController extends BaseController {
1144
- @Get("dashboard")
1145
- @Roles("admin", "moderator") // Требуются роли admin или moderator
1146
- async getDashboard() {
1147
- return await this.adminService.getDashboard();
1148
- }
1149
- }
1150
- ```
1151
-
1152
- ### @Permissions()
1153
-
1154
- Декоратор для указания разрешений, необходимых для доступа к эндпоинту.
1155
-
1156
- **Расположение:** `src/decorators/permissions.decorator.ts`
1157
-
1158
- **Использование:**
1159
-
1160
- ```typescript
1161
- import { Controller, Delete } from "@nestjs/common";
1162
- import { Permissions } from "@packages/nest-common";
1163
-
1164
- @Controller("users")
1165
- export default class UsersController extends BaseController {
1166
- @Delete(":id")
1167
- @Permissions("users:delete", "users:write") // Требуются ВСЕ указанные разрешения
1168
- async deleteUser(@Param("id") id: string) {
1169
- return await this.usersService.delete(id);
1170
- }
1171
- }
1172
- ```
1173
-
1174
- ### @ApiKey()
1175
-
1176
- Декоратор для пометки эндпоинта как требующего API ключ.
1177
-
1178
- **Расположение:** `src/decorators/api-key.decorator.ts`
1179
-
1180
- **Использование:**
1181
-
1182
- ```typescript
1183
- import { Controller, Get } from "@nestjs/common";
1184
- import { ApiKey } from "@packages/nest-common";
1185
-
1186
- @Controller("api")
1187
- export default class ApiController extends BaseController {
1188
- @Get("data")
1189
- @ApiKey() // Эндпоинт требует API ключ в заголовке x-api-key
1190
- async getData() {
1191
- return await this.apiService.getData();
1192
- }
1193
- }
1194
- ```
1195
-
1196
- ### @Serialize()
1197
-
1198
- Декоратор для указания класса DTO для сериализации ответа.
1199
-
1200
- **Расположение:** `src/decorators/serialize.decorator.ts`
1201
-
1202
- **Использование:**
1203
-
1204
- ```typescript
1205
- import { Controller, Get } from "@nestjs/common";
1206
- import { Serialize } from "@packages/nest-common";
1207
- import { UserResponseDto } from "@packages/dtos";
1208
-
1209
- @Controller("users")
1210
- export default class UsersController extends BaseController {
1211
- @Get(":id")
1212
- @Serialize(UserResponseDto) // Ответ будет сериализован через UserResponseDto
1213
- async getUser(@Param("id") id: string) {
1214
- return await this.usersService.findOne(id);
1215
- }
1216
- }
1217
- ```
1218
-
1219
- ---
1220
-
1221
- ## 🛠️ Утилиты
1222
-
1223
- ### validateEnv
1224
-
1225
- Функция валидации переменных окружения с использованием Joi.
1226
-
1227
- **Расположение:** `src/utils/env-validator.ts`
1228
-
1229
- **Особенности:**
1230
- - Валидация обязательных переменных окружения
1231
- - Выбрасывает `Error` с описанием отсутствующих ключей
1232
- - Возвращает валидированный объект env
1233
- - Использует `Joi.string().required()` для каждого обязательного ключа
1234
- - Разрешает дополнительные ключи через `unknown(true)`
1235
- - Использует `abortEarly: false` для получения всех ошибок валидации
1236
-
1237
- **Использование:**
1238
-
1239
- ```typescript
1240
- import { validateEnv } from "@packages/nest-common";
1241
-
1242
- const env = process.env;
1243
- const requiredKeys = ["DATABASE_URL", "REDIS_URL", "RABBITMQ_URL"];
1244
-
1245
- // Выбрасывает Error если ключи отсутствуют
1246
- const validatedEnv = validateEnv(env, requiredKeys);
1247
- ```
1248
-
1249
- ### Утилиты для работы с контекстом
1250
-
1251
- Функции для извлечения данных из контекста выполнения запроса.
1252
-
1253
- **Расположение:** `src/utils/context.utils.ts`
1254
-
1255
- **Функции:**
1256
- - `getUserFromContext(context: ExecutionContext): UserFromContext | undefined` - извлекает пользователя из контекста
1257
- - `getIpFromContext(context: ExecutionContext): string` - извлекает IP адрес из контекста
1258
- - `getUserAgentFromContext(context: ExecutionContext): string` - извлекает User-Agent из контекста
1259
- - `getRequestIdFromContext(context: ExecutionContext): string` - извлекает Request ID из контекста
1260
-
1261
- **Использование:**
1262
-
1263
- ```typescript
1264
- import { getUserFromContext, getIpFromContext } from "@packages/nest-common";
35
+ ## 📦 Установка
1265
36
 
1266
- @Controller("users")
1267
- export default class UsersController extends BaseController {
1268
- @Get("profile")
1269
- async getProfile(@ExecutionContext() context: ExecutionContext) {
1270
- const user = getUserFromContext(context);
1271
- const ip = getIpFromContext(context);
1272
-
1273
- return await this.usersService.getProfile(user, ip);
1274
- }
1275
- }
37
+ ```bash
38
+ npm install @makebelieve21213-packages/nest-common
1276
39
  ```
1277
40
 
1278
- ### createCorsOptions
1279
-
1280
- Функция для создания опций CORS для NestJS приложения.
1281
-
1282
- **Расположение:** `src/utils/cors.utils.ts`
41
+ ## 🔧 Быстрый старт
1283
42
 
1284
- **Особенности:**
1285
- - Настраивает CORS с разумными значениями по умолчанию
1286
- - Поддерживает кастомные настройки origin, methods, headers и т.д.
1287
- - По умолчанию разрешает все источники, основные HTTP методы и стандартные заголовки
1288
-
1289
- **Использование:**
43
+ ### Шаг 1: Регистрация глобальных фильтров и перехватчиков
1290
44
 
1291
45
  ```typescript
46
+ // main.ts
1292
47
  import { NestFactory } from "@nestjs/core";
1293
- import { createCorsOptions } from "@packages/nest-common";
48
+ import { UnifiedExceptionFilter, UnifiedInterceptor } from "@makebelieve21213-packages/nest-common";
49
+ import { LoggerService } from "@makebelieve21213-packages/logger";
1294
50
 
1295
51
  async function bootstrap() {
1296
52
  const app = await NestFactory.create(AppModule);
53
+ const logger = await connectLogger(app, "ServiceName");
1297
54
 
1298
- app.enableCors(createCorsOptions({
1299
- origin: ["https://example.com"],
1300
- credentials: true,
1301
- }));
1302
-
1303
- await app.listen(3000);
1304
- }
1305
- ```
1306
-
1307
- ### createCompressionOptions
1308
-
1309
- Функция для создания опций compression для NestJS приложения.
1310
-
1311
- **Расположение:** `src/utils/compression.utils.ts`
1312
-
1313
- **Особенности:**
1314
- - Настраивает compression middleware с разумными значениями по умолчанию
1315
- - По умолчанию сжимает только текстовые типы контента
1316
- - Поддерживает кастомные настройки уровня сжатия, порога и фильтров
1317
-
1318
- **Использование:**
1319
-
1320
- ```typescript
1321
- import { NestFactory } from "@nestjs/core";
1322
- import { createCompressionOptions } from "@packages/nest-common";
1323
- import compression from "compression";
1324
-
1325
- async function bootstrap() {
1326
- const app = await NestFactory.create(AppModule);
55
+ // Регистрируем глобальный фильтр ошибок
56
+ app.useGlobalFilters(new UnifiedExceptionFilter(logger, dlxExchange)); // dlxExchange опционально
1327
57
 
1328
- app.use(compression(createCompressionOptions({
1329
- level: 9, // Максимальное сжатие
1330
- threshold: 512, // Сжимать ответы больше 512 байт
1331
- })));
58
+ // Регистрируем глобальный перехватчик логирования
59
+ app.useGlobalInterceptors(new UnifiedInterceptor(logger));
1332
60
 
1333
61
  await app.listen(3000);
1334
62
  }
1335
63
  ```
1336
64
 
1337
- ### createVersioningOptions
1338
-
1339
- Функция для создания опций версионирования API для NestJS приложения.
1340
-
1341
- **Расположение:** `src/utils/versioning.utils.ts`
1342
-
1343
- **Особенности:**
1344
- - Поддерживает три стратегии версионирования: URI, Header, Media-Type
1345
- - Настраивает версионирование с указанием типа и версии по умолчанию
1346
-
1347
- **Использование:**
65
+ ### Шаг 2: HTTP контроллер
1348
66
 
1349
67
  ```typescript
1350
- import { NestFactory } from "@nestjs/core";
1351
- import { createVersioningOptions } from "@packages/nest-common";
68
+ import { Controller, Get } from "@nestjs/common";
69
+ import { BaseController } from "@makebelieve21213-packages/nest-common";
70
+ import { LoggerService } from "@makebelieve21213-packages/logger";
1352
71
 
1353
- async function bootstrap() {
1354
- const app = await NestFactory.create(AppModule);
1355
-
1356
- app.enableVersioning(createVersioningOptions({
1357
- type: "uri",
1358
- defaultVersion: "1",
1359
- }));
1360
-
1361
- await app.listen(3000);
72
+ @Controller("test")
73
+ export default class TestController extends BaseController {
74
+ constructor(
75
+ private readonly testService: TestService,
76
+ logger: LoggerService,
77
+ ) {
78
+ super(logger);
79
+ }
80
+
81
+ @Get("data")
82
+ async getData() {
83
+ // БЕЗ try-catch - глобальный фильтр все обработает
84
+ return await this.testService.getData();
85
+ }
1362
86
  }
1363
87
  ```
1364
88
 
1365
- ### Утилиты для работы с файлами
1366
-
1367
- Функции для валидации и работы с файлами.
1368
-
1369
- **Расположение:** `src/utils/file.utils.ts`
1370
-
1371
- **Функции:**
1372
- - `validateFile(file: MulterFile, options?: FileValidationOptions): FileValidationResult` - валидирует файл по заданным опциям
1373
- - `getFileExtension(filename: string): string` - получает расширение файла из имени
1374
- - `formatFileSize(bytes: number): string` - форматирует размер файла в читаемый формат
1375
-
1376
- **Использование:**
89
+ ### Шаг 3: RPC контроллер
1377
90
 
1378
91
  ```typescript
1379
- import { validateFile, formatFileSize } from "@packages/nest-common";
92
+ import { Controller } from "@nestjs/common";
93
+ import { MessagePattern, Payload, Ctx } from "@nestjs/microservices";
94
+ import { BaseController, RpcValidationPipe } from "@makebelieve21213-packages/nest-common";
95
+ import { LoggerService } from "@makebelieve21213-packages/logger";
1380
96
 
1381
- const result = validateFile(file, {
1382
- maxSize: 5 * 1024 * 1024, // 5MB
1383
- allowedMimeTypes: ["image/jpeg", "image/png"],
1384
- allowedExtensions: ["jpg", "jpeg", "png"],
1385
- });
97
+ @Controller()
98
+ export default class TestController extends BaseController {
99
+ constructor(
100
+ private readonly testService: TestService,
101
+ logger: LoggerService,
102
+ ) {
103
+ super(logger);
104
+ }
1386
105
 
1387
- if (!result.isValid) {
1388
- console.error(result.errors);
106
+ @MessagePattern("test.pattern")
107
+ @UsePipes(new RpcValidationPipe(TestRequestDto))
108
+ async getData(
109
+ @Payload() dto: TestRequestDto,
110
+ @Ctx() ctx: RmqContext,
111
+ ): Promise<TestResponseDto> {
112
+ const data = await this.testService.getData();
113
+ this.acknowledge(ctx);
114
+ return data;
115
+ }
1389
116
  }
1390
-
1391
- const size = formatFileSize(1024 * 1024); // "1 MB"
1392
117
  ```
1393
118
 
1394
- ### CircuitBreakerService
1395
-
1396
- Сервис для реализации паттерна Circuit Breaker для защиты от каскадных сбоев.
1397
-
1398
- **Расположение:** `src/utils/circuit-breaker.ts`
1399
-
1400
- **Особенности:**
1401
- - Реализует три состояния: CLOSED (нормальная работа), OPEN (разомкнут), HALF_OPEN (тестирование)
1402
- - Автоматически открывает circuit при превышении порога ошибок
1403
- - Автоматически закрывает circuit при успешных запросах в HALF_OPEN состоянии
1404
- - Поддерживает настройку порогов ошибок и успешных запросов
1405
- - Логирует переходы между состояниями
1406
-
1407
- **Использование:**
119
+ ### Шаг 4: Использование Guards
1408
120
 
1409
121
  ```typescript
1410
- import { CircuitBreakerService } from "@packages/nest-common";
1411
- import { LoggerService } from "@makebelieve21213-packages/logger";
122
+ import { Controller, Get, UseGuards } from "@nestjs/common";
123
+ import { JwtAuthGuard, RolesGuard, Roles, Public } from "@makebelieve21213-packages/nest-common";
1412
124
 
1413
- const circuitBreaker = new CircuitBreakerService(logger, {
1414
- failureThreshold: 5, // Открыть после 5 ошибок
1415
- successThreshold: 2, // Закрыть после 2 успешных запросов
1416
- resetTimeout: 60000, // Переход в HALF_OPEN через 1 минуту
1417
- });
125
+ @Controller("test")
126
+ @UseGuards(JwtAuthGuard, RolesGuard)
127
+ export default class TestController extends BaseController {
128
+ @Get("public")
129
+ @Public() // Публичный эндпоинт
130
+ async getPublicInfo() {
131
+ return { message: "Public information" };
132
+ }
1418
133
 
1419
- try {
1420
- const result = await circuitBreaker.execute("external-api", async () => {
1421
- return await externalApi.call();
1422
- });
1423
- } catch (error) {
1424
- // Circuit breaker открыт или произошла ошибка
134
+ @Get("profile")
135
+ async getProfile(@Request() req) {
136
+ return await this.testService.getProfile(req.user);
137
+ }
138
+
139
+ @Get("admin")
140
+ @Roles("admin", "moderator") // Требуются роли
141
+ async getAdminData() {
142
+ return await this.testService.getData();
143
+ }
1425
144
  }
1426
145
  ```
1427
146
 
1428
- ### getServicePath
147
+ ## 🔄 Система обработки ошибок
1429
148
 
1430
- Универсальная функция для определения пути в сервисе независимо от точки запуска и режима (dev/production).
149
+ Пакет предоставляет единую систему обработки ошибок для трех типов контекстов:
150
+ - **HTTP** - для HTTP контроллеров
151
+ - **RPC** - для RabbitMQ RPC запросов
152
+ - **WebSocket** - для Socket.io соединений
1431
153
 
1432
- **Расположение:** `src/utils/get-service-path.ts`
154
+ Все ошибки автоматически преобразуются между контекстами с сохранением типа и статус-кода.
1433
155
 
1434
- **Особенности:**
1435
- - Работает одинаково в dev (src) и production (dist) режимах
1436
- - Всегда использует пути относительно src/
1437
- - Поддерживает три типа путей: locales, srcRoot, file
1438
- - Автоматически находит корень сервиса по наличию папки src
156
+ ### Принципы работы
1439
157
 
1440
- **Использование:**
158
+ 1. **Глобальные фильтры** - все ошибки обрабатываются автоматически через `UnifiedExceptionFilter`
159
+ 2. **Без catch блоков в контроллерах** - контроллеры не должны иметь try-catch блоков
160
+ 3. **Автоматическое преобразование** - ошибки автоматически преобразуются между контекстами
161
+ 4. **Сохранение типа и статуса** - тип ошибки и статус-код сохраняются при преобразовании
1441
162
 
1442
- ```typescript
1443
- import { getServicePath } from "@packages/nest-common";
1444
-
1445
- // Получить путь к локалям
1446
- const localesPath = getServicePath({
1447
- serviceName: "api-service",
1448
- dirname: __dirname,
1449
- pathType: "locales",
1450
- relativePath: "locales",
1451
- });
163
+ ### Классы ошибок
1452
164
 
1453
- // Получить путь к файлу
1454
- const filePath = getServicePath({
1455
- serviceName: "api-service",
1456
- dirname: __dirname,
1457
- pathType: "file",
1458
- relativePath: "config/app.config.ts",
1459
- });
1460
- ```
165
+ **HttpError** - для HTTP контекста
166
+ - Автоматически преобразует `RpcException`/`RpcError` в HTTP ошибку
167
+ - Сохраняет правильный HTTP статус-код
168
+ - Метод: `HttpError.fromUnknown(error, descriptionPrefix?)`
1461
169
 
1462
- ---
170
+ **RpcError** - для RabbitMQ RPC контекста
171
+ - Автоматически определяет тип ошибки (временная/постоянная)
172
+ - Используется в `RpcExceptionFilter` для retry/DLX логики
173
+ - Метод: `RpcError.fromUnknown(error)`
174
+ - Метод: `isTransient(): boolean`
1463
175
 
1464
- ## 📝 Типы
176
+ **SocketError** - для WebSocket контекста
177
+ - Используется для обработки ошибок при отправке событий через Socket.io
178
+ - Метод: `SocketError.fromUnknown(error)`
1465
179
 
1466
- ### ErrorResponse
180
+ **JsonRpcException** - для JSON-RPC 2.0 протокола
181
+ - Поддерживает стандартные коды ошибок JSON-RPC 2.0
182
+ - Автоматически маппит HTTP статусы на JSON-RPC коды ошибок
1467
183
 
1468
- Интерфейс для HTTP ответов с ошибками.
184
+ ## 📚 API Reference
1469
185
 
1470
- **Расположение:** `src/types/http-response.ts`
186
+ ### Фильтры
1471
187
 
188
+ **UnifiedExceptionFilter** - единый глобальный фильтр для обработки HTTP и RPC ошибок
1472
189
  ```typescript
1473
- interface ErrorResponse {
1474
- statusCode: number;
1475
- timestamp: string;
1476
- path: string;
1477
- error: string;
1478
- message: string;
1479
- stack?: string; // Если доступен в исходной ошибке
1480
- }
190
+ app.useGlobalFilters(new UnifiedExceptionFilter(logger, dlxExchange));
1481
191
  ```
1482
192
 
1483
- ### StandardResponse
1484
-
1485
- Интерфейс стандартизированного ответа для HTTP запросов.
1486
-
1487
- **Расположение:** `src/types/http-response.ts`
1488
-
193
+ **JsonRpcExceptionFilter** - фильтр для обработки JSON-RPC 2.0 ошибок
1489
194
  ```typescript
1490
- interface StandardResponse<T = unknown> {
1491
- success: boolean;
1492
- data: T;
1493
- meta?: {
1494
- timestamp: string;
1495
- path?: string;
1496
- requestId?: string;
1497
- [key: string]: unknown;
1498
- };
1499
- }
195
+ @UseFilters(JsonRpcExceptionFilter)
1500
196
  ```
1501
197
 
1502
- ### RpcErrorType
1503
-
1504
- Enum типов ошибок RPC для классификации временных и постоянных ошибок.
1505
-
1506
- **Расположение:** `src/types/rpc-types.ts`
1507
-
1508
- **Временные ошибки (retry):**
1509
- - `RPC_TIMEOUT` - таймаут соединения
1510
- - `SERVICE_UNAVAILABLE` - сервис недоступен
1511
- - `RPC_SERVICE_UNAVAILABLE` - RPC сервис недоступен
1512
-
1513
- **Постоянные ошибки (DLX сразу):**
1514
- - `BAD_REQUEST` - неправильный запрос
1515
- - `UNAUTHORIZED` - ошибка авторизации
1516
- - `FORBIDDEN` - ошибка доступа
1517
- - `NOT_FOUND` - ресурс не найден
1518
- - `VALIDATION_ERROR` - ошибка валидации
1519
- - `RPC_VALIDATION_ERROR` - ошибка валидации RPC
1520
-
1521
- ### UserFromContext
1522
-
1523
- Интерфейс пользователя из контекста запроса.
1524
-
1525
- **Расположение:** `src/types/context-types.ts`
198
+ ### Перехватчики
1526
199
 
200
+ **UnifiedInterceptor** - единый глобальный перехватчик для логирования запросов
1527
201
  ```typescript
1528
- interface UserFromContext {
1529
- id?: string | number;
1530
- email?: string;
1531
- roles?: string[];
1532
- permissions?: string[];
1533
- [key: string]: unknown;
1534
- }
202
+ app.useGlobalInterceptors(new UnifiedInterceptor(logger));
1535
203
  ```
1536
204
 
1537
- ### FileValidationOptions и FileValidationResult
1538
-
1539
- Типы для валидации файлов.
1540
-
1541
- **Расположение:** `src/types/file-validation-types.ts`
1542
-
205
+ **ResponseInterceptor** - стандартизация формата HTTP ответов
1543
206
  ```typescript
1544
- interface FileValidationOptions {
1545
- maxSize?: number; // Максимальный размер в байтах
1546
- allowedMimeTypes?: string[]; // Разрешенные MIME типы
1547
- allowedExtensions?: string[]; // Разрешенные расширения файлов
1548
- }
1549
-
1550
- interface FileValidationResult {
1551
- isValid: boolean;
1552
- errors: string[];
1553
- }
207
+ @UseInterceptors(ResponseInterceptor)
1554
208
  ```
1555
209
 
1556
- ### CircuitBreakerState и CircuitBreakerOptions
1557
-
1558
- Типы для Circuit Breaker.
1559
-
1560
- **Расположение:** `src/types/circuit-breaker-types.ts`
1561
-
210
+ **SerializeInterceptor** - сериализация ответов с исключением чувствительных данных
1562
211
  ```typescript
1563
- enum CircuitBreakerState {
1564
- CLOSED = "CLOSED", // Нормальная работа
1565
- OPEN = "OPEN", // Разомкнут (ошибки превысили порог)
1566
- HALF_OPEN = "HALF_OPEN", // Полуоткрыт (тестирование восстановления)
1567
- }
1568
-
1569
- interface CircuitBreakerOptions {
1570
- failureThreshold?: number; // Порог ошибок для открытия
1571
- successThreshold?: number; // Порог успешных запросов для закрытия
1572
- timeout?: number; // Время ожидания в открытом состоянии (мс)
1573
- resetTimeout?: number; // Время до перехода в HALF_OPEN (мс)
1574
- }
212
+ @UseInterceptors(SerializeInterceptor)
213
+ @Serialize(UserResponseDto)
1575
214
  ```
1576
215
 
1577
- ### CorsOptionsConfig
1578
-
1579
- Тип для настройки CORS.
1580
-
1581
- **Расположение:** `src/types/cors-types.ts`
1582
-
216
+ **CompressionInterceptor** - автоматическое сжатие больших ответов
1583
217
  ```typescript
1584
- interface CorsOptionsConfig {
1585
- origin?: string | string[] | boolean | RegExp | ((origin: string) => boolean);
1586
- methods?: string | string[];
1587
- allowedHeaders?: string | string[];
1588
- exposedHeaders?: string | string[];
1589
- credentials?: boolean;
1590
- maxAge?: number;
1591
- preflightContinue?: boolean;
1592
- optionsSuccessStatus?: number;
1593
- }
218
+ @UseInterceptors(new CompressionInterceptor(2048, 0.7))
1594
219
  ```
1595
220
 
1596
- ### CompressionOptionsConfig
1597
-
1598
- Тип для настройки compression.
1599
-
1600
- **Расположение:** `src/types/compression-types.ts`
1601
-
221
+ **RequestIdResponseInterceptor** - добавление Request ID в заголовки ответов
1602
222
  ```typescript
1603
- interface CompressionOptionsConfig {
1604
- filter?: (req: Request, res: Response) => boolean;
1605
- level?: number;
1606
- threshold?: number;
1607
- chunkSize?: number;
1608
- windowBits?: number;
1609
- memLevel?: number;
1610
- strategy?: number;
1611
- dictionary?: Buffer | Buffer[] | string;
1612
- }
223
+ @UseInterceptors(RequestIdResponseInterceptor)
1613
224
  ```
1614
225
 
1615
- ### VersioningOptionsConfig
1616
-
1617
- Тип для настройки версионирования API.
1618
-
1619
- **Расположение:** `src/types/versioning-types.ts`
226
+ ### Пайпы валидации
1620
227
 
228
+ **HttpValidationPipe** - валидация DTO для HTTP контроллеров
1621
229
  ```typescript
1622
- type VersioningStrategy = "uri" | "header" | "media-type";
1623
-
1624
- interface VersioningOptionsConfig {
1625
- type: VersioningStrategy;
1626
- defaultVersion?: string;
1627
- header?: string;
1628
- key?: string;
1629
- }
230
+ @UsePipes(new HttpValidationPipe(CreateDto))
1630
231
  ```
1631
232
 
1632
- ### GetServicePathOptions
233
+ **RpcValidationPipe** - валидация DTO для RabbitMQ (автоматически извлекает correlationId и correlationTimestamp)
234
+ ```typescript
235
+ @UsePipes(new RpcValidationPipe(TestRequestDto))
236
+ ```
1633
237
 
1634
- Тип для опций функции getServicePath.
238
+ **JsonRpcValidationPipe** - валидация JSON-RPC 2.0 запросов
239
+ ```typescript
240
+ @Body(JsonRpcValidationPipe) request: JsonRpcRequest
241
+ ```
1635
242
 
1636
- **Расположение:** `src/types/get-service-path-types.ts`
243
+ **FileValidationPipe** - валидация загруженных файлов
244
+ ```typescript
245
+ @UploadedFile(new FileValidationPipe({ maxSize: 5 * 1024 * 1024 }))
246
+ ```
1637
247
 
248
+ **QueryValidationPipe** - валидация query параметров
1638
249
  ```typescript
1639
- type ServicePathType = "locales" | "srcRoot" | "file";
250
+ @Query(new QueryValidationPipe(PaginationDto)) query: PaginationDto
251
+ ```
1640
252
 
1641
- interface GetServicePathOptions {
1642
- serviceName: string;
1643
- dirname: string;
1644
- pathType: ServicePathType;
1645
- relativePath: string;
1646
- }
253
+ **HeaderValidationPipe** - валидация заголовков
254
+ ```typescript
255
+ @Headers(new HeaderValidationPipe(ApiHeadersDto)) headers: ApiHeadersDto
1647
256
  ```
1648
257
 
1649
- ### MulterFile
258
+ ### Guards
1650
259
 
1651
- Тип файла Multer.
260
+ **JwtAuthGuard** - проверка JWT токена
261
+ ```typescript
262
+ @UseGuards(JwtAuthGuard)
263
+ ```
1652
264
 
1653
- **Расположение:** `src/types/file-types.ts`
265
+ **ApiKeyGuard** - проверка API ключа (работает с декоратором `@ApiKey()`)
266
+ ```typescript
267
+ @UseGuards(new ApiKeyGuard(undefined, "x-api-key", new Set(["key1", "key2"])))
268
+ @ApiKey()
269
+ ```
1654
270
 
271
+ **RolesGuard** - проверка ролей пользователя (работает с декоратором `@Roles()`)
1655
272
  ```typescript
1656
- interface MulterFile {
1657
- fieldname: string;
1658
- originalname: string;
1659
- encoding: string;
1660
- mimetype: string;
1661
- size: number;
1662
- buffer: Buffer;
1663
- destination: string;
1664
- filename: string;
1665
- path: string;
1666
- stream: NodeJS.ReadableStream;
1667
- }
273
+ @UseGuards(JwtAuthGuard, RolesGuard)
274
+ @Roles("admin", "moderator")
1668
275
  ```
1669
276
 
1670
- ---
277
+ **PermissionsGuard** - проверка разрешений пользователя (работает с декоратором `@Permissions()`)
278
+ ```typescript
279
+ @UseGuards(JwtAuthGuard, PermissionsGuard)
280
+ @Permissions("users:delete", "users:write")
281
+ ```
1671
282
 
1672
- ## 🚨 Классы ошибок (дополнительные)
283
+ **RateLimitGuard** - ограничение частоты запросов
284
+ ```typescript
285
+ @UseGuards(new RateLimitGuard(undefined, 200, 60000)) // 200 запросов в минуту
286
+ ```
1673
287
 
1674
- ### NestCommonError
288
+ **WebSocketAuthGuard** - проверка аутентификации WebSocket
289
+ ```typescript
290
+ @UseGuards(new WebSocketAuthGuard(async (token, context) => {
291
+ return await this.authService.validateToken(token);
292
+ }))
293
+ ```
1675
294
 
1676
- Базовый класс ошибок пакета nest-common для внутренних ошибок пакета.
295
+ ### Декораторы
1677
296
 
1678
- **Расположение:** `src/errors/nest-common.error.ts`
297
+ - `@Public()` - пометка публичного эндпоинта
298
+ - `@Roles(...)` - указание требуемых ролей
299
+ - `@Permissions(...)` - указание требуемых разрешений
300
+ - `@ApiKey()` - пометка эндпоинта как требующего API ключ
301
+ - `@Serialize(DtoClass)` - указание DTO для сериализации ответа
1679
302
 
1680
- **Наследование:** `Error`
303
+ ### Утилиты
1681
304
 
1682
- **Особенности:**
1683
- - Используется для внутренних ошибок пакета
1684
- - Сохраняет оригинальную ошибку для логирования
1685
- - Корректно работает с `instanceof`
305
+ **validateEnv** - валидация переменных окружения с Joi
306
+ ```typescript
307
+ const validatedEnv = validateEnv(env, ["DATABASE_URL", "REDIS_URL"]);
308
+ ```
1686
309
 
1687
- **Использование:**
310
+ **getUserFromContext** - извлечение пользователя из контекста
311
+ ```typescript
312
+ const user = getUserFromContext(context);
313
+ ```
1688
314
 
315
+ **getIpFromContext** - извлечение IP адреса из контекста
1689
316
  ```typescript
1690
- import { NestCommonError } from "@packages/nest-common";
317
+ const ip = getIpFromContext(context);
318
+ ```
1691
319
 
1692
- throw new NestCommonError("Internal package error", originalError);
320
+ **getRequestIdFromContext** - извлечение Request ID из контекста
321
+ ```typescript
322
+ const requestId = getRequestIdFromContext(context);
1693
323
  ```
1694
324
 
1695
- ---
325
+ **createCorsOptions** - создание опций CORS
326
+ ```typescript
327
+ app.enableCors(createCorsOptions({ origin: ["https://example.com"] }));
328
+ ```
1696
329
 
1697
- ## 📖 Использование
330
+ **createCompressionOptions** - создание опций compression
331
+ ```typescript
332
+ app.use(compression(createCompressionOptions({ level: 9 })));
333
+ ```
1698
334
 
1699
- ### HTTP контроллер (api-service)
335
+ **createVersioningOptions** - создание опций версионирования
336
+ ```typescript
337
+ app.enableVersioning(createVersioningOptions({ type: "uri", defaultVersion: "1" }));
338
+ ```
1700
339
 
340
+ **validateFile** - валидация файла
1701
341
  ```typescript
1702
- import { Controller, Get } from "@nestjs/common";
1703
- import { BaseController } from "@packages/nest-common";
1704
- import { LoggerService } from "@makebelieve21213-packages/logger";
342
+ const result = validateFile(file, { maxSize: 5 * 1024 * 1024 });
343
+ ```
1705
344
 
1706
- @Controller("analytics")
1707
- export default class AnalyticsController extends BaseController {
1708
- constructor(
1709
- private readonly analyticsService: AnalyticsService,
1710
- logger: LoggerService,
1711
- ) {
1712
- super(logger);
1713
- }
345
+ **CircuitBreakerService** - реализация паттерна Circuit Breaker
346
+ ```typescript
347
+ const circuitBreaker = new CircuitBreakerService(logger, {
348
+ failureThreshold: 5,
349
+ successThreshold: 2,
350
+ resetTimeout: 60000,
351
+ });
352
+ ```
1714
353
 
1715
- @Get("global")
1716
- async getGlobal() {
1717
- // БЕЗ try-catch - глобальный фильтр все обработает
1718
- return await this.analyticsService.getGlobalData();
1719
- }
1720
- }
354
+ **getServicePath** - определение пути в сервисе
355
+ ```typescript
356
+ const path = getServicePath({ serviceName: "test-service", dirname: __dirname, pathType: "locales", relativePath: "locales" });
1721
357
  ```
1722
358
 
1723
- ### RPC контроллер (analytics-service)
359
+ ### BaseController
1724
360
 
1725
- ```typescript
1726
- import { Controller } from "@nestjs/common";
1727
- import { MessagePattern, Payload, Ctx } from "@nestjs/microservices";
1728
- import { BaseController } from "@packages/nest-common";
1729
- import { LoggerService } from "@makebelieve21213-packages/logger";
361
+ Базовый контроллер для всех контроллеров проекта. Предоставляет метод `acknowledge(ctx: RmqContext)` для подтверждения RPC сообщений.
1730
362
 
1731
- @Controller()
1732
- export default class AnalyticsController extends BaseController {
363
+ ```typescript
364
+ export default class TestController extends BaseController {
1733
365
  constructor(
1734
- private readonly analyticsService: AnalyticsService,
366
+ private readonly testService: TestService,
1735
367
  logger: LoggerService,
1736
368
  ) {
1737
369
  super(logger);
1738
370
  }
1739
-
1740
- @MessagePattern(ROUTING_KEYS.ANALYTICS_GLOBAL)
1741
- async getGlobalData(
1742
- @Payload() _: GlobalDataIncomeDto,
1743
- @Ctx() ctx: RmqContext,
1744
- ): Promise<GlobalDataOutcomeDto> {
1745
- // БЕЗ try-catch - глобальный фильтр все обработает
1746
- const data = await this.analyticsService.getGlobalData();
1747
- this.acknowledge(ctx);
1748
- return data;
1749
- }
1750
371
  }
1751
372
  ```
1752
373
 
1753
- ### WebSocket Gateway (api-service)
1754
-
1755
- ```typescript
1756
- import { WebSocketGateway, WebSocketServer } from "@nestjs/websockets";
1757
- import { SocketError } from "@packages/nest-common";
1758
- import { SOCKET_EVENTS } from "@packages/types";
1759
-
1760
- @WebSocketGateway()
1761
- export default class SocketGateway extends BaseGateway {
1762
- @WebSocketServer()
1763
- io!: Server;
1764
-
1765
- async publish(userId: string, event: SOCKET_EVENTS, payload: unknown) {
1766
- try {
1767
- await this.io.timeout(5000).to(`user:${userId}`).emitWithAck(event, payload);
1768
- } catch (error) {
1769
- const socketError = SocketError.fromUnknown(error);
1770
-
1771
- // Логирование
1772
- this.logger.error(`Ошибка отправки события: ${socketError.message}`);
1773
-
1774
- // Сохранение в Redis
1775
- await this.redisService.hSet(REDIS_H_KEYS.SOCKET_ERROR, userId, socketError.message);
1776
-
1777
- // Опционально: отправка на фронт
1778
- await this.io.to(`user:${userId}`).emit(SOCKET_EVENTS.ERROR, {
1779
- status: "error",
1780
- message: socketError.message,
1781
- });
1782
-
1783
- throw socketError;
1784
- }
1785
- }
1786
- }
1787
- ```
1788
-
1789
- ---
1790
-
1791
374
  ## ✅ Best Practices
1792
375
 
1793
376
  ### 1. Не используйте catch блоки в контроллерах
@@ -1826,25 +409,7 @@ async getData() {
1826
409
  }
1827
410
  ```
1828
411
 
1829
- ### 3. Отправляйте ошибки на фронт через Socket.io
1830
-
1831
- ```typescript
1832
- // ✅ ХОРОШО - отправка ошибки на фронт
1833
- try {
1834
- await this.processData();
1835
- } catch (error) {
1836
- await this.socketGateway.publish(
1837
- userId,
1838
- SOCKET_EVENTS.ERROR,
1839
- {
1840
- status: "error",
1841
- message: error.message,
1842
- },
1843
- );
1844
- }
1845
- ```
1846
-
1847
- ### 4. Не преобразуйте ошибки вручную в контроллерах
412
+ ### 3. Не преобразуйте ошибки вручную в контроллерах
1848
413
 
1849
414
  ```typescript
1850
415
  // ❌ ПЛОХО
@@ -1865,7 +430,7 @@ async getGlobal() {
1865
430
  }
1866
431
  ```
1867
432
 
1868
- ### 5. Используйте правильный тип ошибки для контекста
433
+ ### 4. Используйте правильный тип ошибки для контекста
1869
434
 
1870
435
  ```typescript
1871
436
  // HTTP контекст → HttpError (автоматически через UnifiedExceptionFilter)
@@ -1873,65 +438,38 @@ async getGlobal() {
1873
438
  // Socket контекст → SocketError (вручную в SocketGateway.publish())
1874
439
  ```
1875
440
 
1876
- ---
1877
-
1878
- ## 🔍 Обработка ошибок WebSocket
1879
-
1880
- ### Типы ошибок WebSocket
1881
-
1882
- 1. **Ошибки отправки событий** - обрабатываются через `SocketError` в `SocketGateway.publish()`
1883
- 2. **Ошибки подключения** - обрабатываются автоматически Socket.io (событие `disconnect`)
1884
- 3. **Внутренние ошибки сервера** - логируются, опционально отправляются на фронт через событие `ERROR`
1885
-
1886
- ### Отправка ошибок на фронт
441
+ ### 5. Используйте валидацию через пайпы
1887
442
 
1888
443
  ```typescript
1889
- // В сервисе при возникновении ошибки
1890
- await this.socketGateway.publish(
1891
- userId,
1892
- SOCKET_EVENTS.ERROR,
1893
- {
1894
- status: "error",
1895
- message: "Произошла ошибка при обработке данных",
1896
- },
1897
- );
444
+ // ХОРОШО
445
+ @Post("create")
446
+ @UsePipes(new HttpValidationPipe(CreateDto))
447
+ async create(@Body() dto: CreateDto) {
448
+ return await this.service.create(dto);
449
+ }
1898
450
  ```
1899
451
 
1900
- ### Обработка на фронтенде
452
+ ### 6. Используйте guards с соответствующими декораторами
1901
453
 
1902
454
  ```typescript
1903
- socket.on(SOCKET_EVENTS.ERROR, (payload, ack) => {
1904
- // Останавливаем загрузки
1905
- setLoadingGlobal(false);
1906
-
1907
- // Устанавливаем критическую ошибку
1908
- useGlobalStore.setState({ criticalError: payload.message });
1909
-
1910
- // Подтверждаем получение
1911
- ack({ status: "received", ts: Date.now() });
1912
- });
455
+ // ХОРОШО
456
+ @UseGuards(JwtAuthGuard, RolesGuard)
457
+ @Roles("admin", "moderator")
458
+ async getAdminData() {
459
+ return await this.testService.getData();
460
+ }
1913
461
  ```
1914
462
 
1915
- ### Внутренние ошибки Socket.io
1916
-
1917
- Внутренние ошибки (подключение/отключение) обрабатываются автоматически:
1918
-
1919
- - **Ошибка подключения** → Socket.io разрывает соединение, фронт получает событие `disconnect`
1920
- - **Ошибка отключения** → Socket.io очищает соединение, фронт получает событие `disconnect`
1921
- - **Таймаут соединения** → Socket.io автоматически переподключается, фронт получает событие `reconnect`
1922
-
1923
- ---
1924
-
1925
463
  ## 🧪 Тестирование
1926
464
 
1927
465
  Пакет имеет высокое покрытие тестами (>95% для веток, 100% для statements и функций).
1928
466
 
1929
467
  ```bash
1930
468
  # Запуск тестов
1931
- pnpm test
469
+ npm test
1932
470
 
1933
471
  # Запуск тестов с покрытием
1934
- pnpm test:coverage
472
+ npm run test:coverage
1935
473
  ```
1936
474
 
1937
475
  **Покрытие тестами:**
@@ -1940,52 +478,17 @@ pnpm test:coverage
1940
478
  - Functions: 100%
1941
479
  - Lines: 100%
1942
480
 
1943
- **Тестовые сценарии:**
1944
- - Все классы ошибок (HttpError, RpcError, SocketError, NestCommonError)
1945
- - Глобальные фильтры (UnifiedExceptionFilter, HttpExceptionFilter, RpcExceptionFilter, WebSocketExceptionHandler)
1946
- - Перехватчики (UnifiedInterceptor, HttpLoggingInterceptor, RpcLoggingInterceptor, WebSocketLoggingInterceptor, ResponseInterceptor, SerializeInterceptor, CompressionInterceptor, RequestIdResponseInterceptor)
1947
- - Пайпы валидации (HttpValidationPipe, RpcValidationPipe, FileValidationPipe, QueryValidationPipe, HeaderValidationPipe)
1948
- - Guards (JwtAuthGuard, ApiKeyGuard, RolesGuard, PermissionsGuard, RateLimitGuard, WebSocketAuthGuard)
1949
- - Декораторы (Public, Roles, Permissions, ApiKey, Serialize)
1950
- - Базовые классы (BaseController)
1951
- - Утилиты (validateEnv, getUserFromContext, getIpFromContext, getUserAgentFromContext, getRequestIdFromContext, createCorsOptions, createCompressionOptions, createVersioningOptions, validateFile, getFileExtension, formatFileSize, CircuitBreakerService, getServicePath)
1952
- - Преобразование ошибок между контекстами
1953
- - Обработка граничных случаев и различных типов ошибок
1954
- - Логирование HTTP, RPC и WebSocket запросов с метриками времени выполнения
1955
- - Валидация файлов, query параметров и заголовков
1956
- - Авторизация и аутентификация через guards
1957
- - Rate limiting и circuit breaker паттерны
1958
-
1959
- ## 📚 Дополнительные ресурсы
1960
-
1961
- - [NestJS Exception Filters](https://docs.nestjs.com/exception-filters)
1962
- - [NestJS Microservices](https://docs.nestjs.com/microservices/basics)
1963
- - [Socket.io Error Handling](https://socket.io/docs/v4/error-handling/)
1964
-
1965
- ## 📦 Установка
1966
-
1967
- ```bash
1968
- pnpm add @makebelieve21213-packages/nest-common
1969
- ```
1970
-
1971
- Или добавьте в `package.json` вашего микросервиса:
1972
- ```json
1973
- {
1974
- "dependencies": {
1975
- "@makebelieve21213-packages/nest-common": "workspace:*"
1976
- }
1977
- }
1978
- ```
1979
-
1980
481
  ## 🏗️ Разработка
1981
482
 
1982
483
  ### Технический стек
484
+
1983
485
  - **TypeScript 5.7+** - строгая типизация
1984
486
  - **ESM модули** - современный стандарт модулей JavaScript
1985
487
  - **NestJS 11.x** - фреймворк для микросервисов
1986
488
  - **Jest** - тестирование
1987
489
 
1988
490
  ### Процесс сборки
491
+
1989
492
  Пакет использует многоэтапную сборку для корректной работы ESM:
1990
493
  1. **TypeScript компиляция** (`tsc --build`) - компиляция TypeScript в JavaScript
1991
494
  2. **Замена алиасов** (`tsc-alias`) - замена путей `src/*` на относительные
@@ -1993,24 +496,24 @@ pnpm add @makebelieve21213-packages/nest-common
1993
496
 
1994
497
  ```bash
1995
498
  # Установка зависимостей
1996
- pnpm install
499
+ npm install
1997
500
 
1998
501
  # Сборка
1999
- pnpm build
502
+ npm run build
2000
503
 
2001
504
  # Запуск тестов
2002
- pnpm test
505
+ npm test
2003
506
 
2004
507
  # Запуск тестов с покрытием
2005
- pnpm test:coverage
508
+ npm run test:coverage
2006
509
 
2007
510
  # Линтер
2008
- pnpm lint
2009
- pnpm lint:fix
511
+ npm run lint
512
+ npm run lint:fix
2010
513
 
2011
514
  # Форматирование
2012
- pnpm format
2013
- pnpm format:fix
515
+ npm run format
516
+ npm run format:fix
2014
517
  ```
2015
518
 
2016
519
  ### Git Hooks (Husky)
@@ -2041,7 +544,7 @@ docker run -d \
2041
544
  ## Совместимость
2042
545
 
2043
546
  - **Node.js**: >=22.11.0
2044
- - **pnpm**: >=10.18.0
547
+ - **npm**: >=10.0.0
2045
548
  - **@nestjs/common**: ^11.1.6
2046
549
  - **@nestjs/microservices**: ^11.1.3
2047
550
  - **rxjs**: ^7.8.2