configurapi-runner-ws 1.10.0 → 1.11.0
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/package.json +1 -1
- package/src/app.mjs +3 -1
- package/src/index.js +146 -3
package/package.json
CHANGED
package/src/app.mjs
CHANGED
|
@@ -32,6 +32,7 @@ function logDebug(s)
|
|
|
32
32
|
const commander = new Command();
|
|
33
33
|
commander.option('-p, --port [number]', 'Port number')
|
|
34
34
|
.option('--s-port [number]', 'Secure port number')
|
|
35
|
+
.option('-m, --mport [number]', 'Management port number')
|
|
35
36
|
.option('-f, --file [path]', 'Path to the config file')
|
|
36
37
|
.option('--key [path]', 'Path to the private key file (.key)')
|
|
37
38
|
.option('--cert [path]', 'Path to the certificate file (.pem)')
|
|
@@ -40,6 +41,7 @@ commander.option('-p, --port [number]', 'Port number')
|
|
|
40
41
|
|
|
41
42
|
let port = commander.port ? commander.port : 9090;
|
|
42
43
|
let sPort = commander.sPort ? commander.sPort : 9443;
|
|
44
|
+
let mPort = commander.mPort ? commander.mPort : 9100;
|
|
43
45
|
let configPath = commander.file ? commander.file : 'config.yaml';
|
|
44
46
|
let keepAliveTimeout = commander.keepAlive ? commander.keepAlive : (process.env.CONFIGURAPI_RUNNER_SELF_KEEP_ALIVE || 0);
|
|
45
47
|
let key = commander.key ? fs.readFileSync(commander.key) : undefined;
|
|
@@ -52,4 +54,4 @@ let app = new App();
|
|
|
52
54
|
app.on('error', (s) => {logError(s);});
|
|
53
55
|
app.on('debug', (s) => {logDebug(s);});
|
|
54
56
|
app.on('trace', (s) => {logTrace(s);});
|
|
55
|
-
await app.run({port: port, configPath:configPath, keepAliveTimeout:keepAliveTimeout, key: key, cert: cert, sPort: sPort});
|
|
57
|
+
await app.run({port: port, configPath:configPath, keepAliveTimeout:keepAliveTimeout, key: key, cert: cert, sPort: sPort, mPort});
|
package/src/index.js
CHANGED
|
@@ -6,6 +6,28 @@ const events = require('events');
|
|
|
6
6
|
const Configurapi = require('configurapi');
|
|
7
7
|
const { WebSocketServer } = require('ws');
|
|
8
8
|
const { randomUUID } = require("node:crypto");
|
|
9
|
+
const { Response, Route, Policy, PolicyHandlerLoader, LogLevel } = require('configurapi');
|
|
10
|
+
const oneliner = require('one-liner');
|
|
11
|
+
const wsAdapter = require('./wsAdapter');
|
|
12
|
+
const URL = require('url');
|
|
13
|
+
|
|
14
|
+
function addInternalRoute(self, config, routeName,)
|
|
15
|
+
{
|
|
16
|
+
let route = new Route();
|
|
17
|
+
route.name = routeName;
|
|
18
|
+
|
|
19
|
+
route.on(LogLevel.Trace, (s)=>self.emit(LogLevel.Trace, oneliner(s)));
|
|
20
|
+
route.on(LogLevel.Debug, (s)=>self.emit(LogLevel.Debug, oneliner(s)));
|
|
21
|
+
route.on(LogLevel.Error, (s)=>self.emit(LogLevel.Error, oneliner(s)));
|
|
22
|
+
|
|
23
|
+
let policy = new Policy()
|
|
24
|
+
policy.name = `${routeName}`;
|
|
25
|
+
route.policies = [policy]
|
|
26
|
+
|
|
27
|
+
config.events.set(route.name, route);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const connections = []
|
|
9
31
|
|
|
10
32
|
module.exports = class HttpRunner extends events.EventEmitter
|
|
11
33
|
{
|
|
@@ -15,6 +37,7 @@ module.exports = class HttpRunner extends events.EventEmitter
|
|
|
15
37
|
|
|
16
38
|
this.port = 8000;
|
|
17
39
|
this.sPort = 8443;
|
|
40
|
+
this.mPort = 9100;
|
|
18
41
|
this.configPath = "config.yaml";
|
|
19
42
|
this.service = undefined;
|
|
20
43
|
this.keepAliveTimeout = 0;
|
|
@@ -28,7 +51,35 @@ module.exports = class HttpRunner extends events.EventEmitter
|
|
|
28
51
|
|
|
29
52
|
let config = Configurapi.Config.load(this.configPath);
|
|
30
53
|
|
|
31
|
-
|
|
54
|
+
let loader = new PolicyHandlerLoader()
|
|
55
|
+
loader.handlers.set('list_@connections', (e)=>
|
|
56
|
+
{
|
|
57
|
+
e.response.headers['Content-Type'] = 'application/json';
|
|
58
|
+
e.response.body = Object.keys(connections);
|
|
59
|
+
});
|
|
60
|
+
loader.handlers.set('post_@connection', async (e)=>
|
|
61
|
+
{
|
|
62
|
+
e.response.headers['Content-Type'] = 'application/json';
|
|
63
|
+
|
|
64
|
+
const connectionId = e.params?.['@connection'];
|
|
65
|
+
|
|
66
|
+
if(connectionId && connections[connectionId])
|
|
67
|
+
{
|
|
68
|
+
let wsResponse = new Response(e.payload, 200, {...e.request.headers, 'connection-id': connectionId})
|
|
69
|
+
await wsAdapter.write(connections[connectionId].ws, wsResponse)
|
|
70
|
+
e.response.statusCode = 204
|
|
71
|
+
}
|
|
72
|
+
else
|
|
73
|
+
{
|
|
74
|
+
e.response.statusCode = 404;
|
|
75
|
+
e.response.body = JSON.stringify({ message: `The connection '${connectionId}' does not exist.`, details: ''})
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
addInternalRoute(this, config, 'list_@connections')
|
|
80
|
+
addInternalRoute(this, config, 'post_@connection')
|
|
81
|
+
|
|
82
|
+
this.service = new Configurapi.Service(config, loader);
|
|
32
83
|
this.service.on("trace", (s) => this.emit("trace", s));
|
|
33
84
|
this.service.on("debug", (s) => this.emit("debug", s));
|
|
34
85
|
this.service.on("error", (s) => this.emit("error", s));
|
|
@@ -55,6 +106,14 @@ module.exports = class HttpRunner extends events.EventEmitter
|
|
|
55
106
|
this.emit("trace", "Server is listening..."+this.port);
|
|
56
107
|
}
|
|
57
108
|
|
|
109
|
+
let managementServer = http.createServer({key: this.key, cert: this.cert}, (req, resp) => {
|
|
110
|
+
this._handleManagementRequest(req, resp);
|
|
111
|
+
});
|
|
112
|
+
managementServer.keepAliveTimeout = this.keepAliveTimeout;
|
|
113
|
+
managementServer.listen(this.mPort);
|
|
114
|
+
|
|
115
|
+
this.emit('trace', 'Management server is listening...'+this.mPort);
|
|
116
|
+
|
|
58
117
|
const wss = new WebSocketServer({ server: httpServer, path: "/ws", handleProtocols: (protocols, req) => {
|
|
59
118
|
if(protocols === undefined)
|
|
60
119
|
{
|
|
@@ -77,9 +136,13 @@ module.exports = class HttpRunner extends events.EventEmitter
|
|
|
77
136
|
const connectionId = randomUUID();
|
|
78
137
|
ws.connectionId = connectionId;
|
|
79
138
|
|
|
139
|
+
connections[connectionId] = {
|
|
140
|
+
connectionId,
|
|
141
|
+
ws
|
|
142
|
+
}
|
|
80
143
|
this.emit("trace", `[connect] ${connectionId}`);
|
|
81
144
|
|
|
82
|
-
let onConnectPromise = this._requestListener(ws, undefined, 'on_connect');
|
|
145
|
+
let onConnectPromise = config.events.has('on_connect') ? this._requestListener(ws, undefined, 'on_connect') : undefined;
|
|
83
146
|
|
|
84
147
|
ws.on("message", async (data) =>
|
|
85
148
|
{
|
|
@@ -92,10 +155,12 @@ module.exports = class HttpRunner extends events.EventEmitter
|
|
|
92
155
|
|
|
93
156
|
ws.on("close", async(code, reason) =>
|
|
94
157
|
{
|
|
95
|
-
await this._requestListener(ws, undefined, 'on_disconnect');
|
|
158
|
+
await (config.events.has('on_disconnect') ? this._requestListener(ws, undefined, 'on_disconnect') : undefined);
|
|
96
159
|
|
|
97
160
|
const reasonMessage = reason && reason.length ? reason.toString("utf8") : "";
|
|
98
161
|
|
|
162
|
+
delete connections[connectionId];
|
|
163
|
+
|
|
99
164
|
this.emit("trace", `[disconnect] ${connectionId} (${code}${reasonMessage ? ` - ${reasonMessage}` : ""})`);
|
|
100
165
|
});
|
|
101
166
|
|
|
@@ -118,6 +183,7 @@ module.exports = class HttpRunner extends events.EventEmitter
|
|
|
118
183
|
if ('key' in options) this.key = options.key;
|
|
119
184
|
if ('cert' in options) this.cert = options.cert;
|
|
120
185
|
if ('sPort' in options) this.sPort = options.sPort;
|
|
186
|
+
if ('mPort' in options) this.mPort = options.mPort;
|
|
121
187
|
}
|
|
122
188
|
|
|
123
189
|
async _requestListener(ws, incomingMessage, customEventName)
|
|
@@ -188,4 +254,81 @@ module.exports = class HttpRunner extends events.EventEmitter
|
|
|
188
254
|
}
|
|
189
255
|
}
|
|
190
256
|
|
|
257
|
+
async _handleManagementRequest(incomingMessage, serverResponse)
|
|
258
|
+
{
|
|
259
|
+
try
|
|
260
|
+
{
|
|
261
|
+
let event = new Configurapi.Event(await this._toRequest(incomingMessage));
|
|
262
|
+
|
|
263
|
+
await this.service.process(event);
|
|
264
|
+
|
|
265
|
+
serverResponse.writeHead(200, event.response.headers);
|
|
266
|
+
serverResponse.end(JSON.stringify(event.response));
|
|
267
|
+
}
|
|
268
|
+
catch (error)
|
|
269
|
+
{
|
|
270
|
+
let response = new Configurapi.ErrorResponse(error, error instanceof SyntaxError ? 400 : 500);
|
|
271
|
+
|
|
272
|
+
serverResponse.writeHead(response.statusCode, response.headers);
|
|
273
|
+
serverResponse.end(JSON.stringify(response.body));
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
async _toRequest(incomingMessage)
|
|
278
|
+
{
|
|
279
|
+
let request = new Configurapi.Request();
|
|
280
|
+
|
|
281
|
+
request.method = incomingMessage.method.toLowerCase();
|
|
282
|
+
request.headers = incomingMessage.headers;
|
|
283
|
+
|
|
284
|
+
let url = URL.parse(incomingMessage.url, true);
|
|
285
|
+
let data = [];
|
|
286
|
+
|
|
287
|
+
request.payload = undefined;
|
|
288
|
+
request.query = url.query;
|
|
289
|
+
request.path = url.pathname;
|
|
290
|
+
request.pathAndQuery = url.href;
|
|
291
|
+
|
|
292
|
+
return new Promise((resolve, reject) =>
|
|
293
|
+
{
|
|
294
|
+
incomingMessage.on('data', function(chunk) {
|
|
295
|
+
data.push(chunk);
|
|
296
|
+
}).on('end', function() {
|
|
297
|
+
if(data.length <= 0) {
|
|
298
|
+
request.payload = undefined;
|
|
299
|
+
return resolve(request);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
request.payload = Buffer.concat(data);
|
|
303
|
+
|
|
304
|
+
if(request.payload && 'content-type' in request.headers)
|
|
305
|
+
{
|
|
306
|
+
let contentType = request.headers['content-type'];
|
|
307
|
+
|
|
308
|
+
if(contentType.startsWith('text/'))
|
|
309
|
+
{
|
|
310
|
+
request.payload = request.payload.toString();
|
|
311
|
+
}
|
|
312
|
+
else if(contentType.startsWith('application/json'))
|
|
313
|
+
{
|
|
314
|
+
try
|
|
315
|
+
{
|
|
316
|
+
if(request.payload)
|
|
317
|
+
{
|
|
318
|
+
request.payload = JSON.parse(request.payload);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
catch(err)
|
|
322
|
+
{
|
|
323
|
+
reject(err);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
resolve(request);
|
|
329
|
+
}).on('error', function(err) {
|
|
330
|
+
reject(err);
|
|
331
|
+
});
|
|
332
|
+
});
|
|
333
|
+
}
|
|
191
334
|
};
|