@twin.org/api-server-fastify 0.0.1-next.12 → 0.0.1-next.13
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/cjs/index.cjs +259 -40
- package/dist/esm/index.mjs +259 -40
- package/dist/types/fastifySocketIo.d.ts +4 -0
- package/dist/types/fastifyWebServer.d.ts +9 -6
- package/dist/types/index.d.ts +1 -0
- package/dist/types/models/IFastifyWebServerConfig.d.ts +19 -0
- package/docs/changelog.md +1 -1
- package/docs/reference/classes/FastifyWebServer.md +14 -6
- package/docs/reference/index.md +4 -0
- package/docs/reference/interfaces/IFastifyWebServerConfig.md +27 -0
- package/locales/en.json +3 -1
- package/package.json +5 -4
package/dist/cjs/index.cjs
CHANGED
|
@@ -7,6 +7,19 @@ var core = require('@twin.org/core');
|
|
|
7
7
|
var loggingModels = require('@twin.org/logging-models');
|
|
8
8
|
var web = require('@twin.org/web');
|
|
9
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" });
|
|
10
23
|
|
|
11
24
|
// Copyright 2024 IOTA Stiftung.
|
|
12
25
|
// SPDX-License-Identifier: Apache-2.0.
|
|
@@ -48,6 +61,11 @@ class FastifyWebServer {
|
|
|
48
61
|
* @internal
|
|
49
62
|
*/
|
|
50
63
|
_fastify;
|
|
64
|
+
/**
|
|
65
|
+
* The options for the socket server.
|
|
66
|
+
* @internal
|
|
67
|
+
*/
|
|
68
|
+
_socketConfig;
|
|
51
69
|
/**
|
|
52
70
|
* Whether the server has been started.
|
|
53
71
|
* @internal
|
|
@@ -58,20 +76,33 @@ class FastifyWebServer {
|
|
|
58
76
|
* @internal
|
|
59
77
|
*/
|
|
60
78
|
_mimeTypeProcessors;
|
|
79
|
+
/**
|
|
80
|
+
* Include the stack with errors.
|
|
81
|
+
* @internal
|
|
82
|
+
*/
|
|
83
|
+
_includeErrorStack;
|
|
61
84
|
/**
|
|
62
85
|
* Create a new instance of FastifyWebServer.
|
|
63
86
|
* @param options The options for the server.
|
|
64
87
|
* @param options.loggingConnectorType The type of the logging connector to use, if undefined, no logging will happen.
|
|
65
|
-
* @param options.config Additional options for the Fastify server.
|
|
66
88
|
* @param options.mimeTypeProcessors Additional MIME type processors.
|
|
89
|
+
* @param options.config Additional configuration for the server.
|
|
67
90
|
*/
|
|
68
91
|
constructor(options) {
|
|
69
92
|
this._loggingConnector = core.Is.stringValue(options?.loggingConnectorType)
|
|
70
93
|
? loggingModels.LoggingConnectorFactory.get(options.loggingConnectorType)
|
|
71
94
|
: undefined;
|
|
72
|
-
this._fastify = Fastify({
|
|
95
|
+
this._fastify = Fastify({
|
|
96
|
+
maxParamLength: 2000,
|
|
97
|
+
...options?.config?.web
|
|
98
|
+
});
|
|
99
|
+
this._socketConfig = {
|
|
100
|
+
path: "/socket",
|
|
101
|
+
...options?.config?.socket
|
|
102
|
+
};
|
|
73
103
|
this._started = false;
|
|
74
104
|
this._mimeTypeProcessors = options?.mimeTypeProcessors ?? [];
|
|
105
|
+
this._includeErrorStack = options?.config?.includeErrorStack ?? false;
|
|
75
106
|
}
|
|
76
107
|
/**
|
|
77
108
|
* Get the web server instance.
|
|
@@ -82,14 +113,19 @@ class FastifyWebServer {
|
|
|
82
113
|
}
|
|
83
114
|
/**
|
|
84
115
|
* Build the server.
|
|
85
|
-
* @param restRouteProcessors The
|
|
116
|
+
* @param restRouteProcessors The processors for incoming requests over REST.
|
|
86
117
|
* @param restRoutes The REST routes.
|
|
118
|
+
* @param socketRouteProcessors The processors for incoming requests over Sockets.
|
|
119
|
+
* @param socketRoutes The socket routes.
|
|
87
120
|
* @param options Options for building the server.
|
|
88
121
|
* @returns Nothing.
|
|
89
122
|
*/
|
|
90
|
-
async build(restRouteProcessors, restRoutes, options) {
|
|
91
|
-
if (!core.Is.arrayValue(restRouteProcessors)) {
|
|
92
|
-
throw new core.GeneralError(this.CLASS_NAME, "
|
|
123
|
+
async build(restRouteProcessors, restRoutes, socketRouteProcessors, socketRoutes, options) {
|
|
124
|
+
if (core.Is.arrayValue(restRoutes) && !core.Is.arrayValue(restRouteProcessors)) {
|
|
125
|
+
throw new core.GeneralError(this.CLASS_NAME, "noRestProcessors");
|
|
126
|
+
}
|
|
127
|
+
if (core.Is.arrayValue(socketRoutes) && !core.Is.arrayValue(socketRouteProcessors)) {
|
|
128
|
+
throw new core.GeneralError(this.CLASS_NAME, "noSocketProcessors");
|
|
93
129
|
}
|
|
94
130
|
await this._loggingConnector?.log({
|
|
95
131
|
level: "info",
|
|
@@ -99,6 +135,9 @@ class FastifyWebServer {
|
|
|
99
135
|
});
|
|
100
136
|
this._options = options;
|
|
101
137
|
await this._fastify.register(FastifyCompress);
|
|
138
|
+
if (core.Is.arrayValue(socketRoutes)) {
|
|
139
|
+
await this._fastify.register(fastifySocketIO, this._socketConfig);
|
|
140
|
+
}
|
|
102
141
|
if (core.Is.arrayValue(this._mimeTypeProcessors)) {
|
|
103
142
|
for (const contentTypeHandler of this._mimeTypeProcessors) {
|
|
104
143
|
this._fastify.addContentTypeParser(contentTypeHandler.getTypes(), { parseAs: "buffer" }, async (request, body, done) => {
|
|
@@ -113,7 +152,7 @@ class FastifyWebServer {
|
|
|
113
152
|
}
|
|
114
153
|
}
|
|
115
154
|
await this.initCors(options);
|
|
116
|
-
this._fastify.setNotFoundHandler({}, async (request, reply) => this.
|
|
155
|
+
this._fastify.setNotFoundHandler({}, async (request, reply) => this.handleRequestRest(restRouteProcessors ?? [], request, reply));
|
|
117
156
|
this._fastify.setErrorHandler(async (error, request, reply) => {
|
|
118
157
|
// If code property is set this is a fastify error
|
|
119
158
|
// otherwise it's from our framework
|
|
@@ -143,22 +182,8 @@ class FastifyWebServer {
|
|
|
143
182
|
error: err
|
|
144
183
|
});
|
|
145
184
|
});
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
let path = core.StringHelper.trimTrailingSlashes(restRoute.path);
|
|
149
|
-
if (!path.startsWith("/")) {
|
|
150
|
-
path = `/${path}`;
|
|
151
|
-
}
|
|
152
|
-
await this._loggingConnector?.log({
|
|
153
|
-
level: "info",
|
|
154
|
-
ts: Date.now(),
|
|
155
|
-
source: this.CLASS_NAME,
|
|
156
|
-
message: `${FastifyWebServer._CLASS_NAME_CAMEL_CASE}.restRouteAdded`,
|
|
157
|
-
data: { route: path, method: restRoute.method }
|
|
158
|
-
});
|
|
159
|
-
const method = restRoute.method.toLowerCase();
|
|
160
|
-
this._fastify[method](path, async (request, reply) => this.handleRequest(restRouteProcessors, request, reply, restRoute));
|
|
161
|
-
}
|
|
185
|
+
await this.addRoutesRest(restRouteProcessors, restRoutes);
|
|
186
|
+
await this.addRoutesSocket(socketRouteProcessors, socketRoutes);
|
|
162
187
|
}
|
|
163
188
|
/**
|
|
164
189
|
* Start the server.
|
|
@@ -223,14 +248,109 @@ class FastifyWebServer {
|
|
|
223
248
|
}
|
|
224
249
|
}
|
|
225
250
|
/**
|
|
226
|
-
*
|
|
251
|
+
* Add the REST routes to the server.
|
|
252
|
+
* @param restRouteProcessors The processors for the incoming requests.
|
|
253
|
+
* @param restRoutes The REST routes to add.
|
|
254
|
+
* @internal
|
|
255
|
+
*/
|
|
256
|
+
async addRoutesRest(restRouteProcessors, restRoutes) {
|
|
257
|
+
if (core.Is.arrayValue(restRouteProcessors) && core.Is.arrayValue(restRoutes)) {
|
|
258
|
+
for (const restRoute of restRoutes) {
|
|
259
|
+
let path = core.StringHelper.trimTrailingSlashes(restRoute.path);
|
|
260
|
+
if (!path.startsWith("/")) {
|
|
261
|
+
path = `/${path}`;
|
|
262
|
+
}
|
|
263
|
+
await this._loggingConnector?.log({
|
|
264
|
+
level: "info",
|
|
265
|
+
ts: Date.now(),
|
|
266
|
+
source: this.CLASS_NAME,
|
|
267
|
+
message: `${FastifyWebServer._CLASS_NAME_CAMEL_CASE}.restRouteAdded`,
|
|
268
|
+
data: { route: path, method: restRoute.method }
|
|
269
|
+
});
|
|
270
|
+
const method = restRoute.method.toLowerCase();
|
|
271
|
+
this._fastify[method](path, async (request, reply) => this.handleRequestRest(restRouteProcessors, request, reply, restRoute));
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Add the socket routes to the server.
|
|
277
|
+
* @param socketRouteProcessors The processors for the incoming requests.
|
|
278
|
+
* @param socketRoutes The socket routes to add.
|
|
279
|
+
* @internal
|
|
280
|
+
*/
|
|
281
|
+
async addRoutesSocket(socketRouteProcessors, socketRoutes) {
|
|
282
|
+
if (core.Is.arrayValue(socketRouteProcessors) && core.Is.arrayValue(socketRoutes)) {
|
|
283
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
284
|
+
const io = this._fastify.io;
|
|
285
|
+
for (const socketRoute of socketRoutes) {
|
|
286
|
+
const path = core.StringHelper.trimLeadingSlashes(core.StringHelper.trimTrailingSlashes(socketRoute.path));
|
|
287
|
+
const pathParts = path.split("/");
|
|
288
|
+
await this._loggingConnector?.log({
|
|
289
|
+
level: "info",
|
|
290
|
+
ts: Date.now(),
|
|
291
|
+
source: this.CLASS_NAME,
|
|
292
|
+
message: `${FastifyWebServer._CLASS_NAME_CAMEL_CASE}.socketRouteAdded`,
|
|
293
|
+
data: { route: path }
|
|
294
|
+
});
|
|
295
|
+
const socketNamespace = io.of(`/${pathParts[0]}`);
|
|
296
|
+
const topic = pathParts.slice(1).join("/");
|
|
297
|
+
socketNamespace.on("connection", async (socket) => {
|
|
298
|
+
const httpServerRequest = {
|
|
299
|
+
method: web.HttpMethod.GET,
|
|
300
|
+
url: socket.handshake.url,
|
|
301
|
+
query: socket.handshake.query,
|
|
302
|
+
headers: socket.handshake.headers
|
|
303
|
+
};
|
|
304
|
+
// Pass the connected information on to any processors
|
|
305
|
+
try {
|
|
306
|
+
const processorState = {
|
|
307
|
+
socketId: socket.id
|
|
308
|
+
};
|
|
309
|
+
for (const socketRouteProcessor of socketRouteProcessors) {
|
|
310
|
+
if (core.Is.function(socketRouteProcessor.connected)) {
|
|
311
|
+
await socketRouteProcessor.connected(httpServerRequest, socketRoute, processorState);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
catch (err) {
|
|
316
|
+
const { error, httpStatusCode } = apiModels.HttpErrorHelper.processError(err, this._includeErrorStack);
|
|
317
|
+
const response = {};
|
|
318
|
+
apiModels.HttpErrorHelper.buildResponse(response, error, httpStatusCode);
|
|
319
|
+
socket.emit(topic, response);
|
|
320
|
+
}
|
|
321
|
+
socket.on("disconnect", async () => {
|
|
322
|
+
try {
|
|
323
|
+
const processorState = {
|
|
324
|
+
socketId: socket.id
|
|
325
|
+
};
|
|
326
|
+
// The socket disconnected so notify any processors
|
|
327
|
+
for (const socketRouteProcessor of socketRouteProcessors) {
|
|
328
|
+
if (core.Is.function(socketRouteProcessor.disconnected)) {
|
|
329
|
+
await socketRouteProcessor.disconnected(httpServerRequest, socketRoute, processorState);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
catch {
|
|
334
|
+
// If something fails on a disconnect there is not much we can do with it
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
// Handle any incoming messages
|
|
338
|
+
socket.on(topic, async (data) => {
|
|
339
|
+
await this.handleRequestSocket(socketRouteProcessors, socketRoute, socket, `/${pathParts.join("/")}`, topic, data);
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Handle the incoming REST request.
|
|
227
347
|
* @param restRouteProcessors The hooks to process the incoming requests.
|
|
228
348
|
* @param request The incoming request.
|
|
229
349
|
* @param reply The outgoing response.
|
|
230
350
|
* @param restRoute The REST route to handle.
|
|
231
351
|
* @internal
|
|
232
352
|
*/
|
|
233
|
-
async
|
|
353
|
+
async handleRequestRest(restRouteProcessors, request, reply, restRoute) {
|
|
234
354
|
const httpServerRequest = {
|
|
235
355
|
method: request.method.toUpperCase(),
|
|
236
356
|
url: `${request.protocol}://${request.hostname}${request.url}`,
|
|
@@ -242,21 +362,7 @@ class FastifyWebServer {
|
|
|
242
362
|
const httpResponse = {};
|
|
243
363
|
const httpRequestIdentity = {};
|
|
244
364
|
const processorState = {};
|
|
245
|
-
|
|
246
|
-
if (core.Is.function(restRouteProcessor.pre)) {
|
|
247
|
-
await restRouteProcessor.pre(httpServerRequest, httpResponse, restRoute, httpRequestIdentity, processorState);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
for (const restRouteProcessor of restRouteProcessors) {
|
|
251
|
-
if (core.Is.function(restRouteProcessor.process)) {
|
|
252
|
-
await restRouteProcessor.process(httpServerRequest, httpResponse, restRoute, httpRequestIdentity, processorState);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
for (const restRouteProcessor of restRouteProcessors) {
|
|
256
|
-
if (core.Is.function(restRouteProcessor.post)) {
|
|
257
|
-
await restRouteProcessor.post(httpServerRequest, httpResponse, restRoute, httpRequestIdentity, processorState);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
365
|
+
await this.runProcessorsRest(restRouteProcessors, restRoute, httpServerRequest, httpResponse, httpRequestIdentity, processorState);
|
|
260
366
|
if (!core.Is.empty(httpResponse.headers)) {
|
|
261
367
|
for (const header of Object.keys(httpResponse.headers)) {
|
|
262
368
|
reply.header(header, httpResponse.headers[header]);
|
|
@@ -266,6 +372,119 @@ class FastifyWebServer {
|
|
|
266
372
|
.status((httpResponse.statusCode ?? web.HttpStatusCode.ok))
|
|
267
373
|
.send(httpResponse.body);
|
|
268
374
|
}
|
|
375
|
+
/**
|
|
376
|
+
* Run the REST processors for the route.
|
|
377
|
+
* @param restRouteProcessors The processors to run.
|
|
378
|
+
* @param restRoute The route to process.
|
|
379
|
+
* @param httpServerRequest The incoming request.
|
|
380
|
+
* @param httpResponse The outgoing response.
|
|
381
|
+
* @param httpRequestIdentity The identity context for the request.
|
|
382
|
+
* @internal
|
|
383
|
+
*/
|
|
384
|
+
async runProcessorsRest(restRouteProcessors, restRoute, httpServerRequest, httpResponse, httpRequestIdentity, processorState) {
|
|
385
|
+
try {
|
|
386
|
+
for (const routeProcessor of restRouteProcessors) {
|
|
387
|
+
if (core.Is.function(routeProcessor.pre)) {
|
|
388
|
+
await routeProcessor.pre(httpServerRequest, httpResponse, restRoute, httpRequestIdentity, processorState);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
for (const routeProcessor of restRouteProcessors) {
|
|
392
|
+
if (core.Is.function(routeProcessor.process)) {
|
|
393
|
+
await routeProcessor.process(httpServerRequest, httpResponse, restRoute, httpRequestIdentity, processorState);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
for (const routeProcessor of restRouteProcessors) {
|
|
397
|
+
if (core.Is.function(routeProcessor.post)) {
|
|
398
|
+
await routeProcessor.post(httpServerRequest, httpResponse, restRoute, httpRequestIdentity, processorState);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
catch (err) {
|
|
403
|
+
const { error, httpStatusCode } = apiModels.HttpErrorHelper.processError(err, this._includeErrorStack);
|
|
404
|
+
apiModels.HttpErrorHelper.buildResponse(httpResponse, error, httpStatusCode);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Handle the incoming socket request.
|
|
409
|
+
* @param socketRouteProcessors The hooks to process the incoming requests.
|
|
410
|
+
* @param socketRoute The socket route to handle.
|
|
411
|
+
* @param socket The socket to handle.
|
|
412
|
+
* @param fullPath The full path of the socket route.
|
|
413
|
+
* @param emitTopic The topic to emit the response on.
|
|
414
|
+
* @param data The incoming data.
|
|
415
|
+
* @internal
|
|
416
|
+
*/
|
|
417
|
+
async handleRequestSocket(socketRouteProcessors, socketRoute, socket, fullPath, emitTopic, data) {
|
|
418
|
+
const httpServerRequest = {
|
|
419
|
+
method: web.HttpMethod.GET,
|
|
420
|
+
url: fullPath,
|
|
421
|
+
query: socket.handshake.query,
|
|
422
|
+
headers: socket.handshake.headers
|
|
423
|
+
};
|
|
424
|
+
const httpResponse = {};
|
|
425
|
+
const httpRequestIdentity = {};
|
|
426
|
+
const processorState = {
|
|
427
|
+
socketId: socket.id
|
|
428
|
+
};
|
|
429
|
+
delete httpServerRequest.query?.EIO;
|
|
430
|
+
delete httpServerRequest.query?.transport;
|
|
431
|
+
httpServerRequest.body = data;
|
|
432
|
+
await this.runProcessorsSocket(socketRouteProcessors, socketRoute, httpServerRequest, httpResponse, httpRequestIdentity, processorState, async () => {
|
|
433
|
+
await socket.emit(emitTopic, httpResponse);
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Run the socket processors for the route.
|
|
438
|
+
* @param socketRouteProcessors The processors to run.
|
|
439
|
+
* @param socketRoute The route to process.
|
|
440
|
+
* @param httpServerRequest The incoming request.
|
|
441
|
+
* @param httpResponse The outgoing response.
|
|
442
|
+
* @param httpRequestIdentity The identity context for the request.
|
|
443
|
+
* @internal
|
|
444
|
+
*/
|
|
445
|
+
async runProcessorsSocket(socketRouteProcessors, socketRoute, httpServerRequest, httpResponse, httpRequestIdentity, processorState, responseEmitter) {
|
|
446
|
+
// Custom emit method which will also call the post processors
|
|
447
|
+
const postProcessEmit = async (response, responseProcessorState) => {
|
|
448
|
+
await responseEmitter(response);
|
|
449
|
+
// The post processors are called after the response has been emitted
|
|
450
|
+
for (const postSocketRouteProcessor of socketRouteProcessors) {
|
|
451
|
+
if (core.Is.function(postSocketRouteProcessor.post)) {
|
|
452
|
+
await postSocketRouteProcessor.post(httpServerRequest, response, socketRoute, httpRequestIdentity, responseProcessorState);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
try {
|
|
457
|
+
for (const socketRouteProcessor of socketRouteProcessors) {
|
|
458
|
+
if (core.Is.function(socketRouteProcessor.pre)) {
|
|
459
|
+
await socketRouteProcessor.pre(httpServerRequest, httpResponse, socketRoute, httpRequestIdentity, processorState);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
// We always call all the processors regardless of any response set by a previous processor.
|
|
463
|
+
// But if a pre processor sets a status code, we will emit the response manually, as the pre
|
|
464
|
+
// and post processors do not receive the emit method, they just populate the response object.
|
|
465
|
+
if (!core.Is.empty(httpResponse.statusCode)) {
|
|
466
|
+
await postProcessEmit(httpResponse, processorState);
|
|
467
|
+
}
|
|
468
|
+
for (const socketRouteProcessor of socketRouteProcessors) {
|
|
469
|
+
if (core.Is.function(socketRouteProcessor.process)) {
|
|
470
|
+
await socketRouteProcessor.process(httpServerRequest, httpResponse, socketRoute, httpRequestIdentity, processorState, async (processResponse) => {
|
|
471
|
+
await postProcessEmit(processResponse, processorState);
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
// If the processors set the status to any kind of error then we should emit this manually
|
|
476
|
+
if (core.Is.integer(httpResponse.statusCode) &&
|
|
477
|
+
httpResponse.statusCode >= web.HttpStatusCode.badRequest) {
|
|
478
|
+
await postProcessEmit(httpResponse, processorState);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
catch (err) {
|
|
482
|
+
// Emit any unhandled errors manually
|
|
483
|
+
const { error, httpStatusCode } = apiModels.HttpErrorHelper.processError(err, this._includeErrorStack);
|
|
484
|
+
apiModels.HttpErrorHelper.buildResponse(httpResponse, error, httpStatusCode);
|
|
485
|
+
await postProcessEmit(httpResponse, processorState);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
269
488
|
/**
|
|
270
489
|
* Initialize the cors options.
|
|
271
490
|
* @param options The web server options.
|
package/dist/esm/index.mjs
CHANGED
|
@@ -5,6 +5,19 @@ import { StringHelper, Is, GeneralError, BaseError } from '@twin.org/core';
|
|
|
5
5
|
import { LoggingConnectorFactory } from '@twin.org/logging-models';
|
|
6
6
|
import { HttpStatusCode, HttpMethod, HeaderTypes } from '@twin.org/web';
|
|
7
7
|
import Fastify from 'fastify';
|
|
8
|
+
import fp from 'fastify-plugin';
|
|
9
|
+
import { Server } from 'socket.io';
|
|
10
|
+
|
|
11
|
+
// This is a clone of fastify-socket.io which runs with recent fastify versions.
|
|
12
|
+
const fastifySocketIO = fp(async (fastify, opts) => {
|
|
13
|
+
const ioServer = new Server(fastify.server, opts);
|
|
14
|
+
fastify.decorate("io", ioServer);
|
|
15
|
+
fastify.addHook("preClose", done => {
|
|
16
|
+
ioServer.disconnectSockets();
|
|
17
|
+
ioServer.close();
|
|
18
|
+
done();
|
|
19
|
+
});
|
|
20
|
+
}, { fastify: ">=5.x.x", name: "socket.io" });
|
|
8
21
|
|
|
9
22
|
// Copyright 2024 IOTA Stiftung.
|
|
10
23
|
// SPDX-License-Identifier: Apache-2.0.
|
|
@@ -46,6 +59,11 @@ class FastifyWebServer {
|
|
|
46
59
|
* @internal
|
|
47
60
|
*/
|
|
48
61
|
_fastify;
|
|
62
|
+
/**
|
|
63
|
+
* The options for the socket server.
|
|
64
|
+
* @internal
|
|
65
|
+
*/
|
|
66
|
+
_socketConfig;
|
|
49
67
|
/**
|
|
50
68
|
* Whether the server has been started.
|
|
51
69
|
* @internal
|
|
@@ -56,20 +74,33 @@ class FastifyWebServer {
|
|
|
56
74
|
* @internal
|
|
57
75
|
*/
|
|
58
76
|
_mimeTypeProcessors;
|
|
77
|
+
/**
|
|
78
|
+
* Include the stack with errors.
|
|
79
|
+
* @internal
|
|
80
|
+
*/
|
|
81
|
+
_includeErrorStack;
|
|
59
82
|
/**
|
|
60
83
|
* Create a new instance of FastifyWebServer.
|
|
61
84
|
* @param options The options for the server.
|
|
62
85
|
* @param options.loggingConnectorType The type of the logging connector to use, if undefined, no logging will happen.
|
|
63
|
-
* @param options.config Additional options for the Fastify server.
|
|
64
86
|
* @param options.mimeTypeProcessors Additional MIME type processors.
|
|
87
|
+
* @param options.config Additional configuration for the server.
|
|
65
88
|
*/
|
|
66
89
|
constructor(options) {
|
|
67
90
|
this._loggingConnector = Is.stringValue(options?.loggingConnectorType)
|
|
68
91
|
? LoggingConnectorFactory.get(options.loggingConnectorType)
|
|
69
92
|
: undefined;
|
|
70
|
-
this._fastify = Fastify({
|
|
93
|
+
this._fastify = Fastify({
|
|
94
|
+
maxParamLength: 2000,
|
|
95
|
+
...options?.config?.web
|
|
96
|
+
});
|
|
97
|
+
this._socketConfig = {
|
|
98
|
+
path: "/socket",
|
|
99
|
+
...options?.config?.socket
|
|
100
|
+
};
|
|
71
101
|
this._started = false;
|
|
72
102
|
this._mimeTypeProcessors = options?.mimeTypeProcessors ?? [];
|
|
103
|
+
this._includeErrorStack = options?.config?.includeErrorStack ?? false;
|
|
73
104
|
}
|
|
74
105
|
/**
|
|
75
106
|
* Get the web server instance.
|
|
@@ -80,14 +111,19 @@ class FastifyWebServer {
|
|
|
80
111
|
}
|
|
81
112
|
/**
|
|
82
113
|
* Build the server.
|
|
83
|
-
* @param restRouteProcessors The
|
|
114
|
+
* @param restRouteProcessors The processors for incoming requests over REST.
|
|
84
115
|
* @param restRoutes The REST routes.
|
|
116
|
+
* @param socketRouteProcessors The processors for incoming requests over Sockets.
|
|
117
|
+
* @param socketRoutes The socket routes.
|
|
85
118
|
* @param options Options for building the server.
|
|
86
119
|
* @returns Nothing.
|
|
87
120
|
*/
|
|
88
|
-
async build(restRouteProcessors, restRoutes, options) {
|
|
89
|
-
if (!Is.arrayValue(restRouteProcessors)) {
|
|
90
|
-
throw new GeneralError(this.CLASS_NAME, "
|
|
121
|
+
async build(restRouteProcessors, restRoutes, socketRouteProcessors, socketRoutes, options) {
|
|
122
|
+
if (Is.arrayValue(restRoutes) && !Is.arrayValue(restRouteProcessors)) {
|
|
123
|
+
throw new GeneralError(this.CLASS_NAME, "noRestProcessors");
|
|
124
|
+
}
|
|
125
|
+
if (Is.arrayValue(socketRoutes) && !Is.arrayValue(socketRouteProcessors)) {
|
|
126
|
+
throw new GeneralError(this.CLASS_NAME, "noSocketProcessors");
|
|
91
127
|
}
|
|
92
128
|
await this._loggingConnector?.log({
|
|
93
129
|
level: "info",
|
|
@@ -97,6 +133,9 @@ class FastifyWebServer {
|
|
|
97
133
|
});
|
|
98
134
|
this._options = options;
|
|
99
135
|
await this._fastify.register(FastifyCompress);
|
|
136
|
+
if (Is.arrayValue(socketRoutes)) {
|
|
137
|
+
await this._fastify.register(fastifySocketIO, this._socketConfig);
|
|
138
|
+
}
|
|
100
139
|
if (Is.arrayValue(this._mimeTypeProcessors)) {
|
|
101
140
|
for (const contentTypeHandler of this._mimeTypeProcessors) {
|
|
102
141
|
this._fastify.addContentTypeParser(contentTypeHandler.getTypes(), { parseAs: "buffer" }, async (request, body, done) => {
|
|
@@ -111,7 +150,7 @@ class FastifyWebServer {
|
|
|
111
150
|
}
|
|
112
151
|
}
|
|
113
152
|
await this.initCors(options);
|
|
114
|
-
this._fastify.setNotFoundHandler({}, async (request, reply) => this.
|
|
153
|
+
this._fastify.setNotFoundHandler({}, async (request, reply) => this.handleRequestRest(restRouteProcessors ?? [], request, reply));
|
|
115
154
|
this._fastify.setErrorHandler(async (error, request, reply) => {
|
|
116
155
|
// If code property is set this is a fastify error
|
|
117
156
|
// otherwise it's from our framework
|
|
@@ -141,22 +180,8 @@ class FastifyWebServer {
|
|
|
141
180
|
error: err
|
|
142
181
|
});
|
|
143
182
|
});
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
let path = StringHelper.trimTrailingSlashes(restRoute.path);
|
|
147
|
-
if (!path.startsWith("/")) {
|
|
148
|
-
path = `/${path}`;
|
|
149
|
-
}
|
|
150
|
-
await this._loggingConnector?.log({
|
|
151
|
-
level: "info",
|
|
152
|
-
ts: Date.now(),
|
|
153
|
-
source: this.CLASS_NAME,
|
|
154
|
-
message: `${FastifyWebServer._CLASS_NAME_CAMEL_CASE}.restRouteAdded`,
|
|
155
|
-
data: { route: path, method: restRoute.method }
|
|
156
|
-
});
|
|
157
|
-
const method = restRoute.method.toLowerCase();
|
|
158
|
-
this._fastify[method](path, async (request, reply) => this.handleRequest(restRouteProcessors, request, reply, restRoute));
|
|
159
|
-
}
|
|
183
|
+
await this.addRoutesRest(restRouteProcessors, restRoutes);
|
|
184
|
+
await this.addRoutesSocket(socketRouteProcessors, socketRoutes);
|
|
160
185
|
}
|
|
161
186
|
/**
|
|
162
187
|
* Start the server.
|
|
@@ -221,14 +246,109 @@ class FastifyWebServer {
|
|
|
221
246
|
}
|
|
222
247
|
}
|
|
223
248
|
/**
|
|
224
|
-
*
|
|
249
|
+
* Add the REST routes to the server.
|
|
250
|
+
* @param restRouteProcessors The processors for the incoming requests.
|
|
251
|
+
* @param restRoutes The REST routes to add.
|
|
252
|
+
* @internal
|
|
253
|
+
*/
|
|
254
|
+
async addRoutesRest(restRouteProcessors, restRoutes) {
|
|
255
|
+
if (Is.arrayValue(restRouteProcessors) && Is.arrayValue(restRoutes)) {
|
|
256
|
+
for (const restRoute of restRoutes) {
|
|
257
|
+
let path = StringHelper.trimTrailingSlashes(restRoute.path);
|
|
258
|
+
if (!path.startsWith("/")) {
|
|
259
|
+
path = `/${path}`;
|
|
260
|
+
}
|
|
261
|
+
await this._loggingConnector?.log({
|
|
262
|
+
level: "info",
|
|
263
|
+
ts: Date.now(),
|
|
264
|
+
source: this.CLASS_NAME,
|
|
265
|
+
message: `${FastifyWebServer._CLASS_NAME_CAMEL_CASE}.restRouteAdded`,
|
|
266
|
+
data: { route: path, method: restRoute.method }
|
|
267
|
+
});
|
|
268
|
+
const method = restRoute.method.toLowerCase();
|
|
269
|
+
this._fastify[method](path, async (request, reply) => this.handleRequestRest(restRouteProcessors, request, reply, restRoute));
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Add the socket routes to the server.
|
|
275
|
+
* @param socketRouteProcessors The processors for the incoming requests.
|
|
276
|
+
* @param socketRoutes The socket routes to add.
|
|
277
|
+
* @internal
|
|
278
|
+
*/
|
|
279
|
+
async addRoutesSocket(socketRouteProcessors, socketRoutes) {
|
|
280
|
+
if (Is.arrayValue(socketRouteProcessors) && Is.arrayValue(socketRoutes)) {
|
|
281
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
282
|
+
const io = this._fastify.io;
|
|
283
|
+
for (const socketRoute of socketRoutes) {
|
|
284
|
+
const path = StringHelper.trimLeadingSlashes(StringHelper.trimTrailingSlashes(socketRoute.path));
|
|
285
|
+
const pathParts = path.split("/");
|
|
286
|
+
await this._loggingConnector?.log({
|
|
287
|
+
level: "info",
|
|
288
|
+
ts: Date.now(),
|
|
289
|
+
source: this.CLASS_NAME,
|
|
290
|
+
message: `${FastifyWebServer._CLASS_NAME_CAMEL_CASE}.socketRouteAdded`,
|
|
291
|
+
data: { route: path }
|
|
292
|
+
});
|
|
293
|
+
const socketNamespace = io.of(`/${pathParts[0]}`);
|
|
294
|
+
const topic = pathParts.slice(1).join("/");
|
|
295
|
+
socketNamespace.on("connection", async (socket) => {
|
|
296
|
+
const httpServerRequest = {
|
|
297
|
+
method: HttpMethod.GET,
|
|
298
|
+
url: socket.handshake.url,
|
|
299
|
+
query: socket.handshake.query,
|
|
300
|
+
headers: socket.handshake.headers
|
|
301
|
+
};
|
|
302
|
+
// Pass the connected information on to any processors
|
|
303
|
+
try {
|
|
304
|
+
const processorState = {
|
|
305
|
+
socketId: socket.id
|
|
306
|
+
};
|
|
307
|
+
for (const socketRouteProcessor of socketRouteProcessors) {
|
|
308
|
+
if (Is.function(socketRouteProcessor.connected)) {
|
|
309
|
+
await socketRouteProcessor.connected(httpServerRequest, socketRoute, processorState);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
catch (err) {
|
|
314
|
+
const { error, httpStatusCode } = HttpErrorHelper.processError(err, this._includeErrorStack);
|
|
315
|
+
const response = {};
|
|
316
|
+
HttpErrorHelper.buildResponse(response, error, httpStatusCode);
|
|
317
|
+
socket.emit(topic, response);
|
|
318
|
+
}
|
|
319
|
+
socket.on("disconnect", async () => {
|
|
320
|
+
try {
|
|
321
|
+
const processorState = {
|
|
322
|
+
socketId: socket.id
|
|
323
|
+
};
|
|
324
|
+
// The socket disconnected so notify any processors
|
|
325
|
+
for (const socketRouteProcessor of socketRouteProcessors) {
|
|
326
|
+
if (Is.function(socketRouteProcessor.disconnected)) {
|
|
327
|
+
await socketRouteProcessor.disconnected(httpServerRequest, socketRoute, processorState);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
catch {
|
|
332
|
+
// If something fails on a disconnect there is not much we can do with it
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
// Handle any incoming messages
|
|
336
|
+
socket.on(topic, async (data) => {
|
|
337
|
+
await this.handleRequestSocket(socketRouteProcessors, socketRoute, socket, `/${pathParts.join("/")}`, topic, data);
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Handle the incoming REST request.
|
|
225
345
|
* @param restRouteProcessors The hooks to process the incoming requests.
|
|
226
346
|
* @param request The incoming request.
|
|
227
347
|
* @param reply The outgoing response.
|
|
228
348
|
* @param restRoute The REST route to handle.
|
|
229
349
|
* @internal
|
|
230
350
|
*/
|
|
231
|
-
async
|
|
351
|
+
async handleRequestRest(restRouteProcessors, request, reply, restRoute) {
|
|
232
352
|
const httpServerRequest = {
|
|
233
353
|
method: request.method.toUpperCase(),
|
|
234
354
|
url: `${request.protocol}://${request.hostname}${request.url}`,
|
|
@@ -240,21 +360,7 @@ class FastifyWebServer {
|
|
|
240
360
|
const httpResponse = {};
|
|
241
361
|
const httpRequestIdentity = {};
|
|
242
362
|
const processorState = {};
|
|
243
|
-
|
|
244
|
-
if (Is.function(restRouteProcessor.pre)) {
|
|
245
|
-
await restRouteProcessor.pre(httpServerRequest, httpResponse, restRoute, httpRequestIdentity, processorState);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
for (const restRouteProcessor of restRouteProcessors) {
|
|
249
|
-
if (Is.function(restRouteProcessor.process)) {
|
|
250
|
-
await restRouteProcessor.process(httpServerRequest, httpResponse, restRoute, httpRequestIdentity, processorState);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
for (const restRouteProcessor of restRouteProcessors) {
|
|
254
|
-
if (Is.function(restRouteProcessor.post)) {
|
|
255
|
-
await restRouteProcessor.post(httpServerRequest, httpResponse, restRoute, httpRequestIdentity, processorState);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
363
|
+
await this.runProcessorsRest(restRouteProcessors, restRoute, httpServerRequest, httpResponse, httpRequestIdentity, processorState);
|
|
258
364
|
if (!Is.empty(httpResponse.headers)) {
|
|
259
365
|
for (const header of Object.keys(httpResponse.headers)) {
|
|
260
366
|
reply.header(header, httpResponse.headers[header]);
|
|
@@ -264,6 +370,119 @@ class FastifyWebServer {
|
|
|
264
370
|
.status((httpResponse.statusCode ?? HttpStatusCode.ok))
|
|
265
371
|
.send(httpResponse.body);
|
|
266
372
|
}
|
|
373
|
+
/**
|
|
374
|
+
* Run the REST processors for the route.
|
|
375
|
+
* @param restRouteProcessors The processors to run.
|
|
376
|
+
* @param restRoute The route to process.
|
|
377
|
+
* @param httpServerRequest The incoming request.
|
|
378
|
+
* @param httpResponse The outgoing response.
|
|
379
|
+
* @param httpRequestIdentity The identity context for the request.
|
|
380
|
+
* @internal
|
|
381
|
+
*/
|
|
382
|
+
async runProcessorsRest(restRouteProcessors, restRoute, httpServerRequest, httpResponse, httpRequestIdentity, processorState) {
|
|
383
|
+
try {
|
|
384
|
+
for (const routeProcessor of restRouteProcessors) {
|
|
385
|
+
if (Is.function(routeProcessor.pre)) {
|
|
386
|
+
await routeProcessor.pre(httpServerRequest, httpResponse, restRoute, httpRequestIdentity, processorState);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
for (const routeProcessor of restRouteProcessors) {
|
|
390
|
+
if (Is.function(routeProcessor.process)) {
|
|
391
|
+
await routeProcessor.process(httpServerRequest, httpResponse, restRoute, httpRequestIdentity, processorState);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
for (const routeProcessor of restRouteProcessors) {
|
|
395
|
+
if (Is.function(routeProcessor.post)) {
|
|
396
|
+
await routeProcessor.post(httpServerRequest, httpResponse, restRoute, httpRequestIdentity, processorState);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
catch (err) {
|
|
401
|
+
const { error, httpStatusCode } = HttpErrorHelper.processError(err, this._includeErrorStack);
|
|
402
|
+
HttpErrorHelper.buildResponse(httpResponse, error, httpStatusCode);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Handle the incoming socket request.
|
|
407
|
+
* @param socketRouteProcessors The hooks to process the incoming requests.
|
|
408
|
+
* @param socketRoute The socket route to handle.
|
|
409
|
+
* @param socket The socket to handle.
|
|
410
|
+
* @param fullPath The full path of the socket route.
|
|
411
|
+
* @param emitTopic The topic to emit the response on.
|
|
412
|
+
* @param data The incoming data.
|
|
413
|
+
* @internal
|
|
414
|
+
*/
|
|
415
|
+
async handleRequestSocket(socketRouteProcessors, socketRoute, socket, fullPath, emitTopic, data) {
|
|
416
|
+
const httpServerRequest = {
|
|
417
|
+
method: HttpMethod.GET,
|
|
418
|
+
url: fullPath,
|
|
419
|
+
query: socket.handshake.query,
|
|
420
|
+
headers: socket.handshake.headers
|
|
421
|
+
};
|
|
422
|
+
const httpResponse = {};
|
|
423
|
+
const httpRequestIdentity = {};
|
|
424
|
+
const processorState = {
|
|
425
|
+
socketId: socket.id
|
|
426
|
+
};
|
|
427
|
+
delete httpServerRequest.query?.EIO;
|
|
428
|
+
delete httpServerRequest.query?.transport;
|
|
429
|
+
httpServerRequest.body = data;
|
|
430
|
+
await this.runProcessorsSocket(socketRouteProcessors, socketRoute, httpServerRequest, httpResponse, httpRequestIdentity, processorState, async () => {
|
|
431
|
+
await socket.emit(emitTopic, httpResponse);
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Run the socket processors for the route.
|
|
436
|
+
* @param socketRouteProcessors The processors to run.
|
|
437
|
+
* @param socketRoute The route to process.
|
|
438
|
+
* @param httpServerRequest The incoming request.
|
|
439
|
+
* @param httpResponse The outgoing response.
|
|
440
|
+
* @param httpRequestIdentity The identity context for the request.
|
|
441
|
+
* @internal
|
|
442
|
+
*/
|
|
443
|
+
async runProcessorsSocket(socketRouteProcessors, socketRoute, httpServerRequest, httpResponse, httpRequestIdentity, processorState, responseEmitter) {
|
|
444
|
+
// Custom emit method which will also call the post processors
|
|
445
|
+
const postProcessEmit = async (response, responseProcessorState) => {
|
|
446
|
+
await responseEmitter(response);
|
|
447
|
+
// The post processors are called after the response has been emitted
|
|
448
|
+
for (const postSocketRouteProcessor of socketRouteProcessors) {
|
|
449
|
+
if (Is.function(postSocketRouteProcessor.post)) {
|
|
450
|
+
await postSocketRouteProcessor.post(httpServerRequest, response, socketRoute, httpRequestIdentity, responseProcessorState);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
try {
|
|
455
|
+
for (const socketRouteProcessor of socketRouteProcessors) {
|
|
456
|
+
if (Is.function(socketRouteProcessor.pre)) {
|
|
457
|
+
await socketRouteProcessor.pre(httpServerRequest, httpResponse, socketRoute, httpRequestIdentity, processorState);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
// We always call all the processors regardless of any response set by a previous processor.
|
|
461
|
+
// But if a pre processor sets a status code, we will emit the response manually, as the pre
|
|
462
|
+
// and post processors do not receive the emit method, they just populate the response object.
|
|
463
|
+
if (!Is.empty(httpResponse.statusCode)) {
|
|
464
|
+
await postProcessEmit(httpResponse, processorState);
|
|
465
|
+
}
|
|
466
|
+
for (const socketRouteProcessor of socketRouteProcessors) {
|
|
467
|
+
if (Is.function(socketRouteProcessor.process)) {
|
|
468
|
+
await socketRouteProcessor.process(httpServerRequest, httpResponse, socketRoute, httpRequestIdentity, processorState, async (processResponse) => {
|
|
469
|
+
await postProcessEmit(processResponse, processorState);
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
// If the processors set the status to any kind of error then we should emit this manually
|
|
474
|
+
if (Is.integer(httpResponse.statusCode) &&
|
|
475
|
+
httpResponse.statusCode >= HttpStatusCode.badRequest) {
|
|
476
|
+
await postProcessEmit(httpResponse, processorState);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
catch (err) {
|
|
480
|
+
// Emit any unhandled errors manually
|
|
481
|
+
const { error, httpStatusCode } = HttpErrorHelper.processError(err, this._includeErrorStack);
|
|
482
|
+
HttpErrorHelper.buildResponse(httpResponse, error, httpStatusCode);
|
|
483
|
+
await postProcessEmit(httpResponse, processorState);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
267
486
|
/**
|
|
268
487
|
* Initialize the cors options.
|
|
269
488
|
* @param options The web server options.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
import { type FastifyInstance
|
|
1
|
+
import { type IMimeTypeProcessor, type IRestRoute, type IRestRouteProcessor, type ISocketRoute, type ISocketRouteProcessor, type IWebServer, type IWebServerOptions } from "@twin.org/api-models";
|
|
2
|
+
import { type FastifyInstance } from "fastify";
|
|
3
|
+
import type { IFastifyWebServerConfig } from "./models/IFastifyWebServerConfig";
|
|
3
4
|
/**
|
|
4
5
|
* Implementation of the web server using Fastify.
|
|
5
6
|
*/
|
|
@@ -12,12 +13,12 @@ export declare class FastifyWebServer implements IWebServer<FastifyInstance> {
|
|
|
12
13
|
* Create a new instance of FastifyWebServer.
|
|
13
14
|
* @param options The options for the server.
|
|
14
15
|
* @param options.loggingConnectorType The type of the logging connector to use, if undefined, no logging will happen.
|
|
15
|
-
* @param options.config Additional options for the Fastify server.
|
|
16
16
|
* @param options.mimeTypeProcessors Additional MIME type processors.
|
|
17
|
+
* @param options.config Additional configuration for the server.
|
|
17
18
|
*/
|
|
18
19
|
constructor(options?: {
|
|
19
20
|
loggingConnectorType?: string;
|
|
20
|
-
config?:
|
|
21
|
+
config?: IFastifyWebServerConfig;
|
|
21
22
|
mimeTypeProcessors?: IMimeTypeProcessor[];
|
|
22
23
|
});
|
|
23
24
|
/**
|
|
@@ -27,12 +28,14 @@ export declare class FastifyWebServer implements IWebServer<FastifyInstance> {
|
|
|
27
28
|
getInstance(): FastifyInstance;
|
|
28
29
|
/**
|
|
29
30
|
* Build the server.
|
|
30
|
-
* @param restRouteProcessors The
|
|
31
|
+
* @param restRouteProcessors The processors for incoming requests over REST.
|
|
31
32
|
* @param restRoutes The REST routes.
|
|
33
|
+
* @param socketRouteProcessors The processors for incoming requests over Sockets.
|
|
34
|
+
* @param socketRoutes The socket routes.
|
|
32
35
|
* @param options Options for building the server.
|
|
33
36
|
* @returns Nothing.
|
|
34
37
|
*/
|
|
35
|
-
build(restRouteProcessors
|
|
38
|
+
build(restRouteProcessors?: IRestRouteProcessor[], restRoutes?: IRestRoute[], socketRouteProcessors?: ISocketRouteProcessor[], socketRoutes?: ISocketRoute[], options?: IWebServerOptions): Promise<void>;
|
|
36
39
|
/**
|
|
37
40
|
* Start the server.
|
|
38
41
|
* @returns Nothing.
|
package/dist/types/index.d.ts
CHANGED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { FastifyServerOptions } from "fastify";
|
|
2
|
+
import type { ServerOptions } from "socket.io";
|
|
3
|
+
/**
|
|
4
|
+
* The configuration for the Fastify web server.
|
|
5
|
+
*/
|
|
6
|
+
export interface IFastifyWebServerConfig {
|
|
7
|
+
/**
|
|
8
|
+
* The web server options.
|
|
9
|
+
*/
|
|
10
|
+
web?: Partial<FastifyServerOptions>;
|
|
11
|
+
/**
|
|
12
|
+
* The socket server options.
|
|
13
|
+
*/
|
|
14
|
+
socket?: Partial<ServerOptions>;
|
|
15
|
+
/**
|
|
16
|
+
* Include the stack with errors.
|
|
17
|
+
*/
|
|
18
|
+
includeErrorStack?: boolean;
|
|
19
|
+
}
|
package/docs/changelog.md
CHANGED
|
@@ -24,9 +24,9 @@ The options for the server.
|
|
|
24
24
|
|
|
25
25
|
The type of the logging connector to use, if undefined, no logging will happen.
|
|
26
26
|
|
|
27
|
-
• **options.config?**: `
|
|
27
|
+
• **options.config?**: [`IFastifyWebServerConfig`](../interfaces/IFastifyWebServerConfig.md)
|
|
28
28
|
|
|
29
|
-
Additional
|
|
29
|
+
Additional configuration for the server.
|
|
30
30
|
|
|
31
31
|
• **options.mimeTypeProcessors?**: `IMimeTypeProcessor`[]
|
|
32
32
|
|
|
@@ -66,20 +66,28 @@ The web server instance.
|
|
|
66
66
|
|
|
67
67
|
### build()
|
|
68
68
|
|
|
69
|
-
> **build**(`restRouteProcessors
|
|
69
|
+
> **build**(`restRouteProcessors`?, `restRoutes`?, `socketRouteProcessors`?, `socketRoutes`?, `options`?): `Promise`\<`void`\>
|
|
70
70
|
|
|
71
71
|
Build the server.
|
|
72
72
|
|
|
73
73
|
#### Parameters
|
|
74
74
|
|
|
75
|
-
• **restRouteProcessors
|
|
75
|
+
• **restRouteProcessors?**: `IRestRouteProcessor`[]
|
|
76
76
|
|
|
77
|
-
The
|
|
77
|
+
The processors for incoming requests over REST.
|
|
78
78
|
|
|
79
|
-
• **restRoutes
|
|
79
|
+
• **restRoutes?**: `IRestRoute`\<`any`, `any`\>[]
|
|
80
80
|
|
|
81
81
|
The REST routes.
|
|
82
82
|
|
|
83
|
+
• **socketRouteProcessors?**: `ISocketRouteProcessor`[]
|
|
84
|
+
|
|
85
|
+
The processors for incoming requests over Sockets.
|
|
86
|
+
|
|
87
|
+
• **socketRoutes?**: `ISocketRoute`\<`any`, `any`\>[]
|
|
88
|
+
|
|
89
|
+
The socket routes.
|
|
90
|
+
|
|
83
91
|
• **options?**: `IWebServerOptions`
|
|
84
92
|
|
|
85
93
|
Options for building the server.
|
package/docs/reference/index.md
CHANGED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Interface: IFastifyWebServerConfig
|
|
2
|
+
|
|
3
|
+
The configuration for the Fastify web server.
|
|
4
|
+
|
|
5
|
+
## Properties
|
|
6
|
+
|
|
7
|
+
### web?
|
|
8
|
+
|
|
9
|
+
> `optional` **web**: `Partial`\<`FastifyServerOptions`\>
|
|
10
|
+
|
|
11
|
+
The web server options.
|
|
12
|
+
|
|
13
|
+
***
|
|
14
|
+
|
|
15
|
+
### socket?
|
|
16
|
+
|
|
17
|
+
> `optional` **socket**: `Partial`\<`ServerOptions`\>
|
|
18
|
+
|
|
19
|
+
The socket server options.
|
|
20
|
+
|
|
21
|
+
***
|
|
22
|
+
|
|
23
|
+
### includeErrorStack?
|
|
24
|
+
|
|
25
|
+
> `optional` **includeErrorStack**: `boolean`
|
|
26
|
+
|
|
27
|
+
Include the stack with errors.
|
package/locales/en.json
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
"stopped": "The Web Server was stopped",
|
|
8
8
|
"badRequest": "The web server could not handle the request",
|
|
9
9
|
"restRouteAdded": "Added REST route \"{route}\" \"{method}\"",
|
|
10
|
-
"
|
|
10
|
+
"socketRouteAdded": "Added socket route \"{route}\"",
|
|
11
|
+
"noRestProcessors": "You must configure at least one REST processor",
|
|
12
|
+
"noSocketProcessors": "You must configure at least one socket processor"
|
|
11
13
|
}
|
|
12
14
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twin.org/api-server-fastify",
|
|
3
|
-
"version": "0.0.1-next.
|
|
3
|
+
"version": "0.0.1-next.13",
|
|
4
4
|
"description": "Use Fastify as the core web server for APIs",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -16,13 +16,14 @@
|
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@fastify/compress": "8.0.1",
|
|
18
18
|
"@fastify/cors": "10.0.1",
|
|
19
|
-
"@twin.org/api-core": "0.0.1-next.
|
|
20
|
-
"@twin.org/api-models": "0.0.1-next.
|
|
19
|
+
"@twin.org/api-core": "0.0.1-next.13",
|
|
20
|
+
"@twin.org/api-models": "0.0.1-next.13",
|
|
21
21
|
"@twin.org/core": "next",
|
|
22
22
|
"@twin.org/logging-models": "next",
|
|
23
23
|
"@twin.org/nameof": "next",
|
|
24
24
|
"@twin.org/web": "next",
|
|
25
|
-
"fastify": "5.
|
|
25
|
+
"fastify": "5.1.0",
|
|
26
|
+
"socket.io": "4.8.1"
|
|
26
27
|
},
|
|
27
28
|
"main": "./dist/cjs/index.cjs",
|
|
28
29
|
"module": "./dist/esm/index.mjs",
|