proteum 2.2.0 → 2.2.2-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -132,7 +132,13 @@ export type Config<
132
132
  // Set it as a function, so when we instanciate the services, we can callthis.router to pass the router instance in roiuter services
133
133
  type TRouterServicesList = { [serviceName: string]: AnyRouterService };
134
134
 
135
- export type Hooks = {};
135
+ export type Hooks = {
136
+ request: { args: [request: ServerRequest<TServerRouter>] };
137
+ 'request.finished': { args: [request: ServerRequest<TServerRouter>] };
138
+ resolve: { args: [request: ServerRequest<TServerRouter>] };
139
+ resolved: { args: [route: TMatchedRoute, request: ServerRequest<TServerRouter>, response: ServerResponse<TServerRouter>] };
140
+ render: { args: [page: Page<TServerRouter>] };
141
+ };
136
142
 
137
143
  export type TControllerDefinition = {
138
144
  path?: string;
@@ -607,6 +613,32 @@ export default class ServerRouter<
607
613
  /*----------------------------------
608
614
  - RESOLUTION
609
615
  ----------------------------------*/
616
+ private async finalizeRequest(
617
+ request: ServerRequest<this>,
618
+ output: {
619
+ statusCode: number;
620
+ user?: string;
621
+ errorMessage?: string;
622
+ },
623
+ ) {
624
+ this.app.container.Trace.finishRequest(request.id, output);
625
+
626
+ try {
627
+ await this.runHook('request.finished', request);
628
+ } catch (error) {
629
+ const typedError =
630
+ error instanceof Error ? error : new Error(typeof error === 'string' ? error : 'Unknown request.finished hook error');
631
+
632
+ try {
633
+ await this.app.runHook('error', typedError, request);
634
+ } catch (hookError) {
635
+ console.error('request.finished hook error', typedError, 'Error hook failure', hookError);
636
+ }
637
+ } finally {
638
+ this.app.container.Trace.releaseRequest(request.id);
639
+ }
640
+ }
641
+
610
642
  public async middleware(req: express.Request, res: express.Response) {
611
643
  // Create request
612
644
  let requestId = uuid();
@@ -629,7 +661,7 @@ export default class ServerRouter<
629
661
  this,
630
662
  );
631
663
 
632
- this.app.container.Trace.startRequest({
664
+ request.profiling = this.app.container.Trace.startRequest({
633
665
  id: request.id,
634
666
  method: request.method,
635
667
  path: request.path,
@@ -640,7 +672,7 @@ export default class ServerRouter<
640
672
  profilerOrigin: request.headers[profilerOriginHeader] || undefined,
641
673
  profilerParentRequestId: request.headers[profilerParentRequestIdHeader] || undefined,
642
674
  });
643
- if (this.app.container.Trace.isEnabled()) res.setHeader(profilerTraceRequestIdHeader, request.id);
675
+ if (this.app.container.Trace.isDevTraceEnabled()) res.setHeader(profilerTraceRequestIdHeader, request.id);
644
676
  if (cachedPage) {
645
677
  this.app.container.Trace.record(
646
678
  request.id,
@@ -659,7 +691,7 @@ export default class ServerRouter<
659
691
  // Bulk API Requests
660
692
  if (request.path === '/api' && typeof request.data.fetchers === 'object') {
661
693
  await this.resolveApiBatch(request.data.fetchers, request);
662
- this.app.container.Trace.finishRequest(request.id, {
694
+ await this.finalizeRequest(request, {
663
695
  statusCode: request.res.statusCode || 200,
664
696
  user: request.user?.email,
665
697
  });
@@ -696,7 +728,7 @@ export default class ServerRouter<
696
728
  },
697
729
  'summary',
698
730
  );
699
- this.app.container.Trace.finishRequest(request.id, {
731
+ await this.finalizeRequest(request, {
700
732
  statusCode: response.statusCode,
701
733
  user: request.user?.email,
702
734
  });
@@ -715,7 +747,7 @@ export default class ServerRouter<
715
747
  'summary',
716
748
  );
717
749
  res.send(cachedPage.rendered);
718
- this.app.container.Trace.finishRequest(request.id, {
750
+ await this.finalizeRequest(request, {
719
751
  statusCode: response.statusCode,
720
752
  user: request.user?.email,
721
753
  });
@@ -739,19 +771,19 @@ export default class ServerRouter<
739
771
  'summary',
740
772
  );
741
773
  res.send(response.data);
742
- this.app.container.Trace.finishRequest(request.id, {
774
+ await this.finalizeRequest(request, {
743
775
  statusCode: response.statusCode,
744
776
  user: request.user?.email,
745
777
  });
746
778
  } else if (response.data !== 'true') {
747
- this.app.container.Trace.finishRequest(request.id, {
779
+ await this.finalizeRequest(request, {
748
780
  statusCode: res.statusCode || response.statusCode,
749
781
  user: request.user?.email,
750
782
  errorMessage: "Can't return data from the controller since response has already been sent via express.",
751
783
  });
752
784
  throw new Error("Can't return data from the controller since response has already been sent via express.");
753
785
  } else {
754
- this.app.container.Trace.finishRequest(request.id, {
786
+ await this.finalizeRequest(request, {
755
787
  statusCode: res.statusCode || response.statusCode,
756
788
  user: request.user?.email,
757
789
  });
@@ -10,7 +10,7 @@ import Bowser from 'bowser';
10
10
 
11
11
  // Core
12
12
  import BaseRequest from '@common/router/request';
13
- import type { TTraceCallOrigin } from '@common/dev/requestTrace';
13
+ import type { TRequestProfiling, TTraceCallOrigin } from '@common/dev/requestTrace';
14
14
 
15
15
  // Specific
16
16
  import type { HttpMethod, HttpHeaders } from '..';
@@ -42,6 +42,24 @@ type TRequestTraceCallContext = {
42
42
  label: string;
43
43
  origin: TTraceCallOrigin;
44
44
  };
45
+ type THeaderValue = string | string[] | undefined;
46
+
47
+ const readHeader = (headers: Record<string, THeaderValue>, name: string): string | null => {
48
+ const exact = headers[name];
49
+ const alternate = exact === undefined ? headers[name.toLowerCase()] : exact;
50
+ const value = alternate === undefined ? headers[name.toUpperCase()] : alternate;
51
+
52
+ if (Array.isArray(value)) {
53
+ const firstValue = value.find((entry) => typeof entry === 'string' && entry.trim());
54
+ return typeof firstValue === 'string' ? firstValue.trim() : null;
55
+ }
56
+
57
+ return typeof value === 'string' && value.trim() ? value.trim() : null;
58
+ };
59
+
60
+ const resolveRequestIp = (req: express.Request): string | undefined => {
61
+ return readHeader(req.headers as Record<string, THeaderValue>, 'cf-connecting-ip') || req.ip;
62
+ };
45
63
 
46
64
  /*----------------------------------
47
65
  - CONTEXTE
@@ -74,6 +92,7 @@ export default class ServerRequest<TRouter extends TAnyRouter = TAnyRouter> exte
74
92
  // Services
75
93
  public api: ApiClient;
76
94
  public traceCall?: TRequestTraceCallContext;
95
+ public profiling!: TRequestProfiling;
77
96
 
78
97
  /*----------------------------------
79
98
  - INITIALISATION
@@ -107,7 +126,7 @@ export default class ServerRequest<TRouter extends TAnyRouter = TAnyRouter> exte
107
126
  this.domain = res.req.hostname;
108
127
  this.cookies = res.req.cookies;
109
128
 
110
- this.ip = res.req.ip;
129
+ this.ip = resolveRequestIp(res.req);
111
130
 
112
131
  this.data = data || {};
113
132
  }