dcp-worker 4.1.1 → 4.2.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/.gitlab-ci.yml +1 -3
- package/bin/dcp-evaluator-manager +33 -15
- package/bin/dcp-evaluator-start +149 -119
- package/bin/dcp-task-probe +20 -11
- package/bin/dcp-worker +698 -578
- package/lib/blessed-components/log.js +10 -0
- package/lib/check-scheduler-version.js +3 -2
- package/lib/consts.js +25 -0
- package/lib/loggers.js +256 -0
- package/lib/node-version-check.js +4 -2
- package/lib/pidfile.js +88 -4
- package/lib/reports.js +95 -0
- package/lib/show.js +54 -0
- package/lib/{remote-console.js → telnetd.js} +32 -19
- package/lib/utils.js +112 -9
- package/lib/web-console.js +178 -0
- package/lib/web-iface.js +128 -0
- package/lib/{dashboard-tui.js → worker-consoles/dashboard-console.js} +167 -57
- package/lib/worker-consoles/index.js +44 -0
- package/lib/worker-consoles/none-console.js +23 -0
- package/lib/worker-consoles/stdio-console.js +70 -0
- package/lib/worker-info.js +29 -20
- package/lib/worker-loggers/event-log.js +24 -26
- package/lib/worker-loggers/logfile.js +73 -65
- package/lib/worker-loggers/syslog.js +145 -43
- package/package.json +6 -4
- package/{bin/publish-docs.sh → publish-docs.sh} +1 -1
- package/tests/worker-with-no-options.simple +35 -13
- package/tests/worker-with-options.simple +65 -18
- package/lib/startWorkerLogger.js +0 -138
- package/lib/worker-loggers/console.js +0 -74
- package/lib/worker-loggers/dashboard.js +0 -76
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @file
|
|
2
|
+
* @file telnetd.js
|
|
3
3
|
* DCP Service Worker support for a remote console, accessible via telnet.
|
|
4
4
|
*
|
|
5
5
|
* * SECURITY NOTICE *
|
|
@@ -23,9 +23,10 @@
|
|
|
23
23
|
*/
|
|
24
24
|
'use strict';
|
|
25
25
|
|
|
26
|
-
const path
|
|
27
|
-
const fs
|
|
28
|
-
const
|
|
26
|
+
const path = require('path');
|
|
27
|
+
const fs = require('fs');
|
|
28
|
+
const debug = require('debug');
|
|
29
|
+
|
|
29
30
|
var dcpConfig;
|
|
30
31
|
var mainEval;
|
|
31
32
|
var ci;
|
|
@@ -46,10 +47,29 @@ function daemonEval()
|
|
|
46
47
|
return eval(arguments[0]); /* eslint-disable-line no-eval */
|
|
47
48
|
}
|
|
48
49
|
|
|
49
|
-
function callbackTelnet(
|
|
50
|
+
function callbackTelnet()
|
|
50
51
|
{
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
/* The telnet daemon is purposefully started as early and synchronously as possible, so that it is
|
|
53
|
+
* ready to capture errors in and/or help troubleshoot the most basic tasks, like setting up the
|
|
54
|
+
* console, log multiplexing, or command-line option parsing.
|
|
55
|
+
*
|
|
56
|
+
* In this callback, we purposefully push the telnetd startup status reporting so that it is more
|
|
57
|
+
* likely that the multiplexed loggers and/or the dashboard will be ready for the diagnostic
|
|
58
|
+
* messages.
|
|
59
|
+
*/
|
|
60
|
+
if (arguments.length === 1)
|
|
61
|
+
{
|
|
62
|
+
const [ error ] = arguments;
|
|
63
|
+
setTimeout(() => console.error(` ! telnetd - startup error ${error.code || error.message}`), 250);
|
|
64
|
+
debug('dcp-worker:telnet')('telnet listen error:', error);
|
|
65
|
+
}
|
|
66
|
+
else
|
|
67
|
+
{
|
|
68
|
+
const [ port, server, _registry ] = arguments; // eslint-disable-line no-unused-vars
|
|
69
|
+
setTimeout(() => console.warn(' ! telnetd listening on port', port), 250);
|
|
70
|
+
debug('dcp-worker:telnet')('telnetd listening on', server.address().family, server.address().address, server.address().port);
|
|
71
|
+
server.unref();
|
|
72
|
+
}
|
|
53
73
|
}
|
|
54
74
|
|
|
55
75
|
/**
|
|
@@ -64,7 +84,7 @@ exports.setMainEval = function removeConsole$$setMainEval()
|
|
|
64
84
|
/**
|
|
65
85
|
* Initialize the remote console
|
|
66
86
|
*
|
|
67
|
-
* @param {...object} commands Command definitions. See telnet-console documentation
|
|
87
|
+
* @param {...object} commands Command definitions. See telnet-console documentation
|
|
68
88
|
* for details
|
|
69
89
|
*/
|
|
70
90
|
exports.init = function remoteConsole$$init(...commands)
|
|
@@ -84,16 +104,8 @@ exports.init = function remoteConsole$$init(...commands)
|
|
|
84
104
|
|
|
85
105
|
console.warn('*** Enabling telnet daemon on port', port, '(security risk) ***');
|
|
86
106
|
|
|
87
|
-
if (port !== 0)
|
|
88
|
-
exports.port = port;
|
|
89
|
-
else
|
|
90
|
-
{
|
|
91
|
-
/* telnet-console library does not properly support port 0 so we mostly work-around here */
|
|
92
|
-
exports.port = Math.floor(1023 + (Math.random() * (63 * 1024)));
|
|
93
|
-
}
|
|
94
|
-
|
|
95
107
|
ci = require('telnet-console').start({
|
|
96
|
-
port
|
|
108
|
+
port,
|
|
97
109
|
callbackTelnet,
|
|
98
110
|
eval: daemonEval,
|
|
99
111
|
histfile: edcFilename + '.history',
|
|
@@ -105,12 +117,13 @@ exports.init = function remoteConsole$$init(...commands)
|
|
|
105
117
|
}
|
|
106
118
|
catch(e)
|
|
107
119
|
{
|
|
108
|
-
console.warn(
|
|
120
|
+
console.warn(` ! Failed to enable telnet daemon (${e.code || e.message})`);
|
|
121
|
+
debug('dcp-worker:telnet')('telnet daemon error:', e);
|
|
109
122
|
}
|
|
110
123
|
}
|
|
111
124
|
|
|
112
125
|
/**
|
|
113
|
-
* Re-intercept log calls. To be used after
|
|
126
|
+
* Re-intercept log calls. To be used after the globalThis.console has been replaced or mutated.
|
|
114
127
|
*/
|
|
115
128
|
exports.reintercept = function remoteConsole$$reintercept()
|
|
116
129
|
{
|
package/lib/utils.js
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
|
-
|
|
2
1
|
/**
|
|
3
2
|
* @file utils.js
|
|
4
3
|
* Shared library code.
|
|
5
4
|
*
|
|
6
5
|
* @author Paul, paul@distributive.network
|
|
7
6
|
* @date August 2023
|
|
7
|
+
* @author Wes Garland, wes@distributive.network
|
|
8
|
+
* @date June 2025
|
|
8
9
|
*/
|
|
9
10
|
'use strict';
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
exports.slicesFetched = slicesFetched;
|
|
13
|
+
exports.debugging = debugging;
|
|
14
|
+
exports.isHash = isHash;
|
|
15
|
+
exports.shortLoc = shortLoc;
|
|
16
|
+
exports.qty = qty;
|
|
17
|
+
exports.uniq = uniq;
|
|
18
|
+
exports.makeSyslogUrl = makeSyslogUrl;
|
|
12
19
|
|
|
13
20
|
/**
|
|
14
21
|
* Figure out #slices fetched from the different forms of the 'fetch' event.
|
|
@@ -33,14 +40,110 @@ function debugging()
|
|
|
33
40
|
}
|
|
34
41
|
|
|
35
42
|
/**
|
|
36
|
-
*
|
|
37
|
-
* @
|
|
43
|
+
* Output hostname:port for an arbitrary URL object
|
|
44
|
+
* @param {URL|DcpURL} url
|
|
45
|
+
*/
|
|
46
|
+
function shortLoc(url)
|
|
47
|
+
{
|
|
48
|
+
var port = url.port;
|
|
49
|
+
|
|
50
|
+
if (!port)
|
|
51
|
+
{
|
|
52
|
+
switch(url.protocol)
|
|
53
|
+
{
|
|
54
|
+
case 'http:':
|
|
55
|
+
case 'ws:':
|
|
56
|
+
port = 80;
|
|
57
|
+
break;
|
|
58
|
+
case 'https:':
|
|
59
|
+
case 'wss:':
|
|
60
|
+
port = 443;
|
|
61
|
+
break;
|
|
62
|
+
case 'ftp:':
|
|
63
|
+
port = 21;
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return `${url.hostname}:${port}`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Imperfect, but handles CG { joinKey, joinHash }.
|
|
73
|
+
* @param {string} b string which might be a join hash
|
|
74
|
+
* @returns {boolean} returns true when b is a join hash; false otherwise
|
|
38
75
|
*/
|
|
39
|
-
function
|
|
76
|
+
function isHash(b)
|
|
40
77
|
{
|
|
41
|
-
return
|
|
78
|
+
return b && b.length === 68 && b.startsWith('eh1-');
|
|
42
79
|
}
|
|
43
80
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
81
|
+
/**
|
|
82
|
+
* Display a quantity of things with units
|
|
83
|
+
* @param {number} amount the number of things
|
|
84
|
+
* @param {string} singular singular form of unit
|
|
85
|
+
* @param {string} plural plural form of unit
|
|
86
|
+
* @returns {string}
|
|
87
|
+
* @example
|
|
88
|
+
* qty(1, 'apple', 'apples') => '1 apple'
|
|
89
|
+
* qty(2, 'apple', 'apples') => '2 apples'
|
|
90
|
+
* @todo i18n
|
|
91
|
+
*/
|
|
92
|
+
function qty(amount, singular, plural)
|
|
93
|
+
{
|
|
94
|
+
if (Array.isArray(amount))
|
|
95
|
+
amount = amount.length;
|
|
96
|
+
if (!plural)
|
|
97
|
+
plural = singular + 's';
|
|
98
|
+
if (!amount)
|
|
99
|
+
return plural;
|
|
100
|
+
if (Number(amount) === 1)
|
|
101
|
+
return singular;
|
|
102
|
+
return plural;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Return a new array composed only of unique elements of arr
|
|
107
|
+
* @param {Array} arr
|
|
108
|
+
* @returns {Array}
|
|
109
|
+
*/
|
|
110
|
+
function uniq(array)
|
|
111
|
+
{
|
|
112
|
+
return array.filter((val, idx, arr) => arr.indexOf(val) === idx);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Syslog URLs are bit special - the pathname of the URL encodes the syslog facility. Additionally, a
|
|
117
|
+
* plain string treated as just a pathname and no other components
|
|
118
|
+
*
|
|
119
|
+
* existingUrl newArg result
|
|
120
|
+
* ------------------------------- -------------------------------- ----------------------------------
|
|
121
|
+
* udp://localhost:513/local0 tls://provider.com:123/local1 tls://provider.com:123/local1
|
|
122
|
+
* udp://localhost:513/local0 local1 udp://localhost:513/local1
|
|
123
|
+
* udp://localhost:513/local0 tls://provider.com:123/ tls://provider.com:123/local0
|
|
124
|
+
*
|
|
125
|
+
* @param {URL} existingUrl
|
|
126
|
+
* @param {string|URL} newArg
|
|
127
|
+
* @returns {URL}
|
|
128
|
+
*/
|
|
129
|
+
function makeSyslogUrl(existingUrl, newArg)
|
|
130
|
+
{
|
|
131
|
+
var newUrl;
|
|
132
|
+
|
|
133
|
+
if (/^[A-Za-z]+[0-9]?$/.test(newArg))
|
|
134
|
+
{
|
|
135
|
+
newUrl = new URL(existingUrl.href);
|
|
136
|
+
newUrl.pathname = newArg;
|
|
137
|
+
}
|
|
138
|
+
else
|
|
139
|
+
{
|
|
140
|
+
newUrl = new URL(newArg)
|
|
141
|
+
if (!newUrl.pathname)
|
|
142
|
+
newUrl.pathname = existingUrl.pathname;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (!newUrl.pathname)
|
|
146
|
+
newUrl.pathname = 'local7';
|
|
147
|
+
|
|
148
|
+
return newUrl;
|
|
149
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* https://stackoverflow.com/questions/52843900/blessed-server-node-js-over-websocket-to-xterm-js-client-in-browser
|
|
3
|
+
*/
|
|
4
|
+
const blessed = require('blessed');
|
|
5
|
+
const contrib = require('blessed-contrib');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const http = require('http');
|
|
9
|
+
|
|
10
|
+
const progDir = path.dirname(require.main.filename);
|
|
11
|
+
const wwwDir = path.resolve(progDir, '../www');
|
|
12
|
+
const nmDir = path.resolve(progDir, '../node_modules');
|
|
13
|
+
|
|
14
|
+
const magic = {
|
|
15
|
+
html: 'text/html',
|
|
16
|
+
txt: 'text/plain',
|
|
17
|
+
js: 'application/javascript',
|
|
18
|
+
css: 'text/css',
|
|
19
|
+
gif: 'image/gif',
|
|
20
|
+
jpeg: 'image/jpeg',
|
|
21
|
+
jpg: 'image/jpeg',
|
|
22
|
+
png: 'image/png',
|
|
23
|
+
svg: 'image/svg+xml',
|
|
24
|
+
kvin: 'application/x-kvin',
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const clients = [ ];
|
|
28
|
+
var worker; /* instance of DistributiveWorker, null (stopped), or undefined (not started) */
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Return a copy of the local dcp config, with the current worker's config at dcpConfig.worker, but with
|
|
32
|
+
* with sensitive information (eg compute group credentials) stripped.
|
|
33
|
+
*/
|
|
34
|
+
function makeSafeConfig()
|
|
35
|
+
{
|
|
36
|
+
const conf = Object.assign({}, dcpConfig);
|
|
37
|
+
conf.worker = Object.assign({}, worker.config || dcpConfig.worker?.config);
|
|
38
|
+
|
|
39
|
+
/* Sanitize worker.computeGroups credentials */
|
|
40
|
+
if (conf.worker?.computeGroups)
|
|
41
|
+
{
|
|
42
|
+
conf.worker.computeGroups = Object.assign({}, conf.worker.computeGroups);
|
|
43
|
+
for (let key in conf.worker.computeGroups)
|
|
44
|
+
{
|
|
45
|
+
const group = conf.worker.computeGroups[key];
|
|
46
|
+
if (group.joinKey)
|
|
47
|
+
group = { joinKey: group.joinKey };
|
|
48
|
+
else
|
|
49
|
+
group = {};
|
|
50
|
+
conf.worker.computeGroups[key] = group;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return conf;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* HTTP server
|
|
59
|
+
*/
|
|
60
|
+
function handleHttpRequest(request, response)
|
|
61
|
+
{
|
|
62
|
+
if (request.url === '/etc/dcp-config.js')
|
|
63
|
+
{
|
|
64
|
+
response.setHeader('Content-Type', magic.js);
|
|
65
|
+
response.end(makeSkeletonConfig());
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (request.url === '/etc/dcp-config.kvin')
|
|
70
|
+
{
|
|
71
|
+
response.setHeader('Content-Type', magic.kvin);
|
|
72
|
+
response.end('(' + JSON.stringify(dcpConfig) + ')');
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
var filename = (request.url.slice(1) || 'index.html');
|
|
77
|
+
const contentType = magic[path.extname(filename).slice(1)] || 'text/plain';
|
|
78
|
+
var errorStatus, contentDir;
|
|
79
|
+
|
|
80
|
+
if (!filename.startsWith('node_modules/'))
|
|
81
|
+
contentDir = wwwDir;
|
|
82
|
+
else
|
|
83
|
+
{
|
|
84
|
+
contentDir = nmDir;
|
|
85
|
+
filename = filename.slice(13);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
filename = path.resolve(contentDir, filename);
|
|
89
|
+
console.log({contentDir,filename,requestUrl:request.url});
|
|
90
|
+
if (!filename.startsWith(contentDir) || filename[0] !== '/')
|
|
91
|
+
errorStatus = 401;
|
|
92
|
+
else if (!fs.existsSync(filename))
|
|
93
|
+
errorStatus = 404;
|
|
94
|
+
|
|
95
|
+
if (errorStatus)
|
|
96
|
+
{
|
|
97
|
+
response.setHeader('Content-Type', 'text/plain');
|
|
98
|
+
response.statusCode = errorStatus;
|
|
99
|
+
response.end(`${errorStatus} accessing ${filename}`);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
response.setHeader('Content-Type', contentType);
|
|
104
|
+
response.end(fs.readFileSync(filename));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
exports.getConfig = function webConsole$$getConfig()
|
|
108
|
+
{
|
|
109
|
+
var config = { host: 'localhost', port: 9080 };
|
|
110
|
+
|
|
111
|
+
if (dcpConfig.worker?.webConsole?.listen)
|
|
112
|
+
{
|
|
113
|
+
config.host = dcpConfig.worker.webConsole.listen.host;
|
|
114
|
+
config.port = dcpConfig.worker.webConsole.listen.port || 80;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return config;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
exports.a$init = function webConsole$$init()
|
|
121
|
+
{
|
|
122
|
+
var server = http.createServer(handleHttpRequest).unref();
|
|
123
|
+
var resolve, reject;
|
|
124
|
+
var a$Promise = new Promise((_resolve, _reject) => {
|
|
125
|
+
resolve = _resolve;
|
|
126
|
+
reject = _reject;
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
server.on('error', error => {
|
|
130
|
+
console.error(` ! Web console not started; ${error.message}`);
|
|
131
|
+
reject(error);
|
|
132
|
+
});
|
|
133
|
+
server.listen(exports.getConfig(), function webConsole$$ready() {
|
|
134
|
+
resolve();
|
|
135
|
+
console.log(` * httpd listening on ${server._connectionKey}`);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* WebSocket server
|
|
140
|
+
*/
|
|
141
|
+
const WebSocketServer = require('websocket').server;
|
|
142
|
+
const wsServer = new WebSocketServer({
|
|
143
|
+
httpServer: server,
|
|
144
|
+
autoAcceptConnections: false
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
wsServer.on('request', handleWssRequest);
|
|
148
|
+
function handleWssRequest(request)
|
|
149
|
+
{
|
|
150
|
+
const connection = request.accept(null, request.origin);
|
|
151
|
+
connection.socket.unref();
|
|
152
|
+
clients.push(connection);
|
|
153
|
+
|
|
154
|
+
const write = connection.send;
|
|
155
|
+
const read = connection.socket.read;
|
|
156
|
+
|
|
157
|
+
connection.send(JSON.stringify({ type: 'log', facility: 'debug', message: 'hello, world' }));
|
|
158
|
+
connection.on('message', messageHandler);
|
|
159
|
+
connection.on('close', closeHandler);
|
|
160
|
+
|
|
161
|
+
function messageHandler(message)
|
|
162
|
+
{
|
|
163
|
+
console.log('message:', message)
|
|
164
|
+
console.log('this:', this);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function closeHandler()
|
|
168
|
+
{
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return a$Promise;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
exports.setWorker = function webConsole$$setWorker(dw)
|
|
176
|
+
{
|
|
177
|
+
worker = dw;
|
|
178
|
+
}
|
package/lib/web-iface.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* https://stackoverflow.com/questions/52843900/blessed-server-node-js-over-websocket-to-xterm-js-client-in-browser
|
|
3
|
+
*/
|
|
4
|
+
const blessed = require('blessed');
|
|
5
|
+
const contrib = require('blessed-contrib');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const http = require('http');
|
|
9
|
+
|
|
10
|
+
const progDir = path.dirname(require.main.filename);
|
|
11
|
+
const wwwDir = path.resolve(progDir, '../www');
|
|
12
|
+
const nmDir = path.resolve(progDir, '../node_modules');
|
|
13
|
+
const listenHost = listenUrl.hostname === 'localhost' ? '::' : listenUrl.hostname;
|
|
14
|
+
|
|
15
|
+
const magic = {
|
|
16
|
+
html: 'text/html',
|
|
17
|
+
txt: 'text/plain',
|
|
18
|
+
js: 'application/javascript',
|
|
19
|
+
css: 'text/css',
|
|
20
|
+
gif: 'image/gif',
|
|
21
|
+
jpeg: 'image/jpeg',
|
|
22
|
+
jpg: 'image/jpeg',
|
|
23
|
+
png: 'image/png',
|
|
24
|
+
svg: 'image/svg+xml',
|
|
25
|
+
kvin: 'application/x-kvin',
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const clients = [ ];
|
|
29
|
+
|
|
30
|
+
function makeSkeletonConfig()
|
|
31
|
+
{
|
|
32
|
+
return `window.dcpConfig = {
|
|
33
|
+
scheduler: {
|
|
34
|
+
location: new URL('${dcpConfig.scheduler.location.href}'),
|
|
35
|
+
},
|
|
36
|
+
const listenUrl = dcpConfig.webConsole.listen;
|
|
37
|
+
worker: (${JSON.stringify(dcpConfig.worker)})
|
|
38
|
+
}`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* HTTP server
|
|
43
|
+
*/
|
|
44
|
+
var server = http.createServer(handleHttpRequest).unref();
|
|
45
|
+
function handleHttpRequest(request, response)
|
|
46
|
+
{
|
|
47
|
+
if (request.url === '/etc/dcp-config.js')
|
|
48
|
+
{
|
|
49
|
+
response.setHeader('Content-Type', magic.js);
|
|
50
|
+
response.end(makeSkeletonConfig());
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (request.url === '/etc/dcp-config.kvin')
|
|
55
|
+
{
|
|
56
|
+
response.setHeader('Content-Type', magic.kvin);
|
|
57
|
+
response.end('(' + JSON.stringify(dcpConfig) + ')');
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
var filename = (request.url.slice(1) || 'index.html');
|
|
62
|
+
const contentType = magic[path.extname(filename).slice(1)] || 'text/plain';
|
|
63
|
+
var errorStatus, contentDir;
|
|
64
|
+
|
|
65
|
+
if (!filename.startsWith('node_modules/'))
|
|
66
|
+
contentDir = wwwDir;
|
|
67
|
+
else
|
|
68
|
+
{
|
|
69
|
+
contentDir = nmDir;
|
|
70
|
+
filename = filename.slice(13);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
filename = path.resolve(contentDir, filename);
|
|
74
|
+
console.log({contentDir,filename,requestUrl:request.url});
|
|
75
|
+
if (!filename.startsWith(contentDir) || filename[0] !== '/')
|
|
76
|
+
errorStatus = 401;
|
|
77
|
+
else if (!fs.existsSync(filename))
|
|
78
|
+
errorStatus = 404;
|
|
79
|
+
|
|
80
|
+
if (errorStatus)
|
|
81
|
+
{
|
|
82
|
+
response.setHeader('Content-Type', 'text/plain');
|
|
83
|
+
response.statusCode = errorStatus;
|
|
84
|
+
response.end(`${errorStatus} accessing ${filename}`);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
response.setHeader('Content-Type', contentType);
|
|
89
|
+
response.end(fs.readFileSync(filename));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
server.listen({ host: listenHost, port: listenUrl.port }, function(server) {
|
|
93
|
+
console.log(`listening on port ${listenUrl.href}`);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* WebSocket server
|
|
98
|
+
*/
|
|
99
|
+
const WebSocketServer = require('websocket').server;
|
|
100
|
+
const wsServer = new WebSocketServer({
|
|
101
|
+
httpServer: server,
|
|
102
|
+
autoAcceptConnections: false
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
wsServer.on('request', handleWssRequest);
|
|
106
|
+
function handleWssRequest(request)
|
|
107
|
+
{
|
|
108
|
+
const connection = request.accept(null, request.origin);
|
|
109
|
+
connection.socket.unref();
|
|
110
|
+
clients.push(connection);
|
|
111
|
+
|
|
112
|
+
const write = connection.send;
|
|
113
|
+
const read = connection.socket.read;
|
|
114
|
+
|
|
115
|
+
connection.send(JSON.stringify({ type: 'log', facility: 'debug', message: 'hello, world' }));
|
|
116
|
+
connection.on('message', messageHandler);
|
|
117
|
+
connection.on('close', closeHandler);
|
|
118
|
+
|
|
119
|
+
function messageHandler(message)
|
|
120
|
+
{
|
|
121
|
+
console.log('message:', message)
|
|
122
|
+
console.log('this:', this);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function closeHandler()
|
|
126
|
+
{
|
|
127
|
+
}
|
|
128
|
+
}
|