alchemymvc 1.4.0-alpha.6 → 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.
- package/lib/core/alchemy.js +96 -41
- package/lib/core/alchemy_functions.js +56 -0
- package/lib/scripts/create_settings.js +18 -0
- package/package.json +1 -1
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
|
|
@@ -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,
|