@biorate/nestjs-tools 1.126.0 → 1.128.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.
Files changed (54) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/interfaces.js +15 -0
  3. package/dist/interfaces.js.map +1 -1
  4. package/dist/tsconfig.build.tsbuildinfo +1 -1
  5. package/interfaces.ts +1 -9
  6. package/package.json +2 -2
  7. package/src/app/application/index.ts +2 -0
  8. package/src/app/application/ports/client.driven.port.ts +5 -0
  9. package/src/app/application/ports/crud.driven.port.ts +60 -0
  10. package/src/app/application/ports/index.ts +3 -0
  11. package/src/app/application/ports/metrics.driven.port.ts +3 -0
  12. package/src/app/application/service/client/get-locale.use-case.ts +13 -0
  13. package/src/app/application/service/client/index.ts +2 -0
  14. package/src/app/application/service/client/set-locale.use-case.ts +13 -0
  15. package/src/app/application/service/index.ts +2 -0
  16. package/src/app/application/service/metrics/get-metrics.use-case.ts +13 -0
  17. package/src/app/application/service/metrics/index.ts +1 -0
  18. package/src/app/domain/.keep +0 -0
  19. package/src/app/index.ts +2 -0
  20. package/src/app/infrastructure/adapters/client.repository.adapter.ts +34 -0
  21. package/src/app/infrastructure/adapters/index.ts +2 -0
  22. package/src/app/infrastructure/adapters/metrics.repository.adapter.ts +13 -0
  23. package/src/app/infrastructure/controllers/client.controller.ts +28 -0
  24. package/src/app/infrastructure/controllers/dto/index.ts +1 -0
  25. package/src/app/infrastructure/controllers/dto/locales.ts +15 -0
  26. package/src/app/infrastructure/controllers/index.ts +3 -0
  27. package/src/app/infrastructure/controllers/metrics.controller.ts +16 -0
  28. package/src/app/infrastructure/controllers/probe.controller.ts +18 -0
  29. package/src/app/infrastructure/index.ts +3 -0
  30. package/src/decorators/auth.ts +9 -0
  31. package/src/decorators/index.ts +2 -0
  32. package/src/decorators/roles.ts +12 -0
  33. package/src/enums/index.ts +1 -0
  34. package/src/enums/user.ts +1 -0
  35. package/src/errors.ts +13 -0
  36. package/src/filters/all-exception-filter.ts +91 -0
  37. package/src/filters/index.ts +1 -0
  38. package/src/http/controllers/index.ts +3 -0
  39. package/src/http/controllers/locales.ts +79 -0
  40. package/src/http/controllers/metrics.ts +21 -0
  41. package/src/http/controllers/probe.ts +26 -0
  42. package/src/http/dto/index.ts +1 -0
  43. package/src/http/dto/locales.ts +15 -0
  44. package/src/http/index.ts +2 -0
  45. package/src/index.ts +9 -0
  46. package/src/interceptors/index.ts +1 -0
  47. package/src/interceptors/routes-interceptor.ts +39 -0
  48. package/src/middlewares/index.ts +3 -0
  49. package/src/middlewares/proxy-middleware.ts +69 -0
  50. package/src/middlewares/request-count-middleware.ts +41 -0
  51. package/src/middlewares/response-time-middleware.ts +68 -0
  52. package/src/providers/auth.ts +36 -0
  53. package/src/providers/index.ts +2 -0
  54. package/src/providers/roles.ts +34 -0
