alchemymvc 1.4.0-alpha.5 → 1.4.0-alpha.7
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.
|
@@ -131,7 +131,11 @@ Mongo.setMethod(function castToBigInt(value) {
|
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
if (typeof value == 'object') {
|
|
134
|
-
|
|
134
|
+
if (value.toBigInt) {
|
|
135
|
+
return value.toBigInt();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return null;
|
|
135
139
|
}
|
|
136
140
|
|
|
137
141
|
return BigInt(value);
|
|
@@ -487,9 +491,21 @@ Mongo.setMethod(function _create(context) {
|
|
|
487
491
|
this.collection(model.table),
|
|
488
492
|
collection => {
|
|
489
493
|
|
|
490
|
-
const
|
|
494
|
+
const converted_data = context.getConvertedData();
|
|
491
495
|
const pledge = new Swift();
|
|
492
496
|
|
|
497
|
+
let data = {};
|
|
498
|
+
|
|
499
|
+
for (let key in converted_data) {
|
|
500
|
+
let val = converted_data[key];
|
|
501
|
+
|
|
502
|
+
if (val == null) {
|
|
503
|
+
continue;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
data[key] = val;
|
|
507
|
+
}
|
|
508
|
+
|
|
493
509
|
Pledge.done(collection.insertOne(data, {w: 1, fullResult: true}), function afterInsert(err, result) {
|
|
494
510
|
|
|
495
511
|
// Clear the cache
|
|
@@ -541,110 +557,125 @@ Mongo.setMethod(function _create(context) {
|
|
|
541
557
|
*/
|
|
542
558
|
Mongo.setMethod(function _update(context) {
|
|
543
559
|
|
|
544
|
-
const model = context.getModel()
|
|
545
|
-
options = context.getSaveOptions();
|
|
560
|
+
const model = context.getModel();
|
|
546
561
|
|
|
547
562
|
return Swift.waterfall(
|
|
548
563
|
this.collection(model.table),
|
|
549
564
|
collection => {
|
|
565
|
+
return performUpdate(collection, model, context);
|
|
566
|
+
}
|
|
567
|
+
);
|
|
568
|
+
});
|
|
550
569
|
|
|
551
|
-
|
|
570
|
+
/**
|
|
571
|
+
* Perform the update
|
|
572
|
+
*
|
|
573
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
574
|
+
* @since 1.4.0
|
|
575
|
+
* @version 1.4.0
|
|
576
|
+
*
|
|
577
|
+
* @return {Pledge}
|
|
578
|
+
*/
|
|
579
|
+
const performUpdate = (collection, model, context) => {
|
|
552
580
|
|
|
553
|
-
|
|
554
|
-
const data = context.getConvertedData();
|
|
581
|
+
const options = context.getSaveOptions();
|
|
555
582
|
|
|
556
|
-
|
|
557
|
-
let id = data._id;
|
|
583
|
+
let key;
|
|
558
584
|
|
|
559
|
-
|
|
560
|
-
|
|
585
|
+
// Get the converted data
|
|
586
|
+
const data = context.getConvertedData();
|
|
561
587
|
|
|
562
|
-
|
|
563
|
-
|
|
588
|
+
// Get the id to update, it should always be inside the given data
|
|
589
|
+
let id = data._id;
|
|
564
590
|
|
|
565
|
-
|
|
566
|
-
|
|
591
|
+
// Clone the data object
|
|
592
|
+
let doc = {...data};
|
|
567
593
|
|
|
568
|
-
|
|
569
|
-
|
|
594
|
+
// Values that will get flattened
|
|
595
|
+
let to_flatten = {};
|
|
570
596
|
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
}
|
|
597
|
+
// Field names that won't get flattened
|
|
598
|
+
let no_flatten = {};
|
|
574
599
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
let field = model.getField(key);
|
|
600
|
+
// Remove the id
|
|
601
|
+
delete doc._id;
|
|
578
602
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
to_flatten[key] = doc[key];
|
|
583
|
-
}
|
|
584
|
-
}
|
|
603
|
+
if (!options.override_created) {
|
|
604
|
+
delete doc.created;
|
|
605
|
+
}
|
|
585
606
|
|
|
586
|
-
|
|
587
|
-
|
|
607
|
+
// Iterate over the fields
|
|
608
|
+
for (key in doc) {
|
|
609
|
+
let field = model.getField(key);
|
|
588
610
|
|
|
589
|
-
|
|
590
|
-
|
|
611
|
+
if (field && (field.is_self_contained || field.is_translatable || typeof doc[key] == 'object')) {
|
|
612
|
+
no_flatten[key] = doc[key];
|
|
613
|
+
} else {
|
|
614
|
+
to_flatten[key] = doc[key];
|
|
615
|
+
}
|
|
616
|
+
}
|
|
591
617
|
|
|
592
|
-
|
|
618
|
+
// Flatten the object, using periods & NOT flattening arrays
|
|
619
|
+
let flat = Object.flatten(to_flatten, '.', false);
|
|
593
620
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
// We can't set null, because that could interfere with dot notation updates
|
|
597
|
-
if (flat[key] == null) {
|
|
621
|
+
// Assign the no-flatten values, too
|
|
622
|
+
Object.assign(flat, no_flatten);
|
|
598
623
|
|
|
599
|
-
|
|
600
|
-
unset[key] = '';
|
|
624
|
+
let unset = {};
|
|
601
625
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
626
|
+
for (key in flat) {
|
|
627
|
+
// Undefined or null means we want to delete the value
|
|
628
|
+
// We can't set null, because that could interfere with dot notation updates
|
|
629
|
+
if (flat[key] == null) {
|
|
606
630
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
};
|
|
631
|
+
// Add the key to the unset object
|
|
632
|
+
unset[key] = '';
|
|
610
633
|
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
634
|
+
// Remove it from the flat object
|
|
635
|
+
delete flat[key];
|
|
636
|
+
}
|
|
637
|
+
}
|
|
614
638
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
639
|
+
let update_object = {
|
|
640
|
+
$set: flat
|
|
641
|
+
};
|
|
618
642
|
|
|
619
|
-
|
|
643
|
+
if (!Object.isEmpty(unset)) {
|
|
644
|
+
update_object.$unset = unset;
|
|
645
|
+
}
|
|
620
646
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
promise = collection.findAndModify({_id: id}, [['_id', 1]], update_object, {upsert: true});
|
|
625
|
-
} else {
|
|
626
|
-
// If it's not available (like nedb)
|
|
627
|
-
promise = collection.update({_id: ''+id}, update_object, {upsert: true});
|
|
628
|
-
}
|
|
647
|
+
if (options.debug) {
|
|
648
|
+
console.log('Updating with obj', id, update_object);
|
|
649
|
+
}
|
|
629
650
|
|
|
630
|
-
|
|
651
|
+
let promise;
|
|
631
652
|
|
|
632
|
-
|
|
653
|
+
if (collection.findOneAndUpdate) {
|
|
654
|
+
promise = collection.findOneAndUpdate({_id: id}, update_object, {upsert: true});
|
|
655
|
+
} else if (collection.findAndModify) {
|
|
656
|
+
promise = collection.findAndModify({_id: id}, [['_id', 1]], update_object, {upsert: true});
|
|
657
|
+
} else {
|
|
658
|
+
// If it's not available (like nedb)
|
|
659
|
+
promise = collection.update({_id: ''+id}, update_object, {upsert: true});
|
|
660
|
+
}
|
|
633
661
|
|
|
634
|
-
|
|
635
|
-
model.nukeCache();
|
|
662
|
+
let pledge = new Swift();
|
|
636
663
|
|
|
637
|
-
|
|
638
|
-
return pledge.reject(err);
|
|
639
|
-
}
|
|
664
|
+
Pledge.done(promise, function afterUpdate(err, result) {
|
|
640
665
|
|
|
641
|
-
|
|
642
|
-
|
|
666
|
+
// Clear the cache
|
|
667
|
+
model.nukeCache();
|
|
643
668
|
|
|
644
|
-
|
|
669
|
+
if (err != null) {
|
|
670
|
+
return pledge.reject(err);
|
|
645
671
|
}
|
|
646
|
-
|
|
647
|
-
});
|
|
672
|
+
|
|
673
|
+
pledge.resolve(Object.assign({}, data));
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
return pledge;
|
|
677
|
+
|
|
678
|
+
};
|
|
648
679
|
|
|
649
680
|
/**
|
|
650
681
|
* Remove a record from the database
|
package/lib/core/alchemy.js
CHANGED
|
@@ -1745,12 +1745,43 @@ Alchemy.setMethod(function addAppcacheEntry(entry) {
|
|
|
1745
1745
|
ac_entries[entry.type].push(entry);
|
|
1746
1746
|
});
|
|
1747
1747
|
|
|
1748
|
+
/**
|
|
1749
|
+
* Get a size limit for the given route
|
|
1750
|
+
*
|
|
1751
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
1752
|
+
* @since 1.4.0
|
|
1753
|
+
* @version 1.4.0
|
|
1754
|
+
*
|
|
1755
|
+
* @param {Route?} route
|
|
1756
|
+
* @param {string} name
|
|
1757
|
+
*/
|
|
1758
|
+
function getRouteSizeLimit(route, name) {
|
|
1759
|
+
|
|
1760
|
+
let global_size = alchemy.settings.network[name];
|
|
1761
|
+
|
|
1762
|
+
if (!route) {
|
|
1763
|
+
return global_size;
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
let route_value = route.options[name];
|
|
1767
|
+
|
|
1768
|
+
if (route_value == null || typeof route_value != 'number') {
|
|
1769
|
+
return global_size;
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
if (route_value <= 0) {
|
|
1773
|
+
return Infinity;
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1776
|
+
return route_value;
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1748
1779
|
/**
|
|
1749
1780
|
* Get the body of an IncomingMessage
|
|
1750
1781
|
*
|
|
1751
1782
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
1752
1783
|
* @since 1.1.0
|
|
1753
|
-
* @version 1.
|
|
1784
|
+
* @version 1.4.0
|
|
1754
1785
|
*
|
|
1755
1786
|
* @param {IncomingMessage} req
|
|
1756
1787
|
* @param {OutgoingMessage} res Optional
|
|
@@ -1777,24 +1808,44 @@ Alchemy.setMethod(function parseRequestBody(req, res, callback) {
|
|
|
1777
1808
|
|
|
1778
1809
|
let content_type = req.headers['content-type'];
|
|
1779
1810
|
|
|
1811
|
+
let request_body_size_limit = getRouteSizeLimit(conduit?.route, 'request_body_size_limit');
|
|
1812
|
+
|
|
1780
1813
|
// Multipart data is handled by "formidable"
|
|
1781
1814
|
if (content_type && content_type.startsWith('multipart/form-data')) {
|
|
1782
1815
|
|
|
1816
|
+
let request_individual_file_size_limit = getRouteSizeLimit(conduit?.route, 'request_individual_file_size_limit'),
|
|
1817
|
+
request_total_file_size_limit = getRouteSizeLimit(conduit?.route, 'request_total_file_size_limit');
|
|
1818
|
+
|
|
1783
1819
|
let form = new this.formidable.IncomingForm({
|
|
1784
1820
|
multiples : true,
|
|
1785
1821
|
hashAlgorithm : this.settings.data_management.file_hash_algorithm || 'sha1',
|
|
1822
|
+
minFileSize : 0,
|
|
1823
|
+
allowEmptyFiles : true,
|
|
1824
|
+
maxFileSize : request_individual_file_size_limit,
|
|
1825
|
+
maxFieldsSize : request_body_size_limit,
|
|
1826
|
+
maxTotalFileSize : request_total_file_size_limit,
|
|
1786
1827
|
});
|
|
1787
1828
|
|
|
1788
1829
|
form.parse(req, function parsedMultipart(err, form_fields, form_files) {
|
|
1789
1830
|
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1831
|
+
if (err) {
|
|
1832
|
+
|
|
1833
|
+
// Ignore the error if the request was already aborted
|
|
1834
|
+
if (conduit?.aborted) {
|
|
1835
|
+
return callback(null);
|
|
1836
|
+
}
|
|
1793
1837
|
|
|
1794
|
-
|
|
1795
|
-
|
|
1838
|
+
if (conduit) {
|
|
1839
|
+
return conduit.error(err);
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
return callback(err);
|
|
1796
1843
|
}
|
|
1797
1844
|
|
|
1845
|
+
let fields = {},
|
|
1846
|
+
files = {},
|
|
1847
|
+
key;
|
|
1848
|
+
|
|
1798
1849
|
// Since formidable v3, all the fields are now arrays.
|
|
1799
1850
|
// We already had a lot of logic to deal with this,
|
|
1800
1851
|
// so we just have to un-array everything
|
|
@@ -1811,18 +1862,12 @@ Alchemy.setMethod(function parseRequestBody(req, res, callback) {
|
|
|
1811
1862
|
Object.setFormPath(files, key, form_files[key]);
|
|
1812
1863
|
}
|
|
1813
1864
|
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
req.body = {};
|
|
1817
|
-
req.files = {};
|
|
1818
|
-
} else {
|
|
1819
|
-
req.body = fields;
|
|
1820
|
-
req.files = files;
|
|
1865
|
+
req.body = fields;
|
|
1866
|
+
req.files = files;
|
|
1821
1867
|
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
}
|
|
1868
|
+
if (conduit) {
|
|
1869
|
+
conduit.setRequestBody(fields);
|
|
1870
|
+
conduit.setRequestFiles(files);
|
|
1826
1871
|
}
|
|
1827
1872
|
|
|
1828
1873
|
callback(null, fields);
|
|
@@ -1834,24 +1879,29 @@ Alchemy.setMethod(function parseRequestBody(req, res, callback) {
|
|
|
1834
1879
|
// Regular form-encoded data
|
|
1835
1880
|
if (content_type && content_type.indexOf('form-urlencoded') > -1) {
|
|
1836
1881
|
|
|
1837
|
-
this.url_form_body(req, res, function parsedBody(err) {
|
|
1882
|
+
this.url_form_body(req, res, {limit: request_body_size_limit}, function parsedBody(err) {
|
|
1883
|
+
|
|
1884
|
+
if (err) {
|
|
1885
|
+
|
|
1886
|
+
// Ignore the error if the request was already aborted
|
|
1887
|
+
if (conduit?.aborted) {
|
|
1888
|
+
return callback(null);
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
if (conduit) {
|
|
1892
|
+
return conduit.error(err);
|
|
1893
|
+
}
|
|
1838
1894
|
|
|
1839
|
-
|
|
1840
|
-
return callback(null);
|
|
1895
|
+
return callback(err);
|
|
1841
1896
|
}
|
|
1842
1897
|
|
|
1843
1898
|
// You can't send files using a regular post
|
|
1844
1899
|
req.files = {};
|
|
1845
1900
|
|
|
1846
|
-
|
|
1847
|
-
log.error('Error parsing x-www-form-urlencoded body data', {err: err});
|
|
1848
|
-
req.body = {};
|
|
1849
|
-
} else {
|
|
1850
|
-
req.body = req.body;
|
|
1901
|
+
req.body = req.body;
|
|
1851
1902
|
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
}
|
|
1903
|
+
if (conduit) {
|
|
1904
|
+
conduit.setRequestBody(req.body);
|
|
1855
1905
|
}
|
|
1856
1906
|
|
|
1857
1907
|
callback(null, req.body);
|
|
@@ -1861,25 +1911,30 @@ Alchemy.setMethod(function parseRequestBody(req, res, callback) {
|
|
|
1861
1911
|
}
|
|
1862
1912
|
|
|
1863
1913
|
// Any other encoded data (like JSON)
|
|
1864
|
-
this.any_body(req, function parsedBody(err, body) {
|
|
1914
|
+
this.any_body(req, res, {limit: request_body_size_limit}, function parsedBody(err, body) {
|
|
1865
1915
|
|
|
1866
1916
|
function handleResponse(err, body) {
|
|
1867
|
-
|
|
1868
|
-
|
|
1917
|
+
|
|
1918
|
+
if (err) {
|
|
1919
|
+
|
|
1920
|
+
// Ignore the error if the request was already aborted
|
|
1921
|
+
if (conduit?.aborted) {
|
|
1922
|
+
return callback(null);
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
if (conduit) {
|
|
1926
|
+
return conduit.error(err);
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
return callback(err);
|
|
1869
1930
|
}
|
|
1870
|
-
|
|
1931
|
+
|
|
1871
1932
|
// You can't send files using a regular post
|
|
1872
1933
|
req.files = {};
|
|
1934
|
+
req.body = body;
|
|
1873
1935
|
|
|
1874
|
-
if (
|
|
1875
|
-
|
|
1876
|
-
req.body = {};
|
|
1877
|
-
} else {
|
|
1878
|
-
req.body = body;
|
|
1879
|
-
|
|
1880
|
-
if (conduit) {
|
|
1881
|
-
conduit.setRequestBody(body);
|
|
1882
|
-
}
|
|
1936
|
+
if (conduit) {
|
|
1937
|
+
conduit.setRequestBody(body);
|
|
1883
1938
|
}
|
|
1884
1939
|
|
|
1885
1940
|
callback(null, req.body);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
let mkdirp = alchemy.use('mkdirp')?.mkdirp,
|
|
2
2
|
ncp = alchemy.use('ncp').ncp,
|
|
3
3
|
fs = alchemy.use('fs'),
|
|
4
|
+
fsp = fs.promises,
|
|
4
5
|
libpath = alchemy.use('path'),
|
|
5
6
|
child = alchemy.use('child_process'),
|
|
6
7
|
crypto = alchemy.use('crypto'),
|
|
@@ -1106,6 +1107,53 @@ Alchemy.setMethod(function request(url, options, callback) {
|
|
|
1106
1107
|
return Blast.fetch(options, callback);
|
|
1107
1108
|
});
|
|
1108
1109
|
|
|
1110
|
+
/**
|
|
1111
|
+
* Turn a `data:` uri into a file
|
|
1112
|
+
*
|
|
1113
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
1114
|
+
* @since 1.4.0
|
|
1115
|
+
* @version 1.4.0
|
|
1116
|
+
*
|
|
1117
|
+
* @param {string} data_uri
|
|
1118
|
+
*
|
|
1119
|
+
* @return {Pledge<File>}
|
|
1120
|
+
*/
|
|
1121
|
+
function convertDataUriToFile(data_uri) {
|
|
1122
|
+
|
|
1123
|
+
// Split the data uri into its 2 parts
|
|
1124
|
+
let pieces = data_uri.slice(5).split(',');
|
|
1125
|
+
|
|
1126
|
+
// Split the info bit
|
|
1127
|
+
let info = pieces[0].split(';');
|
|
1128
|
+
|
|
1129
|
+
// Get the expected mime type
|
|
1130
|
+
let mime_type = info[0];
|
|
1131
|
+
|
|
1132
|
+
// And is it base64?
|
|
1133
|
+
let is_b64 = info[1] && info[1].toLowerCase() == 'base64';
|
|
1134
|
+
|
|
1135
|
+
let buffer;
|
|
1136
|
+
|
|
1137
|
+
if (is_b64) {
|
|
1138
|
+
buffer = Buffer.from(pieces[1], 'base64');
|
|
1139
|
+
} else {
|
|
1140
|
+
buffer = Buffer.from(String.decodeURI(pieces[1]));
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
return Pledge.Swift.waterfall(
|
|
1144
|
+
() => Blast.createTempDir({prefix: 'aldl'}),
|
|
1145
|
+
async (temp_dir) => {
|
|
1146
|
+
let full_path = libpath.resolve(temp_dir, 'buffer_' + alchemy.ObjectId());
|
|
1147
|
+
|
|
1148
|
+
await fsp.writeFile(full_path, buffer);
|
|
1149
|
+
|
|
1150
|
+
return full_path;
|
|
1151
|
+
},
|
|
1152
|
+
(full_path) => Classes.Alchemy.Inode.Inode.from(full_path)
|
|
1153
|
+
);
|
|
1154
|
+
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1109
1157
|
/**
|
|
1110
1158
|
* Download a file
|
|
1111
1159
|
*
|
|
@@ -1119,6 +1167,14 @@ Alchemy.setMethod(function request(url, options, callback) {
|
|
|
1119
1167
|
*/
|
|
1120
1168
|
Alchemy.setMethod(function download(url, options) {
|
|
1121
1169
|
|
|
1170
|
+
if (url.startsWith('data:')) {
|
|
1171
|
+
try {
|
|
1172
|
+
return convertDataUriToFile(url, options);
|
|
1173
|
+
} catch (err) {
|
|
1174
|
+
return Pledge.reject(err);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1122
1178
|
const pledge = new Pledge();
|
|
1123
1179
|
|
|
1124
1180
|
// Get the file
|
package/lib/core/setting.js
CHANGED
|
@@ -73,6 +73,24 @@ network.addSetting('use_compression', {
|
|
|
73
73
|
description : 'Compress responses using gzip/deflate',
|
|
74
74
|
});
|
|
75
75
|
|
|
76
|
+
network.addSetting('request_body_size_limit', {
|
|
77
|
+
type : 'number',
|
|
78
|
+
default : 20 * 1024 * 1024,
|
|
79
|
+
description : 'Maximum allowed size in bytes for the request body, excluding files',
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
network.addSetting('request_individual_file_size_limit', {
|
|
83
|
+
type : 'number',
|
|
84
|
+
default : 200 * 1024 * 1024,
|
|
85
|
+
description : 'Maximum allowed size in bytes for individual uploaded files',
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
network.addSetting('request_total_file_size_limit', {
|
|
89
|
+
type : 'number',
|
|
90
|
+
default : 200 * 1024 * 1024,
|
|
91
|
+
description : 'Maximum allowed size in bytes for all uploaded files',
|
|
92
|
+
});
|
|
93
|
+
|
|
76
94
|
network.addSetting('use_json_dry_responses', {
|
|
77
95
|
type : 'boolean',
|
|
78
96
|
default : true,
|