@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
|
-
|
|
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
|
-
|
|
85
|
-
for (const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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) {
|