@vorplex/api 0.0.74 → 0.0.76

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.
@@ -1,15 +1,17 @@
1
1
  import { Awaitable, Injector } from '@vorplex/core';
2
+ import { IncomingMessage, ServerResponse } from 'http';
2
3
  import { Server } from '../server.model';
3
4
  import { Handler } from './handler.interface';
4
- import { IncomingMessage, ServerResponse } from 'http';
5
5
  export type ControllerGuard = (params: {
6
6
  injector: Injector;
7
7
  server: Server;
8
8
  request: IncomingMessage;
9
9
  response: ServerResponse;
10
+ routeParameters: Record<string, string>;
10
11
  }) => Awaitable<boolean>;
11
12
  export interface Controller {
12
13
  route: string;
13
14
  guards?: ControllerGuard[];
14
- handlers: Handler[];
15
+ controllers?: Controller[];
16
+ handlers?: Handler[];
15
17
  }
@@ -81,79 +81,98 @@ export class Server {
81
81
  const task = new Task(`Request: ${request.method} ${request.url}`);
82
82
  try {
83
83
  task.log('Routing request');
84
- for (const controller of this.controllers) {
85
- for (const handler of controller.handlers) {
86
- if (handler.method === request.method) {
87
- const match = $Router.match(`${controller.route}${handler.route}`, request.url);
88
- if (match) {
89
- const query = $Router.getQueryParameters(request.url);
90
- task.log(`Forwarding request to controller (${controller.route}) handler (${handler.route})`);
91
- for (const guard of controller.guards ?? []) {
92
- const authorized = await guard({
93
- injector: this.injector,
94
- server: this,
95
- request,
96
- response
97
- });
98
- if (!authorized)
99
- throw new HttpError(HttpResponseCodes.Unauthorized);
100
- }
101
- task.log(`${request.method} ${request.url}`, {
102
- attachments: {
103
- parameters: { type: 'json', value: JSON.stringify(query, null, 4) },
104
- headers: { type: 'json', value: JSON.stringify(request.headers, null, 4) }
105
- }
106
- });
107
- for (const parameter of handler.parameters ?? []) {
108
- if (!parameter.endsWith('?') && $String.isNullOrEmpty(query[parameter]))
109
- throw new HttpError(HttpResponseCodes.BadRequest, `Missing required query parameter (${parameter})`);
110
- }
111
- const result = await handler.callback({
112
- injector: this.injector,
113
- server: this,
114
- task,
115
- parameters: {
116
- route: match,
117
- query
118
- },
119
- request,
84
+ function findHandler(controllers, parentRoute = '', parentGuards = []) {
85
+ for (const controller of controllers) {
86
+ const route = `${parentRoute}${controller.route}`;
87
+ const guards = [...parentGuards, ...(controller.guards ?? [])];
88
+ for (const handler of controller.handlers ?? []) {
89
+ if (handler.method === request.method) {
90
+ const routeParameters = $Router.match(`${route}${handler.route}`, request.url);
91
+ if (routeParameters)
92
+ return {
93
+ controller,
94
+ routeParameters,
95
+ guards,
96
+ handler
97
+ };
98
+ }
99
+ }
100
+ if (controller.controllers) {
101
+ const match = findHandler(controller.controllers, route, guards);
102
+ if (match)
103
+ return match;
104
+ }
105
+ }
106
+ }
107
+ const match = findHandler(this.controllers);
108
+ if (match) {
109
+ const { controller, routeParameters, guards, handler } = match;
110
+ const query = $Router.getQueryParameters(request.url);
111
+ task.log(`Forwarding request to controller (${controller.route}) handler (${handler.route})`);
112
+ for (const guard of guards) {
113
+ const authorized = await guard({
114
+ injector: this.injector,
115
+ server: this,
116
+ request,
117
+ response,
118
+ routeParameters
119
+ });
120
+ if (!authorized)
121
+ throw new HttpError(HttpResponseCodes.Unauthorized);
122
+ }
123
+ task.log(`${request.method} ${request.url}`, {
124
+ attachments: {
125
+ parameters: { type: 'json', value: JSON.stringify(query, null, 4) },
126
+ headers: { type: 'json', value: JSON.stringify(request.headers, null, 4) }
127
+ }
128
+ });
129
+ for (const parameter of handler.parameters ?? []) {
130
+ if (!parameter.endsWith('?') && $String.isNullOrEmpty(query[parameter]))
131
+ throw new HttpError(HttpResponseCodes.BadRequest, `Missing required query parameter (${parameter})`);
132
+ }
133
+ const result = await handler.callback({
134
+ injector: this.injector,
135
+ server: this,
136
+ task,
137
+ parameters: {
138
+ route: routeParameters,
139
+ query
140
+ },
141
+ request,
142
+ response
143
+ });
144
+ if (result) {
145
+ if (response.writable) {
146
+ if (result.code)
147
+ response.statusCode = result.code;
148
+ if (result.status)
149
+ response.statusMessage = result.status;
150
+ if (result.headers)
151
+ response.setHeaders(new Map(Object.entries(result.headers)));
152
+ switch (result.type) {
153
+ case 'json':
120
154
  response
121
- });
122
- if (result) {
123
- if (response.writable) {
124
- if (result.code)
125
- response.statusCode = result.code;
126
- if (result.status)
127
- response.statusMessage = result.status;
128
- if (result.headers)
129
- response.setHeaders(new Map(Object.entries(result.headers)));
130
- switch (result.type) {
131
- case 'json':
132
- response
133
- .setHeader('Content-Type', MimeType.json)
134
- .write(result.value === undefined ? undefined : JSON.stringify(result.value));
135
- break;
136
- case 'file':
137
- await new Promise((resolve, reject) => {
138
- response.setHeader('Content-Type', result.mimeType);
139
- response.once('finish', () => resolve());
140
- response.once('error', (error) => reject(error));
141
- fs.createReadStream(result.file).pipe(response, { end: true });
142
- });
143
- break;
144
- case 'redirect':
145
- response.statusCode = 302;
146
- response.setHeader('Location', result.url);
147
- break;
148
- }
149
- }
150
- }
151
- if (!response.writableEnded)
152
- response.end();
153
- return;
155
+ .setHeader('Content-Type', MimeType.json)
156
+ .write(result.value === undefined ? undefined : JSON.stringify(result.value));
157
+ break;
158
+ case 'file':
159
+ await new Promise((resolve, reject) => {
160
+ response.setHeader('Content-Type', result.mimeType);
161
+ response.once('finish', () => resolve());
162
+ response.once('error', (error) => reject(error));
163
+ fs.createReadStream(result.file).pipe(response, { end: true });
164
+ });
165
+ break;
166
+ case 'redirect':
167
+ response.statusCode = 302;
168
+ response.setHeader('Location', result.url);
169
+ break;
154
170
  }
155
171
  }
156
172
  }
173
+ if (!response.writableEnded)
174
+ response.end();
175
+ return;
157
176
  }
158
177
  task.fail(`No route matches URL`);
159
178
  if (response.writable) {