mythix 1.0.1 → 1.0.4

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mythix",
3
- "version": "1.0.1",
3
+ "version": "1.0.4",
4
4
  "description": "Mythix is a NodeJS web-app framework",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -23,12 +23,15 @@
23
23
  "dependencies": {
24
24
  "chokidar": "^3.5.3",
25
25
  "deep-diff": "^1.0.2",
26
- "express": "^4.17.2",
27
- "express-busboy": "^8.0.0",
28
- "inflection": "^1.13.1",
26
+ "express": "^4.17.3",
27
+ "express-busboy": "^8.0.2",
28
+ "inflection": "^1.13.2",
29
29
  "lodash": "^4.17.21",
30
- "nife": "^1.2.2",
31
- "object-hash": "^2.2.0",
32
- "sequelize": "^6.13.0"
30
+ "nife": "^1.6.0",
31
+ "object-hash": "^3.0.0",
32
+ "sequelize": "^6.18.0",
33
+ "sqlite3": "^4.2.0",
34
+ "pg": "^8.7.3",
35
+ "pg-hstore": "^2.3.4"
33
36
  }
34
37
  }
@@ -135,7 +135,7 @@ describe('controller-utils', function() {
135
135
  },
136
136
  });
137
137
 
138
- console.log('ALL ROUTES: ', allRoutes);
138
+ // console.log('ALL ROUTES: ', allRoutes);
139
139
  });
140
140
  });
141
141
  });
@@ -14,7 +14,7 @@ const {
14
14
  } = require('./utils');
15
15
 
16
16
  function nowInSeconds() {
17
- return Math.floor(Date.now() / 1000);
17
+ return Date.now() / 1000;
18
18
  }
19
19
 
20
20
  // Trace what is requesting the application exit
@@ -58,6 +58,7 @@ class Application extends EventEmitter {
58
58
  autoReload: (process.env.NODE_ENV || 'development') === 'development',
59
59
  exitOnShutdown: null,
60
60
  runTasks: true,
61
+ testMode: false,
61
62
  }, _opts || {});
62
63
 