@@ -0,0 +1,39 @@
1
+ import { join, normalize } from 'path/posix';
2
+ import { Reflector } from '@nestjs/core';
3
+ import { PATH_METADATA } from '@nestjs/common/constants';
4
+ import { Request } from 'express';
5
+ import {
6
+ Injectable,
7
+ ExecutionContext,
8
+ CallHandler,
9
+ NestInterceptor,
10
+ } from '@nestjs/common';
11
+
12
+ @Injectable()
13
+ export class RoutesInterceptor implements NestInterceptor {
14
+ public static readonly map = new WeakMap<Request, { path: string }>();
15
+
16
+ private readonly reflector = new Reflector();
17
+
18
+ public intercept(context: ExecutionContext, next: CallHandler) {
19
+ const namespaces = this.reflector.get<string | string[]>(
20
+ PATH_METADATA,
21
+ context.getClass(),
22
+ );
23
+ const routes = this.reflector.get<string | string[]>(
24
+ PATH_METADATA,
25
+ context.getHandler(),
26
+ );
27
+ RoutesInterceptor.map.set(context.switchToHttp().getRequest(), {
28
+ path: normalize(
29
+ join(
30
+ '/',
31
+ Array.isArray(namespaces) ? namespaces[0] : namespaces,
32
+ Array.isArray(routes) ? routes[0] : routes,
33
+ '/',
34
+ ).slice(0, -1),
35
+ ),
36
+ });
37
+ return next.handle();
38
+ }
39
+ }
@@ -0,0 +1,3 @@
1
+ export * from './proxy-middleware';
2
+ export * from './request-count-middleware';
3
+ export * from './response-time-middleware';
@@ -0,0 +1,69 @@
1
+ import http from 'http';
2
+ import type { ServerOptions } from 'http-proxy';
3
+ import { pick, omit } from 'lodash';
4
+ import { Request, Response } from 'express';
5
+ import { createProxyMiddleware } from 'http-proxy-middleware';
6
+ import { Options, RequestHandler } from 'http-proxy-middleware/dist/types';
7
+ import { Injectable } from '@nestjs/common';
8
+ import { counter, histogram, Counter, Histogram } from '@biorate/prometheus';
9
+ import { time } from '@biorate/tools';
10
+
11
+ @Injectable()
12
+ export class ProxyPrometheusMiddleware {
13
+ @counter({
14
+ name: 'http_proxy_server_requests_seconds_count',
15
+ help: 'Http proxy server requests count',
16
+ labelNames: ['method', 'uri', 'status'],
17
+ })
18
+ private counter: Counter;
19
+
20
+ @histogram({
21
+ name: 'http_proxy_server_requests_seconds',
22
+ help: 'Http proxy server requests seconds bucket',
23
+ labelNames: ['method', 'uri', 'status'],
24
+ buckets: [0.005, 0.01, 0.02, 0.05, 0.1, 0.3, 0.5, 1, 2, 3, 5, 10],
25
+ })
26
+ private histogram: Histogram;
27
+
28
+ private handler: RequestHandler;
29
+
30
+ private time = new WeakMap<Request, { time: () => number }>();
31
+
32
+ public static create(options?: Options) {
33
+ return new this(options).handler;
34
+ }
35
+
36
+ private constructor(options: Options = {}) {
37
+ const { onProxyReq, onProxyRes } = pick(options, 'onProxyReq', 'onProxyRes');
38
+ this.handler = createProxyMiddleware({
39
+ ...omit(options, 'onProxyReq', 'onProxyRes'),
40
+ onProxyReq: (
41
+ proxyReq: http.ClientRequest,
42
+ req: Request,
43
+ res: Response,
44
+ options: ServerOptions,
45
+ ) => {
46
+ this.time.set(req, { time: time.diff() });
47
+ return onProxyReq?.(proxyReq, req, res, options);
48
+ },
49
+ onProxyRes: (proxyRes: http.IncomingMessage, req: Request, res: Response) => {
50
+ const diff = this.time.get(req);
51
+ this.counter
52
+ .labels({
53
+ method: req.method,
54
+ uri: req.baseUrl,
55
+ status: res.statusCode,
56
+ })
57
+ .inc();
58
+ this.histogram
59
+ .labels({
60
+ method: req.method,
61
+ uri: req.baseUrl,
62
+ status: res.statusCode,
63
+ })
64
+ .observe(time.msTo(diff!.time(), 's'));
65
+ return onProxyRes?.(proxyRes, req, res);
66
+ },
67
+ });
68
+ }
69
+ }
@@ -0,0 +1,41 @@
1
+ // @ts-ignore
2
+ import * as onHeaders from 'on-headers';
3
+ import { Injectable, NestMiddleware } from '@nestjs/common';
4
+ import { counter, Counter } from '@biorate/prometheus';
5
+ import { inject, Types } from '@biorate/inversion';
6
+ import { IConfig } from '@biorate/config';
7
+ import { Request, Response, NextFunction } from 'express';
8
+ import { RoutesInterceptor } from '../interceptors';
9
+
10
+ @Injectable()
11
+ export class RequestCountMiddleware implements NestMiddleware {
12
+ @inject(Types.Config) private config: IConfig;
13
+
14
+ @counter({
15
+ name: 'http_server_requests_seconds_count',
16
+ help: 'Http server requests count',
17
+ labelNames: ['method', 'uri', 'status'],
18
+ })
19
+ private counter: Counter;
20
+
21
+ public use(req: Request, res: Response, next: NextFunction) {
22
+ onHeaders(res, () => {
23
+ const route = RoutesInterceptor.map.get(req);
24
+ this.counter
25
+ .labels({
26
+ method: req.method,
27
+ uri:
28
+ route?.path ??
29
+ (this.config.get<boolean>(
30
+ 'app.middleware.RequestCountMiddleware.log-base-url',
31
+ false,
32
+ )
33
+ ? req.baseUrl || req.originalUrl
34
+ : '/'),
35
+ status: res.statusCode,
36
+ })
37
+ .inc();
38
+ });
39
+ next();
40
+ }
41
+ }
@@ -0,0 +1,68 @@
1
+ // @ts-ignore
2
+ import * as onHeaders from 'on-headers';
3
+ import { Injectable, NestMiddleware } from '@nestjs/common';
4
+ import { inject, Types } from '@biorate/inversion';
5
+ import { IConfig } from '@biorate/config';
6
+ import { histogram, Histogram } from '@biorate/prometheus';
7
+ import { Request, Response, NextFunction } from 'express';
8
+ import { RoutesInterceptor } from '../interceptors';
9
+ import { time } from '@biorate/tools';
10
+
11
+ @Injectable()
12
+ export class ResponseTimeMiddleware implements NestMiddleware {
13
+ @inject(Types.Config) private config: IConfig;
14
+
15
+ private readonly suffix: boolean;
16
+
17
+ private readonly header: string;
18
+
19
+ private readonly digits: number;
20
+
21
+ @histogram({
22
+ name: 'http_server_requests_seconds',
23
+ help: 'Http server requests seconds bucket',
24
+ labelNames: ['method', 'uri', 'status'],
25
+ buckets: [0.005, 0.01, 0.02, 0.05, 0.1, 0.3, 0.5, 1, 2, 3, 5, 10],
26
+ })
27
+ private histogram: Histogram;
28
+
29
+ public constructor() {
30
+ this.suffix = this.config.get<boolean>(
31
+ 'app.middleware.ResponseTimeMiddleware.suffix',
32
+ true,
33
+ );
34
+ this.header = this.config.get<string>(
35
+ 'app.middleware.ResponseTimeMiddleware.header',
36
+ 'x-response-time',
37
+ );
38
+ this.digits = this.config.get<number>(
39
+ 'app.middleware.ResponseTimeMiddleware.digits',
40
+ 3,
41
+ );
42
+ }
43
+
44
+ public use(req: Request, res: Response, next: NextFunction) {
45
+ const diff = time.diff();
46
+ const msTo = time.msTo;
47
+ onHeaders(res, () => {
48
+ const time = diff();
49
+ const route = RoutesInterceptor.map.get(req);
50
+ this.histogram
51
+ .labels({
52
+ method: req.method,
53
+ uri:
54
+ route?.path ??
55
+ (this.config.get<boolean>(
56
+ 'app.middleware.ResponseTimeMiddleware.log-base-url',
57
+ false,
58
+ )
59
+ ? req.baseUrl || req.originalUrl
60
+ : '/'),
61
+ status: res.statusCode,
62
+ })
63
+ .observe(msTo(time, 's'));
64
+ res.setHeader(this.header, time.toFixed(this.digits) + (this.suffix ? 'ms' : ''));
65
+ });
66
+ next();
67
+ }
68
+ }
@@ -0,0 +1,36 @@
1
+ import { inject, Types } from '@biorate/inversion';
2
+ import { IConfig } from '@biorate/config';
3
+ import {
4
+ Injectable,
5
+ CanActivate,
6
+ ExecutionContext,
7
+ UnauthorizedException,
8
+ } from '@nestjs/common';
9
+
10
+ @Injectable()
11
+ export class AuthGuardProvider implements CanActivate {
12
+ @inject(Types.Config) private config: IConfig;
13
+
14
+ public canActivate(context: ExecutionContext): boolean {
15
+ const request = context.switchToHttp().getRequest();
16
+ const config = this.config.get<{ [key: string]: string } | null>(
17
+ 'app.auth.basic',
18
+ null,
19
+ );
20
+ if (!config) return true;
21
+ let username: string, password: string;
22
+ try {
23
+ [username, password] = Buffer.from(
24
+ request.headers.authorization.replace('Basic ', ''),
25
+ 'base64',
26
+ )
27
+ .toString('utf-8')
28
+ .split(':');
29
+ } catch {
30
+ throw new UnauthorizedException();
31
+ }
32
+ if (!username || !password) throw new UnauthorizedException();
33
+ if (config[username] === password) return true;
34
+ throw new UnauthorizedException();
35
+ }
36
+ }
@@ -0,0 +1,2 @@
1
+ export * from './roles';
2
+ export * from './auth';
@@ -0,0 +1,34 @@
1
+ import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
2
+ import { Reflector } from '@nestjs/core';
3
+ import { ROLES_KEY } from '../decorators';
4
+ import { UserRoles } from '../enums';
5
+
6
+ @Injectable()
7
+ export class RolesGuardProvider implements CanActivate {
8
+ constructor(private reflector: Reflector) {}
9
+ /**
10
+ * @description - This method for override
11
+ * @example
12
+ * ```ts
13
+ * const request = context.switchToHttp().getRequest();
14
+ * const { login, sessionId } = request.cookies;
15
+ * if (!login || !sessionId) throw new AuthorizationNotAuthorizedError();
16
+ * // get user from db, or somewhere else
17
+ * if (!user) throw new AuthorizationNotAuthorizedError();
18
+ * return user.role;
19
+ * ```
20
+ */
21
+ protected async getUser(context: ExecutionContext) {
22
+ return { role: UserRoles.Basic | UserRoles.Admin };
23
+ }
24
+
25
+ public async canActivate(context: ExecutionContext) {
26
+ const roles = this.reflector.getAllAndOverride<number>(ROLES_KEY, [
27
+ context.getHandler(),
28
+ context.getClass(),
29
+ ]);
30
+ if (roles === undefined) return true;
31
+ const user = await this.getUser(context);
32
+ return (user.role & roles) === roles;
33
+ }
34
+ }