UNPKG

11.5 kBJavaScriptView Raw
1/*
2
3 ----------------------------------------------------------------------------
4 | qewd: Quick and Easy Web Development |
5 | |
6 | Copyright (c) 2017-20 M/Gateway Developments Ltd, |
7 | Redhill, Surrey UK. |
8 | All rights reserved. |
9 | |
10 | http://www.mgateway.com |
11 | Email: rtweed@mgateway.com |
12 | |
13 | |
14 | Licensed under the Apache License, Version 2.0 (the "License"); |
15 | you may not use this file except in compliance with the License. |
16 | You may obtain a copy of the License at |
17 | |
18 | http://www.apache.org/licenses/LICENSE-2.0 |
19 | |
20 | Unless required by applicable law or agreed to in writing, software |
21 | distributed under the License is distributed on an "AS IS" BASIS, |
22 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
23 | See the License for the specific language governing permissions and |
24 | limitations under the License. |
25 ----------------------------------------------------------------------------
26
27 11 February 2020
28
29 Thanks to:
30 Ward De Backer for body-parser enhancement
31 and pre-forking code (for use with debuggers)
32
33*/
34
35/*
36// Example usage
37
38var params = {
39 managementPassword: 'keepThisSecret!',
40 serverName: 'New EWD Server',
41 port: 8081,
42 poolSize: 1
43};
44var qewd = require('qewd').master;
45
46// if you want to define REST/web service URL paths with associated routing:
47
48var routes = [
49 {
50 path: '/testx' // module will default to testx.js
51 },
52 {
53 path: '/patient',
54 module: 'my-patient-module'
55 }
56];
57
58var q = qewd.start(config, routes);
59
60 // if you want to add your own user-defined objects that will be
61 // available in the worker process(es) (in the this.userDefined object):
62
63q.userDefined['myCustomObject'] = { // your object };
64
65*/
66
67var qoper8;
68// Use Worker thread version for Node 12 and later
69//if (parseInt(process.versions.node.split('.')[0]) > 11) {
70// qoper8 = require('qoper8');
71//}
72//else {
73 // use older Child Process version
74 qoper8 = require('ewd-qoper8');
75//}
76var sockets = require('./sockets');
77var configureMicroServices = require('./microServices');
78var fs = require('fs');
79//var q = new qoper8.masterProcess();
80var q;
81var app;
82var qx;
83
84
85function start(params, routes) {
86 params = params || {};
87 var maxq = params.max_queue_length || 20000;
88 q = new qoper8.masterProcess(maxq);
89
90 console.log('Double ended queue max length set to ' + maxq);
91
92 var emptyMap = !params.moduleMap;
93 var moduleMap = params.moduleMap || {};
94 if (routes) {
95 routes.forEach(function(route) {
96 var path = route.path;
97 if (path.charAt(0) === '/') {
98 path = path.substring(1);
99 }
100 var module = route.module || path;
101 moduleMap[path] = module;
102 emptyMap = false;
103 });
104 }
105 if (emptyMap) moduleMap = false;
106
107 var config = {
108 managementPassword: params.managementPassword || 'keepThisSecret',
109 serverName: params.serverName || 'qewd',
110 port: params.port || 8080,
111 poolSize: params.poolSize || 1,
112 poolPrefork: params.poolPrefork || false,
113 idleLimit: params.idleLimit || 3600000,
114 webServer: params.webServer || 'express',
115 webServerRootPath: params.webServerRootPath || process.cwd() + '/www/',
116 no_sockets: params.no_sockets || false,
117 webSockets: params.webSockets || {module: 'socket.io'},
118 ssl: params.ssl || false, // for ssl mode: ssl: {keyFilePath: 'xxx.key', certFilePath: 'xxx.crt'},
119 cors: params.cors || false,
120 masterProcessPid: process.pid,
121 database: params.database,
122 errorLogFile: params.errorLogFile || false,
123 mode: params.mode || 'production',
124 bodyParser: params.bodyParser || false, // allow the user to pass in its own bodyParser
125 addMiddleware: params.addMiddleware || false, // allow the user to add custom Express middleware
126 addMiddlewareUp: params.addMiddlewareUp || false, // new interface for QEWD-Up
127 initialSessionTimeout: params.initialSessionTimeout || 300,
128 sessionDocumentName: params.sessionDocumentName || 'CacheTempEWDSession',
129 moduleMap: moduleMap,
130 lockSession: params.lockSession || false, // true | {timeout: 60}
131 resilientMode: params.resilientMode || false,
132 customSocketModule: params.customSocketModule || false,
133 jwt: params.jwt || false,
134 qewd_up: params.qewd_up || false,
135 permit_application_switch: params.permit_application_switch,
136 service_name: params.service_name || false
137 };
138 if (params.resilientMode) {
139 config.resilientMode = {
140 documentName: params.resilientMode.queueDocumentName || 'ewdQueue',
141 keepPeriod: params.resilientMode.keepPeriod || 3600
142 }
143 }
144
145 if (config.webServer !== 'express' && config.webServer !== 'koa') config.webServer = 'express';
146
147 if (config.jwt) {
148 q.jwt = config.jwt;
149 q.jwt.handlers = require('./jwtHandler');
150 }
151
152 if (params.u_services) {
153 configureMicroServices.call(q, params.u_services);
154 }
155
156 qx = require('ewd-qoper8-' + config.webServer);
157 var wsConfig = require('./master-' + config.webServer);
158 qx.addTo(q);
159 config.qxBuild = qx.build;
160
161 app = wsConfig(config, routes, q, qx);
162
163 q.on('start', function() {
164 this.worker.module = 'qewd.worker';
165 //this.worker.loaderFilePath = 'node_modules/ewd-xpress/node_modules/ewd_qoper8-worker.js';
166 this.setWorkerPoolSize(config.poolSize);
167 this.setWorkerIdleLimit(config.idleLimit);
168
169 if (params.database && params.resilientMode) {
170 // connect master process to database
171 // it will use asynch connections to save a copy of
172 // each queued request, and delete them when done
173 console.log('starting resilient mode..');
174 this.resilientMode = {
175 documentName: config.resilientMode.documentName
176 };
177 var db = params.database;
178 var type = db.type;
179
180 if (type === 'cache') db.params.lock = '1';
181 if (type === 'cache' || type === 'gtm' || type === 'redis') {
182 console.log('connecting master process to ' + type);
183 this.on('dbOpened', function() {
184 if (type !== 'redis') {
185 console.log(this.db.version());
186 }
187 else {
188 this.db.version(function(version) {
189 console.log(version);
190 });
191 }
192 });
193 this.on('dbClosed', function() {
194 console.log('Connection to ' + db.type + ' closed');
195 });
196 if (type === 'redis') {
197 if (!db.params) db.params = {};
198 // master process must use Redis async mode (just for sets and kills)
199 var redisParams = {
200 host: db.params.host,
201 port: db.params.port,
202 integer_padding: db.params.integer_padding,
203 key_separator: db.params.key_separator,
204 async: true
205 };
206 console.log('starting ' + type + ' in master process for resilient mode: ' + JSON.stringify(redisParams));
207 require('ewd-qoper8-redis')(this, redisParams);
208 return;
209 }
210 require('ewd-qoper8-' + type)(this, db.params);
211 }
212 }
213 });
214
215 q.on('started', function() {
216 if (!this.userDefined) this.userDefined = {};
217 this.userDefined.config = config;
218 var q = this;
219 var io;
220 var server;
221
222 function startServer() {
223 if (!config.ssl) {
224 server = app.listen(config.port);
225 }
226 else {
227 var https = require('https');
228 //console.log('cwd: ' + process.cwd());
229 var options = {
230 key: fs.readFileSync(config.ssl.keyFilePath),
231 cert: fs.readFileSync(config.ssl.certFilePath)
232 };
233 server = https.createServer(options, app).listen(config.port);
234 }
235 if (!config.no_sockets) {
236
237 // currently only socket.io supported for WebSockets
238
239 if (!config.webSockets.module) config.webSockets.module = 'socket.io';
240 if (config.webSockets.module === 'socket.io') {
241 if (!config.webSockets.engine) {
242 io = require('socket.io')(server, {wsEngine: 'ws'});
243 }
244 else {
245 var engine = config.webSockets.engine;
246 if (engine !== 'ws' && engine !== 'uws') engine = 'ws';
247 io = require('socket.io')(server, {wsEngine: engine});
248 }
249 }
250 }
251
252 // load QEWD socket handling logic
253 if (io) sockets(q, io, config.customSocketModule);
254
255 console.log('========================================================');
256 console.log('QEWD.js is listening on port ' + config.port);
257 console.log('========================================================');
258
259 q.on('response', function(messageObj) {
260 // handle intermediate messages from worker (which hasn't finished)
261 if (messageObj.socketId) {
262 //console.log('*** master on response sending message to ' + messageObj.socketId);
263 var id = messageObj.socketId;
264 delete messageObj.socketId;
265 delete messageObj.finished;
266 io.to(id).emit('ewdjs', messageObj);
267 }
268 });
269 }
270
271 if (config.poolPrefork) {
272 var workersStarted = 0;
273 q.on('workerStarted', function(workerPid, customQueue) {
274 var worker = q.worker.process[workerPid];
275 // put worker on hold until server is started (resilientmode)
276 // this requires that "workerProcessStarted" event is emitted synchronously after setting worker.isAvailable = true; in ewd-qoper8 startWorker
277 // so we set worker immediately unavailable again here until all workers and the server have started
278 if (worker) { worker.isAvailable = false; }
279 workersStarted++;
280 if (workersStarted === q.worker.poolSize) {
281 // start server
282 startServer();
283 // now set all workers available
284 for (var pid in q.worker.process) {
285 worker = q.worker.process[pid];
286 worker.isAvailable = true;
287 }
288 // start processing the queue immediately
289 if (customQueue || q.queue.length > 0) q.processQueue(customQueue);
290 }
291 });
292 // prefork all child processes in worker pool
293 for (var i = 0; i < q.worker.poolSize; i++) {
294 q.startWorker();
295 }
296 }
297 else {
298 startServer();
299 }
300
301 });
302
303 //if (!q.u_services) q.start();
304 q.start();
305 return q;
306}
307
308function intercept() {
309 return {
310 app: app,
311 q: q,
312 qx: qx
313 };
314}
315
316module.exports = {
317 intercept: intercept,
318 start: start
319};
320