63
64
  Object.defineProperties(this, {
@@ -138,7 +139,7 @@ class Application extends EventEmitter {
138
139
  enumerable: false,
139
140
  configurable: true,
140
141
  value: this.createLogger(
141
- Object.assign({}, opts.logger || {}, this.getConfigValue('logger', {})),
142
+ Object.assign({}, this.getConfigValue('logger', {}), opts.logger || {}),
142
143
  Logger,
143
144
  ),
144
145
  },
@@ -777,17 +778,22 @@ class Application extends EventEmitter {
777
778
  }
778
779
 
779
780
  var sequelize = new Sequelize(databaseConfig);
781
+ var dbConnectionString;
782
+
783
+ if (Nife.instanceOf(databaseConfig, 'string'))
784
+ dbConnectionString = databaseConfig;
785
+ else
786
+ dbConnectionString = `${databaseConfig.dialect}://${databaseConfig.host}:${databaseConfig.port || '<default port>'}/${databaseConfig.database}`;
780
787
 
781
- var dbConnectionString = `${databaseConfig.dialect}://${databaseConfig.host}:${databaseConfig.port || '<default port>'}/${databaseConfig.database}`;
782
788
  try {
783
789
  await sequelize.authenticate();
784
790
 
785
- this.getLogger().log(`Connection to ${dbConnectionString} has been established successfully!`);
791
+ this.getLogger().info(`Connection to ${dbConnectionString} has been established successfully!`);
786
792
 
787
793
  return sequelize;
788
794
  } catch (error) {
789
795
  this.getLogger().error(`Unable to connect to database ${dbConnectionString}:`, error);
790
- await this.stop();
796
+ throw error;
791
797
  }
792
798
  }
793
799
 
@@ -803,6 +809,13 @@ class Application extends EventEmitter {
803
809
  return server;
804
810
  }
805
811
 
812
+ getDBTablePrefix(userSpecifiedPrefix) {
813
+ if (Nife.isNotEmpty(userSpecifiedPrefix))
814
+ return userSpecifiedPrefix;
815
+
816
+ return `${this.getApplicationName()}_`;
817
+ }
818
+
806
819
  async start() {
807
820
  var options = this.getOptions();
808
821
 
@@ -818,10 +831,13 @@ class Application extends EventEmitter {
818
831
  return;
819
832
  }
820
833
 
821
- databaseConfig.logging = this.getLogger().isDebugLevel();
834
+ if (options.testMode) {
835
+ databaseConfig.logging = false;
836
+ } else {
837
+ databaseConfig.logging = (this.getLogger().isDebugLevel()) ? this.getLogger().log.bind(this.getLogger()) : false;
838
+ }
822
839
 
823
- if (Nife.isEmpty(databaseConfig.tablePrefix))
824
- databaseConfig.tablePrefix = `${this.getApplicationName()}_`;
840
+ databaseConfig.tablePrefix = this.getDBTablePrefix(databaseConfig.tablePrefix);
825
841
 
826
842
  this.dbConnection = await this.connectToDatabase(databaseConfig);
827
843
 
@@ -34,6 +34,11 @@ class CommandBase {
34
34
  var app = this.getApplication();
35
35
  return app.getLogger();
36
36
  }
37
+
38
+ getDBConnection() {
39
+ var application = this.getApplication();
40
+ return application.getDBConnection();
41
+ }
37
42
  }
38
43
 
39
44
  var loadingAllCommandsInProgress = false;
@@ -63,7 +63,11 @@ module.exports = defineCommand('migrate', ({ Parent }) => {
63
63
  return doneCallback();
64
64
 
65
65
  var migrationFileName = migrationFiles[index];
66
- console.log(`Running migration ${migrationFileName}...`);
66
+
67
+ if (rollback)
68
+ console.log(`Undoing migration ${migrationFileName}...`);
69
+ else
70
+ console.log(`Running migration ${migrationFileName}...`);
67
71
 
68
72
  MigrationUtils.executeMigration(queryInterface, migrationFileName, useTransaction, 0, rollback).then(
69
73
  () => nextMigration(doneCallback, index + 1),
@@ -78,10 +82,10 @@ module.exports = defineCommand('migrate', ({ Parent }) => {
78
82
  var applicationOptions = application.getOptions();
79
83
  var migrationsPath = applicationOptions.migrationsPath;
80
84
  var migrationFiles = this.getMigrationFiles(migrationsPath);
81
- var useTransaction = (args['transaction']) ? true : false;
85
+ var useTransaction = args.transaction;
82
86
  var rollback = args.rollback;
83
87
 
84
- console.log('USING TRANSACTION: ', useTransaction, args['no-transaction']);
88
+ console.log('USING TRANSACTION: ', useTransaction, args['transaction'], rollback, typeof rollback);
85
89
 
86
90
  if (args.revision)
87
91
  migrationFiles = this.getMigrationFilesFromRevision(migrationFiles, args.revision);
@@ -0,0 +1,39 @@
1
+ const { defineCommand } = require('./cli-utils');
2
+ const { Logger } = require('../logger');
3
+
4
+ module.exports = defineCommand('routes', ({ Parent }) => {
5
+ return class RoutesCommand extends Parent {
6
+ static description = 'List application routes';
7
+ static applicationConfig = { logger: { level: Logger.ERROR } };
8
+
9
+ execute(args) {
10
+ const whitespaceOfLength = (len) => {
11
+ if (len < 0)
12
+ return '';
13
+
14
+ var parts = new Array(len);
15
+ for (var i = 0, il = parts.length; i < il; i++)
16
+ parts[i] = ' ';
17
+
18
+ return parts.join('');
19
+ };
20
+
21
+ const stringToLength = (str, len) => {
22
+ return `${str}${whitespaceOfLength(len - str.length)}`
23
+ };
24
+
25
+ var application = this.getApplication();
26
+ var routes = application.buildRoutes(null, application.getRoutes());
27
+
28
+ routes.forEach((route) => {
29
+ var methods = route.methods;
30
+ if (!methods === '*')
31
+ methods = [ '{ANY}' ];
32
+
33
+ methods.forEach((method) => {
34
+ console.log(`${stringToLength(method, 8)}${route.path} -> ${route.controller}`);
35
+ });
36
+ });
37
+ }
38
+ };
39
+ });
@@ -1,4 +1,5 @@
1
- const Nife = require('nife');
1
+ const Nife = require('nife');
2
+ const HTTPErrors = require('../http-server/http-errors');
2
3
 
3
4
  class ControllerBase {
4
5
  constructor(application, logger, request, response) {
@@ -27,6 +28,12 @@ class ControllerBase {
27
28
  configurable: true,
28
29
  value: logger,
29
30
  },
31
+ 'route': {
32
+ writable: true,
33
+ enumberable: false,
34
+ configurable: true,
35
+ value: null,
36
+ },
30
37
  });
31
38
  }
32
39
 
@@ -54,8 +61,31 @@ class ControllerBase {
54
61
  return application.getModels();
55
62
  }
56
63
 
57
- async handleIncomingRequest(request, response, { route, controller, controllerMethod, controllerInstance, startTime, params }) {
58
- return await this[controllerMethod].call(this, params, request.query || {}, request.body, this.getModels());
64
+ getDBConnection() {
65
+ var application = this.getApplication();
66
+ return application.getDBConnection();
67
+ }
68
+
69
+ throwNotFoundError(message) {
70
+ throw new HTTPErrors.HTTPNotFoundError(this.route, message);
71
+ }
72
+
73
+ throwBadRequestError(message) {
74
+ throw new HTTPErrors.HTTPBadRequestError(this.route, message);
75
+ }
76
+
77
+ throwUnauthorizedError(message) {
78
+ throw new HTTPErrors.HTTPUnauthorizedError(this.route, message);
79
+ }
80
+
81
+ throwInternalServerError(message) {
82
+ throw new HTTPErrors.HTTPInternalServerError(this.route, message);
83
+ }
84
+
85
+ async handleIncomingRequest(request, response, { route, controller, controllerMethod, controllerInstance, startTime, params, query }) {
86
+ this.route = route;
87
+
88
+ return await this[controllerMethod].call(this, { params, query, body: request.body, request, response, startTime }, this.getModels());
59
89
  }
60
90
 
61
91
  async handleOutgoingResponse(_controllerResult, request, response, { route, controller, controllerMethod, controllerInstance, startTime, params }) {
@@ -74,6 +104,7 @@ class ControllerBase {
74
104
  if (controllerResult == null)
75
105
  controllerResult = {};
76
106
 
107
+ response.header('Content-Type', 'application/json; charset=UTF-8');
77
108
  response.status(200).send(JSON.stringify(controllerResult));
78
109
  }
79
110
  }
@@ -20,8 +20,9 @@ const ROUTE_PROPERTIES = [
20
20
  'accept',
21
21
  'controller',
22
22
  'methods',
23
- 'middleWare',
23
+ 'middleware',
24
24
  'priority',
25
+ 'queryParams',
25
26
  ];
26
27
 
27
28
  function buildPatternMatcher(_patterns, _opts) {
@@ -281,6 +282,26 @@ function getRouteProperties(route) {
281
282
  function compileRoutes(routes, customParserTypes, _context) {
282
283
  const sortRoutes = (routes) => {
283
284
  return routes.sort((a, b) => {
285
+ var pathA = a.path;
286
+ var pathB = b.path;
287
+
288
+ if (Nife.arrayUnion(a.methods, b.methods).length > 0) {
289
+ // If the routes are at the same level
290
+ // but one of them has a capture parameter
291
+ // then the one without a capture parameter comes first.
292
+ // If we don't do this, then the capture parameter wild-card
293
+ // might match the route name of the non-parameter route
294
+ var lastForwardSlashIndexA = pathA.lastIndexOf('/');
295
+ var lastForwardSlashIndexB = pathB.lastIndexOf('/');
296
+
297
+ if (lastForwardSlashIndexA >= 0 && lastForwardSlashIndexB >= 0 && pathA.substring(0, lastForwardSlashIndexA) === pathB.substring(0, lastForwardSlashIndexB)) {
298
+ if (pathA.indexOf('<', lastForwardSlashIndexA) >= 0 && pathB.indexOf('<', lastForwardSlashIndexB) < 0)
299
+ return 1;
300
+ else if (pathA.indexOf('<', lastForwardSlashIndexA) < 0 && pathB.indexOf('<', lastForwardSlashIndexB) >= 0)
301
+ return -1;
302
+ }
303
+ }
304
+
284
305
  var x = a.priority;
285
306
  var y = b.priority;
286
307
 
@@ -349,9 +370,6 @@ function compileRoutes(routes, customParserTypes, _context) {
349
370
  if (!path)
350
371
  path = '/';
351
372
 
352
- if (routeName !== '/')
353
- path = `${(path)}/${routeName}`;
354
-
355
373
  path = path.replace(/\/{2,}/g, '/');
356
374
 
357
375
  var keys = Object.keys(routes);
@@ -370,10 +388,12 @@ function compileRoutes(routes, customParserTypes, _context) {
370
388
  continue;
371
389
 
372
390
  var thisRouteName = (isArray) ? routeName : key;
391
+ var newPath = (isArray) ? path : `${path}/${key}`;
392
+
373
393
  if (Nife.instanceOf(route, 'object', 'array')) {
374
394
  var subRoutes = compileRoutes(route, customParserTypes, {
375
395
  routeName: thisRouteName,
376
- path: path,
396
+ path: newPath,
377
397
  priority: i,
378
398
  depth: depth + 1,
379
399
  alreadyVisited,
@@ -25,10 +25,19 @@ class HTTPBadRequestError extends HTTPBaseError {
25
25
  constructor(route, message) {
26
26
  super(route, message, 400);
27
27
  }
28
+ }
29
+
30
+ class HTTPBadContentTypeError extends HTTPBaseError {
31
+ constructor(route, message) {
32
+ super(route, message, 400);
33
+ }
28
34
 
29
35
  getMessage() {
30
- var route = this.route;
31
- var accept = route.accept;
36
+ var route = this.route;
37
+ if (!route)
38
+ return this.message;
39
+
40
+ var accept = route.accept;
32
41
  if (!(accept instanceof Array))
33
42
  accept = [ accept ];
34
43
 
@@ -43,7 +52,10 @@ class HTTPBadRequestError extends HTTPBaseError {
43
52
  return `'${part}'`;
44
53
  });
45
54
 
46
- return `${this.message}: Accepted Content-Types are [ ${accept.join(', ')} ]`;
55
+ if (this.message)
56
+ return `${this.message}: Accepted Content-Types are [ ${accept.join(', ')} ]`;
57
+ else
58
+ return `Accepted Content-Types are [ ${accept.join(', ')} ]`;
47
59
  }
48
60
  }
49
61
 
@@ -63,6 +75,7 @@ module.exports = {
63
75
  HTTPBaseError,
64
76
  HTTPNotFoundError,
65
77
  HTTPBadRequestError,
78
+ HTTPBadContentTypeError,
66
79
  HTTPUnauthorizedError,
67
80
  HTTPInternalServerError,
68
81
  };
@@ -12,6 +12,7 @@ const {
12
12
  HTTPBaseError,
13
13
  HTTPNotFoundError,
14
14
  HTTPBadRequestError,
15
+ HTTPBadContentTypeError,
15
16
  HTTPInternalServerError,
16
17
  } = require('./http-errors');
17
18
 
@@ -23,7 +24,7 @@ class HTTPServer {
23
24
  constructor(application, _opts) {
24
25
  var appName = application.getApplicationName();
25
26
 
26
- var uploadPath = Path.resolve(OS.tmpdir(), 'appName', ('' + process.pid));
27
+ var uploadPath = Path.resolve(OS.tmpdir(), appName.replace(/[^\w-]/g, ''), ('' + process.pid));
27
28
 
28
29
  var opts = Nife.extend(true, {
29
30
  host: 'localhost',
@@ -102,46 +103,67 @@ class HTTPServer {
102
103
  this.routes = routes;
103
104
  }
104
105
 
105
- baseMiddleware(request, response, rootNext) {
106
- var middleware = this.middleware;
107
- if (!middleware || !middleware.length)
108
- return rootNext.call(this);
106
+ executeMiddleware(middleware, request, response) {
107
+ return new Promise((resolve, reject) => {
108
+ if (Nife.isEmpty(middleware))
109
+ return resolve();
109
110
 
110
- var application = this.getApplication();
111
- request.mythixApplication = application;
111
+ var application = this.getApplication();
112
+ if (!request.mythixApplication)
113
+ request.mythixApplication = application;
112
114
 
113
- var logger = request.mythixLogger = this.createRequestLogger(application, request);
115
+ var logger = request.mythixLogger;
116
+ if (!logger)
117
+ logger = request.mythixLogger = this.createRequestLogger(application, request);
114
118
 
115
- request.Sequelize = Sequelize;
119
+ if (!request.Sequelize)
120
+ request.Sequelize = Sequelize;
116
121
 
117
- var middlewareIndex = 0;
118
- const next = async () => {
119
- if (middlewareIndex >= middleware.length)
120
- return rootNext();
122
+ var middlewareIndex = 0;
123
+ const next = async () => {
124
+ if (middlewareIndex >= middleware.length)
125
+ return resolve();
121
126
 
122
- var middlewareFunc = middleware[middlewareIndex++];
127
+ var middlewareFunc = middleware[middlewareIndex++];
123
128
 
124
- try {
125
- await middlewareFunc.call(this, request, response, next);
126
- } catch (error) {
127
- var statusCode = error.statusCode || error.status_code || 500;
129
+ try {
130
+ await middlewareFunc.call(this, request, response, next);
131
+ } catch (error) {
132
+ var statusCode = error.statusCode || error.status_code || 500;
128
133
 
129
- if (error instanceof HTTPBaseError) {
130
- logger.log(`Error: ${statusCode} ${statusCodeToMessage(statusCode)}`);
131
- this.errorHandler(error.getMessage(), statusCode, response, request);
132
- } else {
133
- if (statusCode) {
134
+ if (error instanceof HTTPBaseError) {
134
135
  logger.log(`Error: ${statusCode} ${statusCodeToMessage(statusCode)}`);
135
- this.errorHandler(error.message, statusCode, response, request);
136
+ this.errorHandler(error.getMessage(), statusCode, response, request);
136
137
  } else {
137
- logger.log(`Error: ${error.message}`, error);
138
- this.errorHandler(error.message, 500, response, request);
138
+ if (statusCode) {
139
+ logger.log(`Error: ${statusCode} ${statusCodeToMessage(statusCode)}`);
140
+ this.errorHandler(error.message, statusCode, response, request);
141
+ } else {
142
+ logger.log(`Error: ${error.message}`, error);
143
+ this.errorHandler(error.message, 500, response, request);
144
+ }
139
145
  }
146
+
147
+ reject(error);
140
148
  }
141
- }
142
- };
149
+ };
143
150
 
144
- next();
151
+ next().catch(reject);
152
+ });
153
+ }
154
+
155
+ baseMiddleware(request, response, rootNext) {
156
+ var middleware = this.middleware;
157
+ if (Nife.isEmpty(middleware))
158
+ return rootNext();
159
+
160
+ this.executeMiddleware(middleware, request, response).then(
161
+ () => rootNext(),
162
+ (error) => {
163
+ if (!(error instanceof HTTPBaseError))
164
+ this.getApplication().error('Error in middleware: ', error);
165
+ }
166
+ );
145
167
  }
146
168
 
147
169
  findFirstMatchingRoute(request, _routes) {
@@ -160,7 +182,7 @@ class HTTPServer {
160
182
  return;
161
183
 
162
184
  if (typeof contentTypeMatcher === 'function' && !contentTypeMatcher(contentType))
163
- throw new HTTPBadRequestError(route);
185
+ throw new HTTPBadContentTypeError(route);
164
186
 
165
187
  return result;
166
188
  };
@@ -168,7 +190,7 @@ class HTTPServer {
168
190
  var routes = _routes || [];
169
191
  var method = request.method;
170
192
  var contentType = Nife.get(request, 'headers.content-type');
171
- var path = request.url;
193
+ var path = request.path;
172
194
 
173
195
  for (var i = 0, il = routes.length; i < il; i++) {
174
196
  var route = routes[i];
@@ -207,26 +229,81 @@ class HTTPServer {
207
229
 
208
230
  var logger = application.getLogger();
209
231
  var loggerMethod = ('' + request.method).toUpperCase();
210
- var loggerURL = ('' + request.url);
232
+ var loggerURL = ('' + request.path);
211
233
  var requestID = (Date.now() + Math.random()).toFixed(4);
212
234
  var ipAddress = Nife.get(request, 'client.remoteAddress', '<unknown IP address>');
213
235
 
214
236
  return logger.clone({ formatter: (output) => `{${ipAddress}} - [#${requestID} ${loggerMethod} ${loggerURL}]: ${output}`});
215
237
  }
216
238
 
217
- sendRequestToController(...args) {
239
+ validateQueryParam(route, query, paramName, queryValue, queryParams) {
240
+ var { validate } = queryParams;
241
+ if (!validate)
242
+ return true;
243
+
244
+ if (validate instanceof RegExp)
245
+ return !!('' + queryValue).match(validate);
246
+ else if (typeof validate === 'function')
247
+ return !!validate.call(route, queryValue, paramName, query, queryParams);
248
+
249
+ return true;
250
+ }
251
+
252
+ compileQueryParams(route, query, queryParams) {
253
+ var finalQuery = Object.assign({}, query || {});
254
+
255
+ var paramNames = Object.keys(queryParams || {});
256
+ for (var i = 0, il = paramNames.length; i < il; i++) {
257
+ var paramName = paramNames[i];
258
+ var queryParam = queryParams[paramName];
259
+ if (!queryParam)
260
+ continue;
261
+
262
+ var queryValue = finalQuery[paramName];
263
+ if (queryValue == null) {
264
+ if (queryParam.required)
265
+ throw new HTTPBadRequestError(route, `Query param "${paramName}" is required`);
266
+
267
+ if (queryParam.hasOwnProperty('defaultValue'))
268
+ finalQuery[paramName] = queryParam['defaultValue'];
269
+ } else {
270
+ if (!this.validateQueryParam(route, finalQuery, paramName, queryValue, queryParam))
271
+ throw new HTTPBadRequestError(route, `Query param "${paramName}" is invalid`);
272
+
273
+ if (queryParam.hasOwnProperty('type'))
274
+ finalQuery[paramName] = Nife.coerceValue(queryValue, queryParam['type']);
275
+ }
276
+ }
277
+
278
+ return finalQuery;
279
+ }
280
+
281
+ async sendRequestToController(...args) {
218
282
  var context = args[2];
219
283
  var controllerInstance = context.controllerInstance;
220
284
 
221
- return controllerInstance.handleIncomingRequest.apply(controllerInstance, args);
285
+ // Compile query params
286
+ context.query = this.compileQueryParams(context.route, context.query, (context.route && context.route.queryParams));
287
+
288
+ var route = context.route;
289
+
290
+ // Execute middleware if any exists
291
+ var middleware = (typeof controllerInstance.getMiddleware === 'function') ? controllerInstance.getMiddleware.call(controllerInstance, context) : [];
292
+ if (route && Nife.instanceOf(route.middleware, 'array') && Nife.isNotEmpty(route.middleware))
293
+ middleware = route.middleware.concat((middleware) ? middleware : []);
294
+
295
+ if (Nife.isNotEmpty(middleware))
296
+ await this.executeMiddleware(middleware, request, response);
297
+
298
+ return await controllerInstance.handleIncomingRequest.apply(controllerInstance, args);
222
299
  }
223
300
 
224
301
  async baseRouter(request, response, next) {
225
- var startTime = Nife.now();
302
+ var startTime = Nife.now();
303
+ var application = this.getApplication();
226
304
 
227
305
  try {
228
- var application = this.getApplication();
229
- var logger = this.createRequestLogger(application, request, { controller, controllerMethod });
306
+ var logger = this.createRequestLogger(application, request, { controller, controllerMethod });
230
307
 
231
308
  logger.log(`Starting request`);
232
309
 
@@ -250,6 +327,7 @@ class HTTPServer {
250
327
 
251
328
  var context = {
252
329
  params: request.params,
330
+ query: request.query,
253
331
  route,
254
332
  controller,
255
333
  controllerMethod,
@@ -259,13 +337,18 @@ class HTTPServer {
259
337
 
260
338
  var controllerResult = await this.sendRequestToController(request, response, context);
261
339
 
262
- if (!response.finished)
340
+ if (!(response.finished || response.statusMessage))
263
341
  await controllerInstance.handleOutgoingResponse(controllerResult, request, response, context);
342
+ else if (!response.finished)
343
+ response.end();
264
344
 
265
345
  var statusCode = response.statusCode || 200;
266
346
  var requestTime = Nife.now() - startTime;
267
347
  logger.log(`Completed request in ${requestTime.toFixed(3)}ms: ${statusCode} ${response.statusMessage || statusCodeToMessage(statusCode)}`);
268
348
  } catch (error) {
349
+ if ((error instanceof HTTPInternalServerError || !(error instanceof HTTPBaseError)) && application.getOptions().testMode)
350
+ console.error(error);
351
+
269
352
  try {
270
353
  var statusCode = error.statusCode || error.status_code || 500;
271
354
  var requestTime = Nife.now() - startTime;
@@ -305,14 +388,15 @@ class HTTPServer {
305
388
  }
306
389
 
307
390
  async start() {
308
- var options = this.getOptions();
309
- var app = this.createExpressApplication(options);
391
+ var options = this.getOptions();
392
+ var app = this.createExpressApplication(options);
393
+ var portString = (options.port) ? `:${options.port}` : '';
310
394
  var server;
311
395
 
312
396
  app.use(this.baseMiddleware.bind(this));
313
397
  app.all('*', this.baseRouter.bind(this));
314
398
 
315
- this.getLogger().log(`Starting ${(options.https) ? 'HTTPS' : 'HTTP'} server ${(options.https) ? 'https' : 'http'}://${options.host}:${options.port}...`);
399
+ this.getLogger().log(`Starting ${(options.https) ? 'HTTPS' : 'HTTP'} server ${(options.https) ? 'https' : 'http'}://${options.host}${portString}...`);
316
400
 
317
401
  if (options.https) {
318
402
  var credentials = await this.getHTTPSCredentials(options.https);
@@ -325,7 +409,8 @@ class HTTPServer {
325
409
 
326
410
  this.server = server;
327
411
 
328
- this.getLogger().log(`Web server listening at ${(options.https) ? 'https' : 'http'}://${options.host}:${options.port}`);
412
+ var listeningPort = server.address().port;
413
+ this.getLogger().log(`Web server listening at ${(options.https) ? 'https' : 'http'}://${options.host}:${listeningPort}`);
329
414
 
330
415
  return server;
331
416
  }