@twin.org/api-server-fastify 0.0.2-next.9 → 0.0.3-next.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.
- package/dist/es/fastifySocketIo.js +18 -0
- package/dist/es/fastifySocketIo.js.map +1 -0
- package/dist/{esm/index.mjs → es/fastifyWebServer.js} +111 -88
- package/dist/es/fastifyWebServer.js.map +1 -0
- package/dist/es/index.js +6 -0
- package/dist/es/index.js.map +1 -0
- package/dist/es/models/IFastifyWebServerConfig.js +2 -0
- package/dist/es/models/IFastifyWebServerConfig.js.map +1 -0
- package/dist/es/models/IFastifyWebServerConstructorOptions.js +2 -0
- package/dist/es/models/IFastifyWebServerConstructorOptions.js.map +1 -0
- package/dist/types/fastifyWebServer.d.ts +2 -2
- package/dist/types/index.d.ts +3 -3
- package/dist/types/models/IFastifyWebServerConstructorOptions.d.ts +1 -1
- package/docs/changelog.md +98 -0
- package/docs/reference/classes/FastifyWebServer.md +1 -1
- package/locales/.validate-ignore +2 -0
- package/locales/en.json +18 -12
- package/package.json +24 -13
- package/dist/cjs/index.cjs +0 -562
package/dist/cjs/index.cjs
DELETED
|
@@ -1,562 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var FastifyCompress = require('@fastify/compress');
|
|
4
|
-
var FastifyCors = require('@fastify/cors');
|
|
5
|
-
var apiModels = require('@twin.org/api-models');
|
|
6
|
-
var apiProcessors = require('@twin.org/api-processors');
|
|
7
|
-
var core = require('@twin.org/core');
|
|
8
|
-
var web = require('@twin.org/web');
|
|
9
|
-
var Fastify = require('fastify');
|
|
10
|
-
var fp = require('fastify-plugin');
|
|
11
|
-
var socket_io = require('socket.io');
|
|
12
|
-
|
|
13
|
-
// This is a clone of fastify-socket.io which runs with recent fastify versions.
|
|
14
|
-
const fastifySocketIO = fp(async (fastify, opts) => {
|
|
15
|
-
const ioServer = new socket_io.Server(fastify.server, opts);
|
|
16
|
-
fastify.decorate("io", ioServer);
|
|
17
|
-
fastify.addHook("preClose", done => {
|
|
18
|
-
ioServer.disconnectSockets();
|
|
19
|
-
ioServer.close();
|
|
20
|
-
done();
|
|
21
|
-
});
|
|
22
|
-
}, { fastify: ">=5.x.x", name: "socket.io" });
|
|
23
|
-
|
|
24
|
-
// Copyright 2024 IOTA Stiftung.
|
|
25
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
26
|
-
/**
|
|
27
|
-
* Implementation of the web server using Fastify.
|
|
28
|
-
*/
|
|
29
|
-
class FastifyWebServer {
|
|
30
|
-
/**
|
|
31
|
-
* Runtime name for the class in camel case.
|
|
32
|
-
* @internal
|
|
33
|
-
*/
|
|
34
|
-
static _CLASS_NAME_CAMEL_CASE = core.StringHelper.camelCase("FastifyWebServer");
|
|
35
|
-
/**
|
|
36
|
-
* Default port for running the server.
|
|
37
|
-
* @internal
|
|
38
|
-
*/
|
|
39
|
-
static _DEFAULT_PORT = 3000;
|
|
40
|
-
/**
|
|
41
|
-
* Default host for running the server.
|
|
42
|
-
* @internal
|
|
43
|
-
*/
|
|
44
|
-
static _DEFAULT_HOST = "localhost";
|
|
45
|
-
/**
|
|
46
|
-
* Runtime name for the class.
|
|
47
|
-
*/
|
|
48
|
-
CLASS_NAME = "FastifyWebServer";
|
|
49
|
-
/**
|
|
50
|
-
* The logging component type.
|
|
51
|
-
* @internal
|
|
52
|
-
*/
|
|
53
|
-
_loggingComponentType;
|
|
54
|
-
/**
|
|
55
|
-
* The logging component.
|
|
56
|
-
* @internal
|
|
57
|
-
*/
|
|
58
|
-
_logging;
|
|
59
|
-
/**
|
|
60
|
-
* The options for the server.
|
|
61
|
-
* @internal
|
|
62
|
-
*/
|
|
63
|
-
_options;
|
|
64
|
-
/**
|
|
65
|
-
* The Fastify instance.
|
|
66
|
-
* @internal
|
|
67
|
-
*/
|
|
68
|
-
_fastify;
|
|
69
|
-
/**
|
|
70
|
-
* The options for the socket server.
|
|
71
|
-
* @internal
|
|
72
|
-
*/
|
|
73
|
-
_socketConfig;
|
|
74
|
-
/**
|
|
75
|
-
* Whether the server has been started.
|
|
76
|
-
* @internal
|
|
77
|
-
*/
|
|
78
|
-
_started;
|
|
79
|
-
/**
|
|
80
|
-
* The mime type processors.
|
|
81
|
-
* @internal
|
|
82
|
-
*/
|
|
83
|
-
_mimeTypeProcessors;
|
|
84
|
-
/**
|
|
85
|
-
* Include the stack with errors.
|
|
86
|
-
* @internal
|
|
87
|
-
*/
|
|
88
|
-
_includeErrorStack;
|
|
89
|
-
/**
|
|
90
|
-
* Create a new instance of FastifyWebServer.
|
|
91
|
-
* @param options The options for the server.
|
|
92
|
-
*/
|
|
93
|
-
constructor(options) {
|
|
94
|
-
this._loggingComponentType = options?.loggingComponentType;
|
|
95
|
-
this._logging = core.ComponentFactory.getIfExists(options?.loggingComponentType ?? "logging");
|
|
96
|
-
this._fastify = Fastify({
|
|
97
|
-
routerOptions: {
|
|
98
|
-
maxParamLength: 2000
|
|
99
|
-
},
|
|
100
|
-
...options?.config?.web
|
|
101
|
-
// Need this cast for now as maxParamLength has moved in to routerOptions
|
|
102
|
-
// but the TS defs has not been updated yet
|
|
103
|
-
});
|
|
104
|
-
this._socketConfig = {
|
|
105
|
-
path: "/socket",
|
|
106
|
-
...options?.config?.socket
|
|
107
|
-
};
|
|
108
|
-
this._started = false;
|
|
109
|
-
this._mimeTypeProcessors = options?.mimeTypeProcessors ?? [];
|
|
110
|
-
const hasJsonLd = this._mimeTypeProcessors.find(processor => processor.CLASS_NAME === "json-ld");
|
|
111
|
-
if (!hasJsonLd) {
|
|
112
|
-
this._mimeTypeProcessors.push(new apiProcessors.JsonLdMimeTypeProcessor());
|
|
113
|
-
}
|
|
114
|
-
this._includeErrorStack = options?.config?.includeErrorStack ?? false;
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Get the web server instance.
|
|
118
|
-
* @returns The web server instance.
|
|
119
|
-
*/
|
|
120
|
-
getInstance() {
|
|
121
|
-
return this._fastify;
|
|
122
|
-
}
|
|
123
|
-
/**
|
|
124
|
-
* Build the server.
|
|
125
|
-
* @param restRouteProcessors The processors for incoming requests over REST.
|
|
126
|
-
* @param restRoutes The REST routes.
|
|
127
|
-
* @param socketRouteProcessors The processors for incoming requests over Sockets.
|
|
128
|
-
* @param socketRoutes The socket routes.
|
|
129
|
-
* @param options Options for building the server.
|
|
130
|
-
* @returns Nothing.
|
|
131
|
-
*/
|
|
132
|
-
async build(restRouteProcessors, restRoutes, socketRouteProcessors, socketRoutes, options) {
|
|
133
|
-
if (core.Is.arrayValue(restRoutes) && !core.Is.arrayValue(restRouteProcessors)) {
|
|
134
|
-
throw new core.GeneralError(this.CLASS_NAME, "noRestProcessors");
|
|
135
|
-
}
|
|
136
|
-
if (core.Is.arrayValue(socketRoutes) && !core.Is.arrayValue(socketRouteProcessors)) {
|
|
137
|
-
throw new core.GeneralError(this.CLASS_NAME, "noSocketProcessors");
|
|
138
|
-
}
|
|
139
|
-
await this._logging?.log({
|
|
140
|
-
level: "info",
|
|
141
|
-
ts: Date.now(),
|
|
142
|
-
source: this.CLASS_NAME,
|
|
143
|
-
message: `${FastifyWebServer._CLASS_NAME_CAMEL_CASE}.building`
|
|
144
|
-
});
|
|
145
|
-
this._options = options;
|
|
146
|
-
await this._fastify.register(FastifyCompress);
|
|
147
|
-
if (core.Is.arrayValue(socketRoutes)) {
|
|
148
|
-
await this._fastify.register(fastifySocketIO, this._socketConfig);
|
|
149
|
-
}
|
|
150
|
-
if (core.Is.arrayValue(this._mimeTypeProcessors)) {
|
|
151
|
-
for (const contentTypeHandler of this._mimeTypeProcessors) {
|
|
152
|
-
this._fastify.addContentTypeParser(contentTypeHandler.getTypes(), { parseAs: "buffer" }, (request, body, done) => {
|
|
153
|
-
// Fastify does not handle this method correctly if it is async
|
|
154
|
-
// so we have to use the callback method
|
|
155
|
-
contentTypeHandler
|
|
156
|
-
.handle(body)
|
|
157
|
-
// eslint-disable-next-line promise/prefer-await-to-then, promise/no-callback-in-promise
|
|
158
|
-
.then(processed => done(null, processed))
|
|
159
|
-
// eslint-disable-next-line promise/prefer-await-to-then, promise/no-callback-in-promise
|
|
160
|
-
.catch(err => done(core.BaseError.fromError(err)));
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
await this.initCors(options);
|
|
165
|
-
this._fastify.setNotFoundHandler({}, async (request, reply) => this.handleRequestRest(restRouteProcessors ?? [], request, reply));
|
|
166
|
-
this._fastify.setErrorHandler(async (error, request, reply) => {
|
|
167
|
-
// If code property is set this is a fastify error
|
|
168
|
-
// otherwise it's from our framework
|
|
169
|
-
let httpStatusCode;
|
|
170
|
-
let err;
|
|
171
|
-
if (core.Is.number(error.code)) {
|
|
172
|
-
err = {
|
|
173
|
-
source: this.CLASS_NAME,
|
|
174
|
-
name: error.name,
|
|
175
|
-
message: `${error.code}: ${error.message}`
|
|
176
|
-
};
|
|
177
|
-
httpStatusCode = error.statusCode ?? web.HttpStatusCode.badRequest;
|
|
178
|
-
}
|
|
179
|
-
else {
|
|
180
|
-
const errorAndCode = apiModels.HttpErrorHelper.processError(error);
|
|
181
|
-
err = errorAndCode.error;
|
|
182
|
-
httpStatusCode = errorAndCode.httpStatusCode;
|
|
183
|
-
}
|
|
184
|
-
await this._logging?.log({
|
|
185
|
-
level: "error",
|
|
186
|
-
ts: Date.now(),
|
|
187
|
-
source: this.CLASS_NAME,
|
|
188
|
-
message: `${FastifyWebServer._CLASS_NAME_CAMEL_CASE}.badRequest`,
|
|
189
|
-
error: err
|
|
190
|
-
});
|
|
191
|
-
return reply.status(httpStatusCode).send({
|
|
192
|
-
error: err
|
|
193
|
-
});
|
|
194
|
-
});
|
|
195
|
-
await this.addRoutesRest(restRouteProcessors, restRoutes);
|
|
196
|
-
await this.addRoutesSocket(socketRouteProcessors, socketRoutes);
|
|
197
|
-
}
|
|
198
|
-
/**
|
|
199
|
-
* Start the server.
|
|
200
|
-
* @returns Nothing.
|
|
201
|
-
*/
|
|
202
|
-
async start() {
|
|
203
|
-
const host = this._options?.host ?? FastifyWebServer._DEFAULT_HOST;
|
|
204
|
-
const port = this._options?.port ?? FastifyWebServer._DEFAULT_PORT;
|
|
205
|
-
await this._logging?.log({
|
|
206
|
-
level: "info",
|
|
207
|
-
ts: Date.now(),
|
|
208
|
-
source: this.CLASS_NAME,
|
|
209
|
-
message: `${FastifyWebServer._CLASS_NAME_CAMEL_CASE}.starting`,
|
|
210
|
-
data: {
|
|
211
|
-
host,
|
|
212
|
-
port
|
|
213
|
-
}
|
|
214
|
-
});
|
|
215
|
-
if (!this._started) {
|
|
216
|
-
try {
|
|
217
|
-
await this._fastify.listen({ port, host });
|
|
218
|
-
const addresses = this._fastify.addresses();
|
|
219
|
-
const protocol = core.Is.object(this._fastify.initialConfig.https) ? "https://" : "http://";
|
|
220
|
-
await this._logging?.log({
|
|
221
|
-
level: "info",
|
|
222
|
-
ts: Date.now(),
|
|
223
|
-
source: this.CLASS_NAME,
|
|
224
|
-
message: `${FastifyWebServer._CLASS_NAME_CAMEL_CASE}.started`,
|
|
225
|
-
data: {
|
|
226
|
-
addresses: addresses
|
|
227
|
-
.map(a => `${protocol}${a.family === "IPv6" ? "[" : ""}${a.address}${a.family === "IPv6" ? "]" : ""}:${a.port}`)
|
|
228
|
-
.join(", ")
|
|
229
|
-
}
|
|
230
|
-
});
|
|
231
|
-
this._started = true;
|
|
232
|
-
}
|
|
233
|
-
catch (err) {
|
|
234
|
-
await this._logging?.log({
|
|
235
|
-
level: "error",
|
|
236
|
-
ts: Date.now(),
|
|
237
|
-
source: this.CLASS_NAME,
|
|
238
|
-
message: `${FastifyWebServer._CLASS_NAME_CAMEL_CASE}.startFailed`,
|
|
239
|
-
error: core.BaseError.fromError(err)
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
/**
|
|
245
|
-
* Stop the server.
|
|
246
|
-
* @returns Nothing.
|
|
247
|
-
*/
|
|
248
|
-
async stop() {
|
|
249
|
-
if (this._started) {
|
|
250
|
-
this._started = false;
|
|
251
|
-
await this._fastify.close();
|
|
252
|
-
await this._logging?.log({
|
|
253
|
-
level: "info",
|
|
254
|
-
ts: Date.now(),
|
|
255
|
-
source: this.CLASS_NAME,
|
|
256
|
-
message: `${FastifyWebServer._CLASS_NAME_CAMEL_CASE}.stopped`
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
/**
|
|
261
|
-
* Add the REST routes to the server.
|
|
262
|
-
* @param restRouteProcessors The processors for the incoming requests.
|
|
263
|
-
* @param restRoutes The REST routes to add.
|
|
264
|
-
* @internal
|
|
265
|
-
*/
|
|
266
|
-
async addRoutesRest(restRouteProcessors, restRoutes) {
|
|
267
|
-
if (core.Is.arrayValue(restRouteProcessors) && core.Is.arrayValue(restRoutes)) {
|
|
268
|
-
for (const restRoute of restRoutes) {
|
|
269
|
-
let path = core.StringHelper.trimTrailingSlashes(restRoute.path);
|
|
270
|
-
if (!path.startsWith("/")) {
|
|
271
|
-
path = `/${path}`;
|
|
272
|
-
}
|
|
273
|
-
await this._logging?.log({
|
|
274
|
-
level: "info",
|
|
275
|
-
ts: Date.now(),
|
|
276
|
-
source: this.CLASS_NAME,
|
|
277
|
-
message: `${FastifyWebServer._CLASS_NAME_CAMEL_CASE}.restRouteAdded`,
|
|
278
|
-
data: { route: path, method: restRoute.method }
|
|
279
|
-
});
|
|
280
|
-
const method = restRoute.method.toLowerCase();
|
|
281
|
-
this._fastify[method](path, async (request, reply) => this.handleRequestRest(restRouteProcessors, request, reply, restRoute));
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
/**
|
|
286
|
-
* Add the socket routes to the server.
|
|
287
|
-
* @param socketRouteProcessors The processors for the incoming requests.
|
|
288
|
-
* @param socketRoutes The socket routes to add.
|
|
289
|
-
* @internal
|
|
290
|
-
*/
|
|
291
|
-
async addRoutesSocket(socketRouteProcessors, socketRoutes) {
|
|
292
|
-
if (core.Is.arrayValue(socketRouteProcessors) && core.Is.arrayValue(socketRoutes)) {
|
|
293
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
294
|
-
const io = this._fastify.io;
|
|
295
|
-
for (const socketRoute of socketRoutes) {
|
|
296
|
-
const path = core.StringHelper.trimLeadingSlashes(core.StringHelper.trimTrailingSlashes(socketRoute.path));
|
|
297
|
-
const pathParts = path.split("/");
|
|
298
|
-
const namespace = `/${pathParts[0]}`;
|
|
299
|
-
const topic = pathParts.slice(1).join("/");
|
|
300
|
-
await this._logging?.log({
|
|
301
|
-
level: "info",
|
|
302
|
-
ts: Date.now(),
|
|
303
|
-
source: this.CLASS_NAME,
|
|
304
|
-
message: `${FastifyWebServer._CLASS_NAME_CAMEL_CASE}.socketRouteAdded`,
|
|
305
|
-
data: {
|
|
306
|
-
handshakePath: this._socketConfig.path,
|
|
307
|
-
namespace,
|
|
308
|
-
eventName: topic
|
|
309
|
-
}
|
|
310
|
-
});
|
|
311
|
-
const socketNamespace = io.of(namespace);
|
|
312
|
-
socketNamespace.on("connection", async (socket) => {
|
|
313
|
-
const socketServerRequest = {
|
|
314
|
-
method: web.HttpMethod.GET,
|
|
315
|
-
url: socket.handshake.url,
|
|
316
|
-
query: socket.handshake.query,
|
|
317
|
-
headers: socket.handshake.headers,
|
|
318
|
-
socketId: socket.id
|
|
319
|
-
};
|
|
320
|
-
// Pass the connected information on to any processors
|
|
321
|
-
try {
|
|
322
|
-
for (const socketRouteProcessor of socketRouteProcessors) {
|
|
323
|
-
if (socketRouteProcessor.connected) {
|
|
324
|
-
await socketRouteProcessor.connected(socketServerRequest, socketRoute, this._loggingComponentType);
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
catch (err) {
|
|
329
|
-
const { error, httpStatusCode } = apiModels.HttpErrorHelper.processError(err, this._includeErrorStack);
|
|
330
|
-
const response = {};
|
|
331
|
-
apiModels.HttpErrorHelper.buildResponse(response, error, httpStatusCode);
|
|
332
|
-
socket.emit(topic, response);
|
|
333
|
-
}
|
|
334
|
-
socket.on("disconnect", async () => {
|
|
335
|
-
try {
|
|
336
|
-
// The socket disconnected so notify any processors
|
|
337
|
-
for (const socketRouteProcessor of socketRouteProcessors) {
|
|
338
|
-
if (socketRouteProcessor.disconnected) {
|
|
339
|
-
await socketRouteProcessor.disconnected(socketServerRequest, socketRoute, this._loggingComponentType);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
catch {
|
|
344
|
-
// If something fails on a disconnect there is not much we can do with it
|
|
345
|
-
}
|
|
346
|
-
});
|
|
347
|
-
// Handle any incoming messages
|
|
348
|
-
socket.on(topic, async (data) => {
|
|
349
|
-
await this.handleRequestSocket(socketRouteProcessors, socketRoute, socket, `/${pathParts.join("/")}`, topic, data);
|
|
350
|
-
});
|
|
351
|
-
});
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
/**
|
|
356
|
-
* Handle the incoming REST request.
|
|
357
|
-
* @param restRouteProcessors The hooks to process the incoming requests.
|
|
358
|
-
* @param request The incoming request.
|
|
359
|
-
* @param reply The outgoing response.
|
|
360
|
-
* @param restRoute The REST route to handle.
|
|
361
|
-
* @internal
|
|
362
|
-
*/
|
|
363
|
-
async handleRequestRest(restRouteProcessors, request, reply, restRoute) {
|
|
364
|
-
const httpServerRequest = {
|
|
365
|
-
method: request.method.toUpperCase(),
|
|
366
|
-
url: `${request.protocol}://${request.hostname}${request.url}`,
|
|
367
|
-
body: request.body,
|
|
368
|
-
query: request.query,
|
|
369
|
-
pathParams: request.params,
|
|
370
|
-
headers: request.headers
|
|
371
|
-
};
|
|
372
|
-
const httpResponse = {};
|
|
373
|
-
const httpRequestIdentity = {};
|
|
374
|
-
const processorState = {};
|
|
375
|
-
await this.runProcessorsRest(restRouteProcessors, restRoute, httpServerRequest, httpResponse, httpRequestIdentity, processorState);
|
|
376
|
-
if (!core.Is.empty(httpResponse.headers)) {
|
|
377
|
-
for (const header of Object.keys(httpResponse.headers)) {
|
|
378
|
-
reply.header(header, httpResponse.headers[header]);
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
return reply
|
|
382
|
-
.status((httpResponse.statusCode ?? web.HttpStatusCode.ok))
|
|
383
|
-
.send(httpResponse.body);
|
|
384
|
-
}
|
|
385
|
-
/**
|
|
386
|
-
* Run the REST processors for the route.
|
|
387
|
-
* @param restRouteProcessors The processors to run.
|
|
388
|
-
* @param restRoute The route to process.
|
|
389
|
-
* @param httpServerRequest The incoming request.
|
|
390
|
-
* @param httpResponse The outgoing response.
|
|
391
|
-
* @param httpRequestIdentity The identity context for the request.
|
|
392
|
-
* @internal
|
|
393
|
-
*/
|
|
394
|
-
async runProcessorsRest(restRouteProcessors, restRoute, httpServerRequest, httpResponse, httpRequestIdentity, processorState) {
|
|
395
|
-
try {
|
|
396
|
-
for (const routeProcessor of restRouteProcessors) {
|
|
397
|
-
if (routeProcessor.pre) {
|
|
398
|
-
await routeProcessor.pre(httpServerRequest, httpResponse, restRoute, httpRequestIdentity, processorState, this._loggingComponentType);
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
for (const routeProcessor of restRouteProcessors) {
|
|
402
|
-
if (routeProcessor.process) {
|
|
403
|
-
await routeProcessor.process(httpServerRequest, httpResponse, restRoute, httpRequestIdentity, processorState, this._loggingComponentType);
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
for (const routeProcessor of restRouteProcessors) {
|
|
407
|
-
if (routeProcessor.post) {
|
|
408
|
-
await routeProcessor.post(httpServerRequest, httpResponse, restRoute, httpRequestIdentity, processorState, this._loggingComponentType);
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
catch (err) {
|
|
413
|
-
const { error, httpStatusCode } = apiModels.HttpErrorHelper.processError(err, this._includeErrorStack);
|
|
414
|
-
apiModels.HttpErrorHelper.buildResponse(httpResponse, error, httpStatusCode);
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
/**
|
|
418
|
-
* Handle the incoming socket request.
|
|
419
|
-
* @param socketRouteProcessors The hooks to process the incoming requests.
|
|
420
|
-
* @param socketRoute The socket route to handle.
|
|
421
|
-
* @param socket The socket to handle.
|
|
422
|
-
* @param fullPath The full path of the socket route.
|
|
423
|
-
* @param emitTopic The topic to emit the response on.
|
|
424
|
-
* @param data The incoming data.
|
|
425
|
-
* @internal
|
|
426
|
-
*/
|
|
427
|
-
async handleRequestSocket(socketRouteProcessors, socketRoute, socket, fullPath, emitTopic, request) {
|
|
428
|
-
const socketServerRequest = {
|
|
429
|
-
method: web.HttpMethod.GET,
|
|
430
|
-
url: fullPath,
|
|
431
|
-
query: socket.handshake.query,
|
|
432
|
-
headers: socket.handshake.headers,
|
|
433
|
-
body: request.body,
|
|
434
|
-
socketId: socket.id
|
|
435
|
-
};
|
|
436
|
-
const httpResponse = {};
|
|
437
|
-
const httpRequestIdentity = {};
|
|
438
|
-
const processorState = {};
|
|
439
|
-
delete socketServerRequest.query?.EIO;
|
|
440
|
-
delete socketServerRequest.query?.transport;
|
|
441
|
-
await this.runProcessorsSocket(socketRouteProcessors, socketRoute, socketServerRequest, httpResponse, httpRequestIdentity, processorState, emitTopic, async (topic, response) => {
|
|
442
|
-
await socket.emit(topic, response);
|
|
443
|
-
});
|
|
444
|
-
}
|
|
445
|
-
/**
|
|
446
|
-
* Run the socket processors for the route.
|
|
447
|
-
* @param socketId The id of the socket.
|
|
448
|
-
* @param socketRouteProcessors The processors to run.
|
|
449
|
-
* @param socketRoute The route to process.
|
|
450
|
-
* @param socketServerRequest The incoming request.
|
|
451
|
-
* @param httpResponse The outgoing response.
|
|
452
|
-
* @param httpRequestIdentity The identity context for the request.
|
|
453
|
-
* @param processorState The state handed through the processors.
|
|
454
|
-
* @param requestTopic The topic of the request.
|
|
455
|
-
* @internal
|
|
456
|
-
*/
|
|
457
|
-
async runProcessorsSocket(socketRouteProcessors, socketRoute, socketServerRequest, httpResponse, httpRequestIdentity, processorState, requestTopic, responseEmitter) {
|
|
458
|
-
// Custom emit method which will also call the post processors
|
|
459
|
-
const postProcessEmit = async (topic, response, responseProcessorState) => {
|
|
460
|
-
await responseEmitter(topic, response);
|
|
461
|
-
try {
|
|
462
|
-
// The post processors are called after the response has been emitted
|
|
463
|
-
for (const postSocketRouteProcessor of socketRouteProcessors) {
|
|
464
|
-
if (postSocketRouteProcessor.post) {
|
|
465
|
-
await postSocketRouteProcessor.post(socketServerRequest, response, socketRoute, httpRequestIdentity, responseProcessorState, this._loggingComponentType);
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
catch (err) {
|
|
470
|
-
this._logging?.log({
|
|
471
|
-
level: "error",
|
|
472
|
-
ts: Date.now(),
|
|
473
|
-
source: this.CLASS_NAME,
|
|
474
|
-
message: `${FastifyWebServer._CLASS_NAME_CAMEL_CASE}.postProcessorError`,
|
|
475
|
-
error: core.BaseError.fromError(err),
|
|
476
|
-
data: {
|
|
477
|
-
route: socketRoute.path
|
|
478
|
-
}
|
|
479
|
-
});
|
|
480
|
-
}
|
|
481
|
-
};
|
|
482
|
-
try {
|
|
483
|
-
for (const socketRouteProcessor of socketRouteProcessors) {
|
|
484
|
-
if (socketRouteProcessor.pre) {
|
|
485
|
-
await socketRouteProcessor.pre(socketServerRequest, httpResponse, socketRoute, httpRequestIdentity, processorState, this._loggingComponentType);
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
// We always call all the processors regardless of any response set by a previous processor.
|
|
489
|
-
// But if a pre processor sets a status code, we will emit the response manually, as the pre
|
|
490
|
-
// and post processors do not receive the emit method, they just populate the response object.
|
|
491
|
-
if (!core.Is.empty(httpResponse.statusCode)) {
|
|
492
|
-
await postProcessEmit(requestTopic, httpResponse, processorState);
|
|
493
|
-
}
|
|
494
|
-
for (const socketRouteProcessor of socketRouteProcessors) {
|
|
495
|
-
if (socketRouteProcessor.process) {
|
|
496
|
-
await socketRouteProcessor.process(socketServerRequest, httpResponse, socketRoute, httpRequestIdentity, processorState, async (topic, processResponse) => {
|
|
497
|
-
await postProcessEmit(topic, processResponse, processorState);
|
|
498
|
-
}, this._loggingComponentType);
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
// If the processors set the status to any kind of error then we should emit this manually
|
|
502
|
-
if (core.Is.integer(httpResponse.statusCode) &&
|
|
503
|
-
httpResponse.statusCode >= web.HttpStatusCode.badRequest) {
|
|
504
|
-
await postProcessEmit(requestTopic, httpResponse, processorState);
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
catch (err) {
|
|
508
|
-
// Emit any unhandled errors manually
|
|
509
|
-
const { error, httpStatusCode } = apiModels.HttpErrorHelper.processError(err, this._includeErrorStack);
|
|
510
|
-
apiModels.HttpErrorHelper.buildResponse(httpResponse, error, httpStatusCode);
|
|
511
|
-
await postProcessEmit(requestTopic, httpResponse, processorState);
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
/**
|
|
515
|
-
* Initialize the cors options.
|
|
516
|
-
* @param options The web server options.
|
|
517
|
-
* @internal
|
|
518
|
-
*/
|
|
519
|
-
async initCors(options) {
|
|
520
|
-
let origins = ["*"];
|
|
521
|
-
if (core.Is.arrayValue(options?.corsOrigins)) {
|
|
522
|
-
origins = options?.corsOrigins;
|
|
523
|
-
}
|
|
524
|
-
else if (core.Is.stringValue(options?.corsOrigins)) {
|
|
525
|
-
origins = [options?.corsOrigins];
|
|
526
|
-
}
|
|
527
|
-
const hasWildcardOrigin = origins.includes("*");
|
|
528
|
-
const methods = options?.methods ?? [
|
|
529
|
-
web.HttpMethod.GET,
|
|
530
|
-
web.HttpMethod.PUT,
|
|
531
|
-
web.HttpMethod.POST,
|
|
532
|
-
web.HttpMethod.DELETE,
|
|
533
|
-
web.HttpMethod.OPTIONS
|
|
534
|
-
];
|
|
535
|
-
const allowedHeaders = [
|
|
536
|
-
"Access-Control-Allow-Origin",
|
|
537
|
-
"Content-Encoding",
|
|
538
|
-
"Accept-Encoding",
|
|
539
|
-
web.HeaderTypes.ContentType,
|
|
540
|
-
web.HeaderTypes.Authorization,
|
|
541
|
-
web.HeaderTypes.Accept
|
|
542
|
-
];
|
|
543
|
-
const exposedHeaders = [web.HeaderTypes.ContentDisposition, web.HeaderTypes.Location];
|
|
544
|
-
if (core.Is.arrayValue(options?.allowedHeaders)) {
|
|
545
|
-
allowedHeaders.push(...options.allowedHeaders);
|
|
546
|
-
}
|
|
547
|
-
if (core.Is.arrayValue(options?.exposedHeaders)) {
|
|
548
|
-
exposedHeaders.push(...options.exposedHeaders);
|
|
549
|
-
}
|
|
550
|
-
await this._fastify.register(FastifyCors, {
|
|
551
|
-
origin: (origin, callback) => {
|
|
552
|
-
callback(null, hasWildcardOrigin ? true : origins.includes(origin));
|
|
553
|
-
},
|
|
554
|
-
methods,
|
|
555
|
-
allowedHeaders,
|
|
556
|
-
exposedHeaders,
|
|
557
|
-
credentials: true
|
|
558
|
-
});
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
exports.FastifyWebServer = FastifyWebServer;
|