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