alchemymvc 1.3.15 → 1.3.17

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.
@@ -490,9 +490,9 @@ Conduit.setMethod(function time() {
490
490
  /**
491
491
  * Parse the request, get information from the url
492
492
  *
493
- * @author Jelle De Loecker <jelle@develry.be>
493
+ * @author Jelle De Loecker <jelle@elevenways.be>
494
494
  * @since 0.2.0
495
- * @version 1.2.5
495
+ * @version 1.3.17
496
496
  *
497
497
  * @param {IncomingMessage} req
498
498
  * @param {ServerResponse} res
@@ -555,6 +555,11 @@ Conduit.setMethod(async function parseRequest() {
555
555
  // Set the host
556
556
  this.url.hostname = this.headers.host;
557
557
 
558
+ // If no URL was set, use the first requests URL (without the path)
559
+ if (!alchemy.settings.url && this.headers.host) {
560
+ alchemy.settings.url = this.url+'';
561
+ }
562
+
558
563
  let path = this.path;
559
564
 
560
565
  if (this.prefix) {
@@ -569,9 +574,9 @@ Conduit.setMethod(async function parseRequest() {
569
574
  /**
570
575
  * Parse the headers for shortcuts
571
576
  *
572
- * @author Jelle De Loecker <jelle@develry.be>
577
+ * @author Jelle De Loecker <jelle@elevenways.be>
573
578
  * @since 0.2.0
574
- * @version 0.2.0
579
+ * @version 1.3.17
575
580
  */
576
581
  Conduit.setMethod(function parseShortcuts() {
577
582
 
@@ -597,6 +602,10 @@ Conduit.setMethod(function parseShortcuts() {
597
602
  this.ajax = headers['x-requested-with'] === 'XMLHttpRequest';
598
603
  }
599
604
 
605
+ // Is this request coming from a webview?
606
+ if (this.in_webview == null) {
607
+ this.in_webview = headers['x-protoblast-webview'] === 'true';
608
+ }
600
609
  });
601
610
 
602
611
  /**
@@ -1161,6 +1170,8 @@ Conduit.setMethod(async function callMiddleware() {
1161
1170
  middleDebug.mark('Preparing viewrender');
1162
1171
  }
1163
1172
 
1173
+ // An action will be called,
1174
+ // so we can already prepare the viewrender now.
1164
1175
  that.prepareViewRender();
1165
1176
 
1166
1177
  if (middleDebug) {
@@ -1184,10 +1195,14 @@ Conduit.setMethod(async function callMiddleware() {
1184
1195
  *
1185
1196
  * @author Jelle De Loecker <jelle@develry.be>
1186
1197
  * @since 0.2.0
1187
- * @version 1.1.1
1198
+ * @version 1.3.17
1188
1199
  */
1189
1200
  Conduit.setMethod(function prepareViewRender() {
1190
1201
 
1202
+ if (this.renderer.conduit) {
1203
+ return;
1204
+ }
1205
+
1191
1206
  // Add a link to this conduit
1192
1207
  this.renderer.conduit = this;
1193
1208
  this.renderer.server_var('conduit', this);
@@ -1211,7 +1226,7 @@ Conduit.setMethod(function prepareViewRender() {
1211
1226
  *
1212
1227
  * @author Jelle De Loecker <jelle@develry.be>
1213
1228
  * @since 0.2.0
1214
- * @version 1.1.7
1229
+ * @version 1.3.16
1215
1230
  */
1216
1231
  Conduit.setMethod(function callHandler() {
1217
1232
 
@@ -1226,7 +1241,7 @@ Conduit.setMethod(function callHandler() {
1226
1241
  this.error(405, 'Method Not Allowed', false);
1227
1242
 
1228
1243
  } else {
1229
- if (alchemy.settings.debug) {
1244
+ if (alchemy.settings.debug && this.original_path.indexOf('.js.map') == -1) {
1230
1245
  console.log('Route not found:', this);
1231
1246
  }
1232
1247
 
@@ -1410,7 +1425,7 @@ Conduit.setMethod(function setResponseUrl(new_url) {
1410
1425
  *
1411
1426
  * @author Jelle De Loecker <jelle@elevenways.be>
1412
1427
  * @since 0.2.0
1413
- * @version 1.3.6
1428
+ * @version 1.3.17
1414
1429
  *
1415
1430
  * @param {Number} status 3xx redirection codes. 302 (temporary redirect) by default
1416
1431
  * @param {String|Object} options Options or url
@@ -1508,13 +1523,18 @@ Conduit.setMethod(function redirect(status, options) {
1508
1523
  }
1509
1524
  }
1510
1525
 
1511
- if (hard_refresh && this.headers['x-hawkejs-request']) {
1512
- this.setHeader('x-hawkejs-navigate', url);
1526
+ if (options?.popup) {
1527
+ this.setHeader('x-hawkejs-popup', options.popup);
1513
1528
 
1514
- if (options && options.popup) {
1515
- this.setHeader('x-hawkejs-popup', options.popup);
1529
+ if (options.popup_url) {
1530
+ this.setHeader('x-hawkejs-popup-url', options.popup_url);
1516
1531
  }
1517
1532
 
1533
+ hard_refresh = true;
1534
+ }
1535
+
1536
+ if (hard_refresh && this.headers['x-hawkejs-request']) {
1537
+ this.setHeader('x-hawkejs-navigate', url);
1518
1538
  } else {
1519
1539
  this.setHeader('Location', url);
1520
1540
  }
@@ -1527,7 +1547,7 @@ Conduit.setMethod(function redirect(status, options) {
1527
1547
  *
1528
1548
  * @author Jelle De Loecker <jelle@elevenways.be>
1529
1549
  * @since 0.2.0
1530
- * @version 1.3.4
1550
+ * @version 1.3.16
1531
1551
  *
1532
1552
  * @param {Nulber} status Response statuscode
1533
1553
  * @param {Error} message Optional error to send
@@ -1561,6 +1581,11 @@ Conduit.setMethod(function error(status, message, print_error) {
1561
1581
 
1562
1582
  if (alchemy.settings.environment == 'dev') {
1563
1583
  print_dev = true;
1584
+
1585
+ if (is_400 && this.original_path.indexOf('.js.map') > -1) {
1586
+ print_dev = false;
1587
+ print_error = false;
1588
+ }
1564
1589
  }
1565
1590
 
1566
1591
  if (print_error == null) {
@@ -1916,11 +1941,11 @@ Conduit.setMethod(function createScene() {
1916
1941
  *
1917
1942
  * @author Jelle De Loecker <jelle@develry.be>
1918
1943
  * @since 0.2.0
1919
- * @version 1.1.0
1944
+ * @version 1.3.17
1920
1945
  */
1921
1946
  Conduit.setMethod(function render(template_name, options, callback) {
1922
1947
 
1923
- var that = this,
1948
+ let that = this,
1924
1949
  templates;
1925
1950
 
1926
1951
  if (typeof options == 'function') {
@@ -1930,6 +1955,10 @@ Conduit.setMethod(function render(template_name, options, callback) {
1930
1955
  options = {};
1931
1956
  }
1932
1957
 
1958
+ // Make sure the viewrender is prepared
1959
+ // (Fallback)
1960
+ this.prepareViewRender();
1961
+
1933
1962
  if (template_name) {
1934
1963
  templates = [template_name];
1935
1964
  }
@@ -2309,6 +2338,34 @@ Conduit.setMethod(function getSession(allow_create = true) {
2309
2338
  return session;
2310
2339
  });
2311
2340
 
2341
+ /**
2342
+ * Create a controller of the given type and attach this conduit
2343
+ *
2344
+ * @author Jelle De Loecker <jelle@elevenways.be>
2345
+ * @since 1.3.16
2346
+ * @version 1.3.16
2347
+ *
2348
+ * @param {String} controller_name
2349
+ *
2350
+ * @return {Controller}
2351
+ */
2352
+ Conduit.setMethod(function getController(controller_name) {
2353
+
2354
+ if (!controller_name) {
2355
+ return this.controller;
2356
+ }
2357
+
2358
+ let instance = Controller.get(controller_name);
2359
+
2360
+ if (!instance) {
2361
+ return false;
2362
+ }
2363
+
2364
+ instance.conduit = this;
2365
+
2366
+ return instance;
2367
+ });
2368
+
2312
2369
  /**
2313
2370
  * Register live data bindings via websockets
2314
2371
  *
@@ -2688,7 +2745,7 @@ Conduit.setMethod(function shouldBePostponed() {
2688
2745
  *
2689
2746
  * @author Jelle De Loecker <jelle@develry.be>
2690
2747
  * @since 0.3.0
2691
- * @version 0.4.0
2748
+ * @version 1.3.16
2692
2749
  *
2693
2750
  * @param {String} type
2694
2751
  * @param {Object} data
@@ -2697,18 +2754,18 @@ Alchemy.setMethod(function broadcast(type, data) {
2697
2754
 
2698
2755
  alchemy.sessions.forEach(function eachSession(session, key) {
2699
2756
 
2700
- // Go over every listening scene and submit the data
2701
- Object.each(session.connections, function eachScene(scene, scene_id) {
2757
+ // Go over every listening socket and submit the data
2758
+ Object.each(session.connections, function eachScene(socket_conduit, scene_id) {
2702
2759
 
2703
- if (!scene) {
2760
+ if (!socket_conduit?.is_connected) {
2704
2761
  return;
2705
2762
  }
2706
2763
 
2707
2764
  if (alchemy.settings.debug) {
2708
- log.debug('Broadcasting', type, {data, scene});
2765
+ log.debug('Broadcasting', type, {data, scene: socket_conduit});
2709
2766
  }
2710
2767
 
2711
- scene.submit(type, data);
2768
+ socket_conduit.submit(type, data);
2712
2769
  });
2713
2770
  });
2714
2771
  });
@@ -455,7 +455,7 @@ Datasource.setMethod(function _valueToApp(field, query, options, value, callback
455
455
  *
456
456
  * @author Jelle De Loecker <jelle@develry.be>
457
457
  * @since 0.2.0
458
- * @version 1.1.5
458
+ * @version 1.3.16
459
459
  *
460
460
  * @param {Model} model
461
461
  * @param {Criteria} criteria
@@ -540,24 +540,29 @@ Datasource.setMethod(function read(model, criteria, callback) {
540
540
  return pledge.reject(err);
541
541
  }
542
542
 
543
- let result = {
544
- items : app_results,
545
- available : available
546
- };
543
+ try {
547
544
 
548
- if (hash) {
545
+ let result = {
546
+ items : app_results,
547
+ available : available
548
+ };
549
549
 
550
- // Emit the storing_cache event
551
- model.emit('storing_cache', criteria, result);
550
+ if (hash) {
552
551
 
553
- let cloned = JSON.clone(result);
552
+ // Emit the storing_cache event
553
+ model.emit('storing_cache', criteria, result);
554
554
 
555
- model.emit('stored_cache', criteria, cloned);
555
+ let cloned = JSON.clone(result);
556
556
 
557
- cache_pledge.resolve(cloned);
558
- }
557
+ model.emit('stored_cache', criteria, cloned);
559
558
 
560
- pledge.resolve(result);
559
+ cache_pledge.resolve(cloned);
560
+ }
561
+
562
+ pledge.resolve(result);
563
+ } catch (err) {
564
+ pledge.reject(err);
565
+ }
561
566
  }
562
567
  });
563
568
 
@@ -296,12 +296,16 @@ File.setMethod(function createReadStream(options) {
296
296
  *
297
297
  * @author Jelle De Loecker <jelle@develry.be>
298
298
  * @since 1.1.0
299
- * @version 1.1.8
299
+ * @version 1.3.16
300
300
  */
301
301
  File.setMethod(function read(options) {
302
302
 
303
303
  let pledge = new Pledge();
304
304
 
305
+ if (typeof options == 'string') {
306
+ options = {encoding: options};
307
+ }
308
+
305
309
  if (!options) {
306
310
  options = {flag: 'r'};
307
311
  } else if (!options.flag) {
@@ -845,7 +845,7 @@ Model.setMethod(function ensureIds(list, callback) {
845
845
  *
846
846
  * @author Jelle De Loecker <jelle@develry.be>
847
847
  * @since 0.0.1
848
- * @version 1.2.0
848
+ * @version 1.3.16
849
849
  *
850
850
  * @param {Document} document
851
851
  * @param {Object} options
@@ -943,6 +943,11 @@ Model.setMethod(function saveRecord(document, options, callback) {
943
943
 
944
944
  Object.each(document.$record, function eachEntry(entry, key) {
945
945
 
946
+ // Skip empty entries
947
+ if (!entry) {
948
+ return;
949
+ }
950
+
946
951
  // Skip our own record
947
952
  if (key == that.name) {
948
953
  return;
@@ -957,7 +962,6 @@ Model.setMethod(function saveRecord(document, options, callback) {
957
962
  }
958
963
 
959
964
  // Add the saved _id
960
- //@TODO: this throws an error sometimes.
961
965
  entry[assoc.options.foreignKey] = saved_record[assoc.options.localKey];
962
966
 
963
967
  // Add the task
@@ -1242,6 +1246,30 @@ Model.setMethod(function nukeCache(associated, parent) {
1242
1246
  }
1243
1247
  });
1244
1248
 
1249
+ /**
1250
+ * Perform a MongoDB pipeline
1251
+ *
1252
+ * @author Jelle De Loecker <jelle@elevenways.be>
1253
+ * @since 1.3.17
1254
+ * @version 1.3.17
1255
+ *
1256
+ * @param {Array} pipeline
1257
+ *
1258
+ * @return {Promise}
1259
+ */
1260
+ Model.setMethod(async function executeMongoPipeline(pipeline) {
1261
+
1262
+ if (typeof this.datasource.collection != 'function') {
1263
+ throw new Error('The `' + this.model_name + '` model does not seem to use MongoDB, unable to perform pipeline');
1264
+ }
1265
+
1266
+ let collection = await this.datasource.collection(this.table);
1267
+ let cursor = await collection.aggregate(pipeline);
1268
+ let result = await cursor.toArray();
1269
+
1270
+ return result;
1271
+ });
1272
+
1245
1273
  /**
1246
1274
  * Delete the given record id
1247
1275
  *
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * @author Jelle De Loecker <jelle@elevenways.be>
5
5
  * @since 0.2.0
6
- * @version 1.3.1
6
+ * @version 1.3.16
7
7
  */
8
8
  const Route = Function.inherits('Alchemy.Base', function Route(router, paths, options) {
9
9
 
@@ -51,6 +51,10 @@ const Route = Function.inherits('Alchemy.Base', function Route(router, paths, op
51
51
  // Can this route's path be used in the browser's address location?
52
52
  this.visible_location = true;
53
53
 
54
+ // Is this a system route of some kind?
55
+ // (meaning: not for end users)
56
+ this.is_system_route = false;
57
+
54
58
  // If no fnc is given, these will be called
55
59
  this.controller = null;
56
60
  this.action = null;
@@ -767,7 +767,7 @@ RouterClass.setMethod(function setOption(name, value) {
767
767
  *
768
768
  * @author Jelle De Loecker <jelle@elevenways.be>
769
769
  * @since 0.2.0
770
- * @version 1.3.1
770
+ * @version 1.3.16
771
771
  *
772
772
  * @param {Object} args
773
773
  * @param {String} args.name Optional route name
@@ -824,6 +824,10 @@ RouterClass.setMethod(function add(args) {
824
824
  route.visible_location = args.visible_location;
825
825
  }
826
826
 
827
+ if (args.is_system_route != null) {
828
+ route.is_system_route = args.is_system_route;
829
+ }
830
+
827
831
  if (args.permission) {
828
832
  route.setPermission(args.permission);
829
833
  }
@@ -1163,7 +1167,7 @@ RouterClass.setMethod(function getOptions(result) {
1163
1167
  *
1164
1168
  * @author Jelle De Loecker <jelle@elevenways.be>
1165
1169
  * @since 0.2.0
1166
- * @version 1.3.7
1170
+ * @version 1.3.16
1167
1171
  *
1168
1172
  * @param {Object} result Optional object to store sectioned results in
1169
1173
  *
@@ -1212,6 +1216,7 @@ RouterClass.setMethod(function getRoutes(result) {
1212
1216
  has_permission_assignments : route.has_permission_assignments || undefined,
1213
1217
  title : route.title,
1214
1218
  visible_location : route.visible_location,
1219
+ is_system_route : route.is_system_route,
1215
1220
  schema : route.schema,
1216
1221
  param_definitions : route.param_definitions,
1217
1222
  };
@@ -892,7 +892,7 @@ Schema.setMethod(function getFieldNames() {
892
892
  *
893
893
  * @author Jelle De Loecker <jelle@develry.be>
894
894
  * @since 0.2.0
895
- * @version 1.3.0
895
+ * @version 1.3.17
896
896
  *
897
897
  * @param {String|FieldType} _field
898
898
  * @param {Object} options
@@ -901,24 +901,19 @@ Schema.setMethod(function getFieldNames() {
901
901
  */
902
902
  Schema.setMethod(function addIndex(_field, _options) {
903
903
 
904
- var that = this,
905
- datasource,
906
- options,
907
- order,
908
- field,
909
- path;
910
-
911
904
  // `Schema` is the `Client.Schema` class in this scope
912
905
  if (Blast.isNode && this.constructor == Schema) {
913
906
  return;
914
907
  }
915
908
 
916
- field = this.getField(_field);
909
+ let field = this.getField(_field);
917
910
 
918
911
  if (!field) {
919
912
  throw new Error('Could not find field "' + _field + '"');
920
913
  }
921
914
 
915
+ let options;
916
+
922
917
  if (typeof _options === 'string') {
923
918
  options = {};
924
919
  options[_options] = true;
@@ -961,6 +956,8 @@ Schema.setMethod(function addIndex(_field, _options) {
961
956
  this.has_alternates++;
962
957
  }
963
958
 
959
+ const that = this;
960
+
964
961
  that.getDatasource().done(function gotDs(err, datasource) {
965
962
 
966
963
  if (err) {
@@ -977,7 +974,7 @@ Schema.setMethod(function addIndex(_field, _options) {
977
974
  return alchemy.printLog('error', ['Unable to ensure index on this datasource', options.name], {err: new Error()});
978
975
  }
979
976
 
980
- path = field.path;
977
+ let path = field.path;
981
978
 
982
979
  if (options.db_property) {
983
980
  path += '.' + options.db_property;
@@ -990,7 +987,7 @@ Schema.setMethod(function addIndex(_field, _options) {
990
987
  datasource.ensureIndex(that.model_class, that.indexes[options.name], function ensuredIndex(err, result) {
991
988
 
992
989
  if (err) {
993
- alchemy.printLog('error', ['Error ensuring index', options.name], {err: err});
990
+ alchemy.printLog('error', ['Error ensuring index', options.name, 'in model', that.model_name], {err: err});
994
991
  }
995
992
  });
996
993
  });
@@ -63,7 +63,7 @@ var Session = Function.inherits('Alchemy.Base', function ClientSession(conduit)
63
63
  *
64
64
  * @author Jelle De Loecker <jelle@develry.be>
65
65
  * @since 1.1.0
66
- * @version 1.1.0
66
+ * @version 1.3.17
67
67
  *
68
68
  * @type {Number}
69
69
  */
@@ -75,6 +75,10 @@ Session.enforceProperty(function request_count(amount) {
75
75
 
76
76
  this.last_activity_date = new Date();
77
77
 
78
+ if (this.menu_item) {
79
+ this.menu_item.setWeight(this.last_activity_date.getTime());
80
+ }
81
+
78
82
  return amount;
79
83
  });
80
84
 
@@ -167,7 +171,7 @@ Session.setMethod(function hasAlreadyQueued() {
167
171
  *
168
172
  * @author Jelle De Loecker <jelle@develry.be>
169
173
  * @since 0.2.0
170
- * @version 0.4.0
174
+ * @version 1.3.17
171
175
  *
172
176
  * @param {Conduit} conduit The conduit to postpone
173
177
  *
@@ -187,7 +191,14 @@ Session.setMethod(function createMenuItem(conduit) {
187
191
 
188
192
  browser += ': ' + conduit.ip;
189
193
 
190
- this.menu_item = alchemy.Janeway.session_menu.addItem(browser);
194
+ let options = {
195
+ title : browser,
196
+ weight : this.last_activity_date.getTime(),
197
+ };
198
+
199
+ this.menu_item = alchemy.Janeway.session_menu.addItem(options, () => {
200
+ console.log('Clicked on session', this, this.id);
201
+ });
191
202
  });
192
203
 
193
204
  /**